跨主机互联是说 A 宿主机的容器可以访问 B 主机上的容器,但是前提是保证各宿主机之间的网络是可以相互通信的, 然后各容器才可以通过宿主机访问到对方的容器, 实现原理是在宿主机做一个网络路由就可以实现 A 宿主机的容器访问 B主机的容器的目的, 复杂的网络或者大型的网络可以使用 google 开源的 k8s 进行互联。本文之后将详细介绍docker网络配置,并演示容器跨主机通信的实现。

docker网络基础

  之前我们说过,当我们安装完docker应用后,就会自动添加一块虚拟的docker0网卡,并基于docker0网卡,提供了3种可选网络类型供创建的容器使用,分别是bridge(桥接),host(主机),none(无外部网络)。其中默认是采用桥接模式,容器中的网卡桥接在docker的网桥上,且通过DHCP自动分配IP,与docker0在同一网段。
  当我们每创建一个容器,宿主机上就会新建一个网卡与容器中的网卡相对应,如下图所示。
网桥
  容器桥接模式跨网络访问的结构示意图如下图所示
docker跨主机通信
  想实现不同宿主机上的容器跨主机肯定要经过宿主机来做网络转发,通过设置宿主机静态路由或者修改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
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

&emsp;&emsp;同样的操作,将node2宿主机中的docker0网段设置为10.2.0.0/24
### 修改静态路由
&emsp;&emsp;我这里node1的eth0网卡ip为192.168.32.19,node2的eth0网卡ip为192.168.32.20,且两个宿主机之前网络是可以通过eth0网卡相互连接的。添加路由规则如下:
&emsp;&emsp;在node1上添加静态路由

ip r add 10.2.0.0/24 via 192.168.32.20 dev eth0

&emsp;&emsp;在node2上添加静态路由

ip r add 10.1.0.0/24 via 192.168.32.19 dev eth0

### 修改iptables规则
&emsp;&emsp;宿主机如果为centos7,则不需要修改iptables规则,而宿主机如果为ubuntu系统则需要添加forward规则来放行。我仔细看了下这两个系统的iptables规则,发现在centos系统docker创建的iptables规则中对``Chain FORWARD``是默认``ACCEPT``,而ubuntu系统中docker创建的iptables对``Chain FORWARD``是默认``DROP``
![dockecentosiptables](https://img-blog.csdnimg.cn/20191207185124292.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70)
![dockerubuntuiptables](https://img-blog.csdnimg.cn/20191207185139492.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70)
&emsp;&emsp;之后两边各启动一个容器就可以实现相互通信或访问了。
## docker网络进阶
&emsp;&emsp;之前我们演示了通过docker0网卡的桥接方式实现了容器跨主机访问。我们通过修改docker0网卡的网段设置来实现,每个主机上容器的网段不同。
&emsp;&emsp;这种实现方式有个问题就是,但当每次我们修改了docker0网段之后,如果之后打算变更网段,之前的容器都将无法与docker0网桥桥接,导致网络不通,不能使用。
&emsp;&emsp;对此我们有一个更灵活的方案来实现容器的跨主机通信。那就是我们还可以通过创建一个或多个自定义网络,将新创建的每个容器指定连接到我们创建的这个网络中,这样他们的网段就是我们设置的这个网络的网段,实现每个主机上容器网段都不相同。
### 创建自定义网络
&emsp;&emsp;可以将我们之前对docker0网卡的修改还原了的(当然,也可以不修改,出于控制变量方便观察考虑,建议修改回去)。
&emsp;&emsp;我们可以通过``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

&emsp;&emsp;例如我们在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:~#

&emsp;&emsp;可以用``docker network ls``(或``docker network list``)看到网络类型多了一种,也就是我们刚刚创建的web1类型。
而用``ifconfig``或者``ip a``命令也可以看到我们的网卡设备里多了一个``br-a817cf36502e``,ip也恰好是我们指定的``10.10.0.0/24``网段。
![自定义网络](https://img-blog.csdnimg.cn/20191207192708651.png)
&emsp;&emsp;此时我们就可以通过``--net``选项指定我们刚刚创建的web1网络,来创建并启动容器了。

root@DockerUbuntu:# docker run -it -d -p 8080:8080 -p 8009:8009 –net=web1 tomcat-app1:v1
afc1e3db8a6a670d30cdd0756af65da74895976ce5ebbf876329b04b452a3710
root@DockerUbuntu:
#

&emsp;&emsp;同样,在宿主机node2上也创建一个自定义网络web2,然后新创建的容器,也指定网络为web2,再设置静态路由和修改iptables规则放行,也可以实现容器间跨主机访问。

一个低调的男人