前言
最近VPS重启,发现容器间的IP全乱了。这就直接出现一个致命问题,那就是按之前方案Traefik2 根据配置的 Dynamic Configuration找到对应服务时,是要写死services容器IP的。而现在一重启ip全乱了,对应的traefik流量就会乱套,急需为docker每个容器指定固定IP,把容器的IP固定下来。这样下次重启VPS,就不会再出现随机分配IP给容器的情况,也就不会套服务了。
问题描述
举个例子:
假设我给Traefik 配置了 2个router 及都指定了对应service容器的IP,如下图
- blog1.zctou.com -->
http://172.18.0.3
对应 typecho1容器 - blog2.zctou.com -->
http://172.18.0.4
对应 typecho2容器
因为之前的配置没有把ip固定下来,那么 http://172.18.0.3
完全有可能分配 typecho2容器,http://172.18.0.4
分配给typecho1容器。
那么,这时你访问blog1.zctou.com
给的数据自然就是blog2.zctou.com
的数据了,反过来亦然。
若然不能及时发现,那么你的网站排名就别想要了。
这时候,要解决这个乱分配ip的问题思路就很简单了。最好就是像家用路由器一样,能给每个容易在创建的时候就把ip固定下来。
利用docker-compose 给容器分配指定ip
Docker-compose 分配静态IP的两种方法
由于先前用traeifk配置都是docker-compose拉起容器,因此这里解决固定ip的问题也是围绕docker-compose配置进行。
而在compose 的yml文件中分给容器固定IP,一般就两种方法:
- 1.利用
docker ipam
功能在同一yml文件中分配固定IP。 - 2.先在外部创建一个网络(external),然后在想要用这网络的容器指定IP。
下面以之前的【VPS搭建Typecho代码】为例,分别给出两种方法配置方法。
version: '3'
services:
web:
image: fukoy/nginx-php-fpm:php7.4
container_name: server-phpnginx-tp1
restart: always
volumes:
- ./www:/usr/share/nginx/typecho/ #typecho网站文件
- ./nginx/conf.d:/etc/nginx/conf.d #nginx配置文件夹
- ./nginx/nginx.conf:/etc/nginx/nginx.conf #nginx配置文件
depends_on:
- mysql
networks:
- default
- proxy
mysql:
image: mariadb
restart: always
container_name: server-mysql-tp1
volumes:
- ./mysql/data:/var/lib/mysql
- ./mysql/logs:/var/log/mysql
- ./mysql/conf:/etc/mysql/conf.d
env_file:
- mysql.env
networks:
- default
networks:
default:
proxy:
external: true
方法一:利用 docker ipam分配固定IP
这里用到docker ipam
就是IP Address Management Driver ,docker官方文档地址如下:
https://docs.docker.com/engine/reference/commandline/network_create/
使用格式:
version: '3'
services:
web:
image: fukoy/nginx-php-fpm:php7.4
container_name: server-phpnginx-tp1
restart: always
volumes:
- ./www:/usr/share/nginx/typecho/ #typecho网站文件
- ./nginx/conf.d:/etc/nginx/conf.d #nginx配置文件夹
- ./nginx/nginx.conf:/etc/nginx/nginx.conf #nginx配置文件
depends_on:
- mysql
networks:
default:
ipv4_address: 172.18.0.2
proxy:
mysql:
image: mariadb
restart: always
container_name: server-mysql-tp1
volumes:
- ./mysql/data:/var/lib/mysql
- ./mysql/logs:/var/log/mysql
- ./mysql/conf:/etc/mysql/conf.d
env_file:
- mysql.env
networks:
default:
ipv4_address: 172.18.0.3
networks:
proxy:
external: true
default:
driver: bridge
ipam:
config:
- subnet: 172.19.0.0/16
可以看到用ipam
指定一个内部网络,容器用ipv4_address
获取固定IP。如下图所示:
但这些IP只能用于这个内部网络容器间相互访问,要打通与其他网络间的访问,至少要有一个容器要同时在两个网络内。
说得更白一点就是:
这样分配的IP,是以这个yml为单位的小局域网,只能是这个yml中创建的容器间能互相访问。其他yml创建的容器访问不到这网络里面的容器。
只有这样,才能被外部网络访问得到,因此,这种方法并不适合traefik做反代的配置。
方法二:先建外部网络,再给容器分配ip
要明确一点就是,不同容易间想互通,得在同一个网络中。
而这里我们流量都交给的traefik处理,用traefik进行反代,traefik接管了宿主机的80和443端口。
因此要成功把流量导入其他独立容器,必须要将service容器与traefik置于同一个网络中,比如我这里创建的proxy
网络。
因此,不同的docker-compose.yml,都要有一个容器与traefik处于同一网段中。
这时候最佳的解决办法就是先定义一个外部网络,再分别在每个docker-compose中用ipv4_address
固定IP。
2.1 创建外部网络:
#创建proxy网络
docker network create --driver bridge --subnet=172.18.0.0/24 proxy
2.2 在不同的docker-compose指定IP
比如以前配置的v2ray,要分配固定IP,可以修改如下:
version: '3.7'
services:
v2ray:
image: alphacodinghub/v2ray-nginx
expose:
- 13307
container_name: v2ray
volumes:
- ./www/html:/var/www/html
- ./nginxconf/conf.d:/etc/nginx/conf.d
- ./v2rayconf:/etc/v2ray
restart: always
networks:
proxy:
ipv4_address: 172.18.0.3
networks:
proxy:
external: true
而以前配置的typecho blog,两样的分配方式即可。
version: '3'
services:
web:
image: fukoy/nginx-php-fpm:php7.4
container_name: server-phpnginx-tp1
restart: always
volumes:
- ./www:/usr/share/nginx/typecho/ #typecho网站文件
- ./nginx/conf.d:/etc/nginx/conf.d #nginx配置文件夹
- ./nginx/nginx.conf:/etc/nginx/nginx.conf #nginx配置文件
depends_on:
- mysql
networks:
default:
proxy:
ipv4_address: 172.18.0.4
mysql:
image: mariadb
restart: always
container_name: server-mysql-tp1
volumes:
- ./mysql/data:/var/lib/mysql
- ./mysql/logs:/var/log/mysql
- ./mysql/conf:/etc/mysql/conf.d
env_file:
- mysql.env
networks:
- default
networks:
default:
proxy:
external: true
总结
终上所述,利用docker-compose.yml
给容器指定固定IP很简单,直接在networks上指定即可,命令:ipv4_address: 内部ip
networks:
default:
proxy:
ipv4_address: 172.18.0.4
前提是,这个ip段必须在你在第一步开始之初就创建好的网络中,也就是docker network create --driver bridge --subnet=172.18.0.0/24 proxy
内。
这样给不同的容器分配好固定IP,就算VPS自动重启,也不会出错服务不匹配现象了。
踩过的坑:
docker-compose.yml的配置写法有两种 :
- list
- mapping
两种写法不可以混用在同一个根目节点下。如:
# 这样的写法是正确的都是一对一对的出现,就是mapping
networks:
default:
proxy:
ipv4_address: 172.18.0.4
# 这样的写法也是对的,就是list
networks:
- default
- proxy
# 这样的写法是错的,list与mapping混用了
networks:
- default #list
proxy: #mapping
ipv4_address: 172.18.0.4
也就是说
# list 的样式
list:
- a
- b
# mapping 的样式
mapping:
a:
b:
翻译成json文件是这样的:
{
"list": [
"a",
"b"
],
"mapping": {
"a": null,
"b": null
}
}
混用会报错。
要想两个容器能互相访问,处在同一网络(docker network)是关键。
把握好这点,就能娴熟驾驭不同的docker-compose拉起的容器的连接问题。
- 查看docker现有网络:
docker network ls
- 宿主机中,创建一个网络:
docker network create networkName
- 将容器连到已经创建的网络中:
docker network connect networkName containerName
- 查看某个网络内有哪些容器:
docker network inspect networkName
- 使用docker network --help 获得network相关的帮助。
- 可以在运行容器时直接指定连接network:
docker run --network networkName imageName