Docker(四)——容器跨主机网络配置
跨主机互联是说 A 宿主机的容器可以访问 B 主机上的容器,但是前提是保证各宿主机之间的网络是可以相互通信的, 然后各容器才可以通过宿主机访问到对方的容器, 实现原理是在宿主机做一个网络路由就可以实现 A 宿主机的容器访问 B主机的容器的目的, 复杂的网络或者大型的网络可以使用 google 开源的 k8s 进行互联。本文之后将详细介绍docker网络配置,并演示容器跨主机通信的实现。doc.
本文首发于我的个人网站: https://hewanyue.com/
本文作者: Hechao
本文链接: https://hewanyue.com/blog/7e3785f.html
跨主机互联是说 A 宿主机的容器可以访问 B 主机上的容器,但是前提是保证各宿主机之间的网络是可以相互通信的, 然后各容器才可以通过宿主机访问到对方的容器, 实现原理是在宿主机做一个网络路由就可以实现 A 宿主机的容器访问 B主机的容器的目的, 复杂的网络或者大型的网络可以使用 google 开源的 k8s 进行互联。本文之后将详细介绍docker网络配置,并演示容器跨主机通信的实现。
docker网络基础
之前我们说过,当我们安装完docker应用后,就会自动添加一块虚拟的docker0网卡,并基于docker0网卡,提供了3种可选网络类型供创建的容器使用,分别是bridge(桥接),host(主机),none(无外部网络)。其中默认是采用桥接模式,容器中的网卡桥接在docker的网桥上,且通过DHCP自动分配IP,与docker0在同一网段。
当我们每创建一个容器,宿主机上就会新建一个网卡与容器中的网卡相对应,如下图所示。
容器桥接模式跨网络访问的结构示意图如下图所示
想实现不同宿主机上的容器跨主机肯定要经过宿主机来做网络转发,通过设置宿主机静态路由或者修改iptables规则来实现,可这时就面临一个问题:所有的容器服务默认的DHCP网段都是172.17.0.0/16网段,如果node1上的容器想直接访问node2宿主机上的容器,就会被直接当做docker0网桥的内部网段,数据报文根本都不会从node1主机的eth0网卡发出去,也根本到不了node2主机上。这种情况下,无论我们怎么修改iptables规则或者路由规则都无济于事的。所以我们想实现容器跨主机访问,首先要将不同宿主机上的容器分到不同的网段,然后才可以通过路由规则或者iptables进行跳转或转发。
修改docker网络的网段
我们可以对每一个宿主机上的docker配置文件进行修改,实现每个宿主机的docker容器都在不同网段的目的,可以通过以下方式修改(未避免影响, 先在各服务器删除之前创建的所有容器,docker rm -f `docker ps -a -q`
)。
- 修改启动system脚本文件
docker.service
vim /lib/systemd/system/docker.service
在ExecStart=
选项结尾加上--bip=10.1.0.1/24
,就指定了10.1.0.0/24网段,然后执行命令重新加载配置文件和重启服务。
systemctl daemon-reload
systemctl restart docker
注意:不能写10.1.0.0/24,会报错
,虽然写网段结尾是0,如10.1.0.0/24更符合我们的习惯,不过确实会报错,报错信息如下:
Dec 07 16:27:58 DockerUbuntu dockerd[14794]: failed to start daemon: Error initializing network controller: Error creating default "bridge" network: failed to allocate gateway (10.10.0.0): Address already in use
Dec 07 16:27:58 DockerUbuntu systemd[1]: docker.service: Main process exited, code=exited, status=1/FAILURE
Dec 07 16:27:58 DockerUbuntu systemd[1]: docker.service: Failed with result 'exit-code'.
Dec 07 16:27:58 DockerUbuntu systemd[1]: Failed to start Docker Application Container Engine.
-- Subject: Unit docker.service has failed
-- Defined-By: systemd
- 也可以修改daemon.json文件,在里面添加
"bip": "10.2.0.1/24"
,如下所示(上面那个是我的阿里云加速器链接,注册阿里账号免费获取,之前文章有详细介绍,需改成自己的或者删掉):
vim /etc/docker/daemon.json
{
"registry-mirrors": ["https://xxxxxxxx.mirror.aliyuncs.com","https://registry.docker-cn.com"],
"bip": "10.1.0.1/24"
}
daemon.json文件是json数据格式,需要遵守json语法,换行记得要加,
逗号。
直接重启docker服务后生效
systemctl restart docker
此时看网卡的ip就已经变为了我们设置的网段,之后创建的容器服务器就会自动获取我们设置好的网段中的IP了。
root@DockerUbuntu:~# ifconfig
docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 10.10.0.1 netmask 255.255.255.0 broadcast 10.10.0.255
inet6 fe80::42:25ff:fe2b:ecbc prefixlen 64 scopeid 0x20<link>
ether 02:42:25:2b:ec:bc txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 41 bytes 3526 (3.5 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
同样的操作,将node2宿主机中的docker0网段设置为10.2.0.0/24
修改静态路由
我这里node1的eth0网卡ip为192.168.32.19,node2的eth0网卡ip为192.168.32.20,且两个宿主机之前网络是可以通过eth0网卡相互连接的。添加路由规则如下:
在node1上添加静态路由
ip r add 10.2.0.0/24 via 192.168.32.20 dev eth0
在node2上添加静态路由
ip r add 10.1.0.0/24 via 192.168.32.19 dev eth0
修改iptables规则
宿主机如果为centos7,则不需要修改iptables规则,而宿主机如果为ubuntu系统则需要添加forward规则来放行。我仔细看了下这两个系统的iptables规则,发现在centos系统docker创建的iptables规则中对Chain FORWARD
是默认ACCEPT
,而ubuntu系统中docker创建的iptables对Chain FORWARD
是默认DROP
之后两边各启动一个容器就可以实现相互通信或访问了。
docker网络进阶
之前我们演示了通过docker0网卡的桥接方式实现了容器跨主机访问。我们通过修改docker0网卡的网段设置来实现,每个主机上容器的网段不同。
这种实现方式有个问题就是,但当每次我们修改了docker0网段之后,如果之后打算变更网段,之前的容器都将无法与docker0网桥桥接,导致网络不通,不能使用。
对此我们有一个更灵活的方案来实现容器的跨主机通信。那就是我们还可以通过创建一个或多个自定义网络,将新创建的每个容器指定连接到我们创建的这个网络中,这样他们的网段就是我们设置的这个网络的网段,实现每个主机上容器网段都不相同。
创建自定义网络
可以将我们之前对docker0网卡的修改还原了的(当然,也可以不修改,出于控制变量方便观察考虑,建议修改回去)。
我们可以通过docker network create
命令来创建一个自定义网络
root@DockerUbuntu:~# docker network create --help
Usage: docker network create [OPTIONS] NETWORK
Create a network
Options:
--attachable Enable manual container attachment
--aux-address map Auxiliary IPv4 or IPv6 addresses used by Network driver
(default map[])
--config-from string The network from which copying the configuration
--config-only Create a configuration only network
-d, --driver string Driver to manage the Network (default "bridge")
--gateway strings IPv4 or IPv6 Gateway for the master subnet
--ingress Create swarm routing-mesh network
--internal Restrict external access to the network
--ip-range strings Allocate container ip from a sub-range
--ipam-driver string IP Address Management Driver (default "default")
--ipam-opt map Set IPAM driver specific options (default map[])
--ipv6 Enable IPv6 networking
--label list Set metadata on a network
-o, --opt map Set driver specific options (default map[])
--scope string Control the network's scope
--subnet strings Subnet in CIDR format that represents a network segment
例如我们在node1创建一个名为web1的桥接网络,网段为10.10.0.0/24,设置网关为10.10.0.1(可设置为此网段内任意ip)。
root@DockerUbuntu:~# docker network create -d bridge --subnet 10.10.0.0/24 --gateway 10.10.0.1 web1
a817cf36502eea3469e1cb4b9b7577044f8dce96f015ba57a47f6809c00d72c7
root@DockerUbuntu:~# docker network ls
NETWORK ID NAME DRIVER SCOPE
afd91b2e1731 bridge bridge local
241d3e94a6b3 host host local
7cc9cf9eb69e none null local
a817cf36502e web1 bridge local
root@DockerUbuntu:~#
可以用docker network ls
(或docker network list
)看到网络类型多了一种,也就是我们刚刚创建的web1类型。
而用ifconfig
或者ip a
命令也可以看到我们的网卡设备里多了一个br-a817cf36502e
,ip也恰好是我们指定的10.10.0.0/24
网段。
此时我们就可以通过--net
选项指定我们刚刚创建的web1网络,来创建并启动容器了。
root@DockerUbuntu:~# docker run -it -d -p 8080:8080 -p 8009:8009 --net=web1 tomcat-app1:v1
afc1e3db8a6a670d30cdd0756af65da74895976ce5ebbf876329b04b452a3710
root@DockerUbuntu:~#
同样,在宿主机node2上也创建一个自定义网络web2,然后新创建的容器,也指定网络为web2,再设置静态路由和修改iptables规则放行,也可以实现容器间跨主机访问。
更多推荐
所有评论(0)