Docker容器跨主机通信之:直接路由方式

发布时间:2020-02-14 19:02:24编辑:admin阅读(2623)

    一、Docker网络基本原理

    直观上看,要实现网络通信,机器需要至少一个网络接口(物理接口或虚拟接口)与外界相通,并可以收发数据包;此外,如果不同子网之间要进行通信,需要额外的路由机制。

     

    Docker中的网络接口默认都是虚拟的接口。虚拟接口的最大优势就是转发效率极高。这是因为Linux通过在内核中进行数据复制来实现虚拟接口之间的数据转发,即发送接口的发送缓存中的数据包将被直接复制到接收接口的接收缓存中,而无需通过外部物理网络设备进行交换。对于本地系统和容器内系统来看,虚拟接口跟一个正常的以太网卡相比并无区别,只是它速度要快得多。

     

    Docker容器网络就很好地利用了Linux虚拟网络技术,在本地主机和容器内分别创建一个虚拟接口,并让它们彼此连通(这样的一对接口叫做veth pair)

    1.png

    一般情况下,Docker创建一个容器的时候,会具体执行如下操作:

    1.创建一对虚拟接口,分别放到本地主机和新容器的命名空间中;

    2.本地主机一端的虚拟接口连接到默认的docker0网桥或指定网桥上,并具有一个以veth开头的唯一名字,如veth1234;

    3.容器一端的虚拟接口将放到新创建的容器中,并修改名字作为eth0。这个接口只在容器的命名空间可见;

    4.从网桥可用地址段中获取一个空闲地址分配给容器的eth0(例如172.17.0.2/16),并配置默认路由网关为docker0网卡的内部接口docker0的IP地址(例如172.17.42.1/16)。

    完成这些之后,容器就可以使用它所能看到的eth0虚拟网卡来连接其他容器和访问外部网络。用户也可以通过docker network命令来手动管理网络。

     

    二、Docker网络默认模式

    按docker官方的说法,docker容器的网络有五种模式:

    1)bridge模式,--net=bridge(默认)
    这是dokcer网络的默认设置,为容器创建独立的网络命名空间,容器具有独立的网卡等所有单独的网络栈,是最常用的使用方式。
    在docker run启动容器的时候,如果不加--net参数,就默认采用这种网络模式。安装完docker,系统会自动添加一个供docker使用的网桥docker0,我们创建一个新的容器时,
    容器通过DHCP获取一个与docker0同网段的IP地址,并默认连接到docker0网桥,以此实现容器与宿主机的网络互通。
     
    2)host模式,--net=host
    这个模式下创建出来的容器,直接使用容器宿主机的网络命名空间。
    将不拥有自己独立的Network Namespace,即没有独立的网络环境。它使用宿主机的ip和端口。
     
    3)none模式,--net=none
    为容器创建独立网络命名空间,但不为它做任何网络配置,容器中只有lo,用户可以在此基础上,对容器网络做任意定制。
    这个模式下,dokcer不为容器进行任何网络配置。需要我们自己为容器添加网卡,配置IP。
    因此,若想使用pipework配置docker容器的ip地址,必须要在none模式下才可以。
     
    4)其他容器模式(即container模式),--net=container:NAME_or_ID
    与host模式类似,只是容器将与指定的容器共享网络命名空间。
    这个模式就是指定一个已有的容器,共享该容器的IP和端口。除了网络方面两个容器共享,其他的如文件系统,进程等还是隔离开的。
     
    5)用户自定义:docker 1.9版本以后新增的特性,允许容器使用第三方的网络实现或者创建单独的bridge网络,提供网络隔离能力。

     

    bridge模式

    bridge模式是docker默认的,也是开发者最常使用的网络模式。在这种模式下,docker为容器创建独立的网络栈,保证容器内的进程使用独立的网络环境,
    实现容器之间、容器与宿主机之间的网络栈隔离。同时,通过宿主机上的docker0网桥,容器可以与宿主机乃至外界进行网络通信。
    其网络模型可以参考下图:

    1.png 

    从上面的网络模型可以看出,容器从原理上是可以与宿主机乃至外界的其他机器通信的。同一宿主机上,容器之间都是连接掉docker0这个网桥上的,它可以作为虚拟交换机使容器可以相互通信。
    然而,由于宿主机的IP地址与容器veth pair的 IP地址均不在同一个网段,故仅仅依靠veth pair和namespace的技术,还不足以使宿主机以外的网络主动发现容器的存在。为了使外界可以方位容器中的进程,docker采用了端口绑定的方式,也就是通过iptables的NAT,将宿主机上的端口
    端口流量转发到容器内的端口上。

    举一个简单的例子,使用下面的命令创建容器,并将宿主机的3306端口绑定到容器的3306端口:
    docker run -tid --name db -p 3306:3306 MySQL
     
    在宿主机上,可以通过iptables -t nat -L -n,查到一条DNAT规则:
     
    DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:3306 to:172.17.0.5:3306
     
    上面的172.17.0.5即为bridge模式下,创建的容器IP。
     
    很明显,bridge模式的容器与外界通信时,必定会占用宿主机上的端口,从而与宿主机竞争端口资源,对宿主机端口的管理会是一个比较大的问题。同时,由于容器与外界通信是基于三层上iptables NAT,性能和效率上的损耗是可以预见的。

     

    三、方案介绍

    概述

    就目前Docker自身默认的网络来说,单台主机上的不同Docker容器可以借助docker0网桥直接通信,这没毛病,而不同主机上的Docker容器之间只能通过在主机上用映射端口的方法来进行通信,有时这种方式会很不方便,甚至达不到我们的要求,因此位于不同物理机上的Docker容器之间直接使用本身的IP地址进行通信很有必要。再者说,如果将Docker容器起在不同的物理主机上,我们不可避免的会遭遇到Docker容器的跨主机通信问题。本文就来尝试一下。

    情景构造

    如下图所示,我们有两个物理主机1和主机2,我们在各自宿主机上启动一个centos容器,启动成功之后,两个容器分别运行在两个宿主机之上,默认的IP地址分配如图所示,这也是Docker自身默认的网络。

    1.png

     

    此时两台主机上的Docker容器如何直接通过IP地址进行通信?

    一种直接想到的方案便是通过分别在各自主机中 添加路由 来实现两个centos容器之间的直接通信。我们来试试吧

     

    方案原理分析

    由于使用容器的IP进行路由,就需要避免不同主机上的容器使用了相同的IP,为此我们应该为不同的主机分配不同的子网来保证。于是我们构造一下两个容器之间通信的路由方案,如下图所示。

     1.png

     

    各项配置如下:

    • 主机1的IP地址为:192.168.91.128

    • 主机2的IP地址为:192.168.91.129

    • 为主机1上的Docker容器分配的子网:10.0.128.0/24

    • 为主机2上的Docker容器分配的子网:10.0.129.0/24

    这样配置之后,两个主机上的Docker容器就肯定不会使用相同的IP地址从而避免了IP冲突。

    我们接下来 定义两条路由规则 即可:

    • 所有目的地址为10.0.128.0/24的包都被转发到主机1上

    • 所有目的地址为10.0.129.0/24的包都被转发到主机2上

    综上所述,数据包在两个容器间的传递过程如下:

    • 从container1 发往 container2 的数据包,首先发往container1的“网关”docker0,然后通过查找主机1的路由得知需要将数据包发给主机2,数据包到达主机2后再转发给主机2的docker0,最后由其将数据包转到container2中;反向原理相同,不再赘述。

    我们心里方案想的是这样,接下来实践一下看看是否可行。

     

    四、实际试验

    环境介绍

    操作系统服务器地址Dockerd地址
    ubuntu-16.04.5-server-amd64192.168.91.12810.0.128.2
    ubuntu-16.04.5-server-amd64192.168.91.12910.0.129.2

     

     

     

     


    请确保已经安装好了docker,这里是使用以下命令安装的

    apt-get install -y docker.io

     

    docker的版本为  17.03.2-ce

     

    比如主机1,我需要运行一个docker镜像,要求它的网段必须是 10.0.128.0/24 ,怎么设置呢?

    有3个办法:

    1. 修改docker0的网段

    2. 创建一个docker网桥

     

    这里为了快速实现,选用第一种方案。直接修改 /etc/default/docker 文件,添加 DOCKER_OPTS 参数即可

    修改docker0

    分别对主机1和主机2上的docker0进行配置

    主机1

    编辑主机1上的 /etc/default/docker 文件,最后一行添加

    DOCKER_OPTS="--bip 10.0.128.1/24"

     

    特别注意,DOCKER_OPTS参数后面必须有引号。--bip后面的ip就是docker0的ip地址,一般从第一个ip开始!

    不要修改 /etc/docker/daemon.json 文件添加bip,我测试了一些,重启docker会报错!

     

    主机2

    编辑主机1上的 /etc/default/docker 文件,最后一行添加

    DOCKER_OPTS="--bip 10.0.129.1/24"

     

    重启docker服务

    主机1和主机2上均执行如下命令重启docker服务以使修改后的docker0网段生效

    systemctl restart docker

     

    查看主机1上docker0的ip地址

    复制代码

    root@ubuntu:~# ifconfig docker0docker0   Link encap:Ethernet  HWaddr 02:42:8a:46:e2:eb
              inet addr:10.0.128.1  Bcast:0.0.0.0  Mask:255.255.255.0
              UP BROADCAST MULTICAST  MTU:1500  Metric:1
              RX packets:0 errors:0 dropped:0 overruns:0 frame:0
              TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:0
              RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

    复制代码

     

    查看主机2上docker0的ip地址

    复制代码

    root@ubuntu:~# ifconfig docker0docker0   Link encap:Ethernet  HWaddr 02:42:22:b1:25:66
              inet addr:10.0.129.1  Bcast:0.0.0.0  Mask:255.255.255.0
              UP BROADCAST MULTICAST  MTU:1500  Metric:1
              RX packets:0 errors:0 dropped:0 overruns:0 frame:0
              TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:0
              RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

    复制代码

     

    发现默认的网段已经改变了!

     

    添加路由规则

    主机1

    查看路由表

    复制代码

    root@ubuntu:~# route -nKernel IP routing table
    Destination     Gateway         Genmask         Flags Metric Ref    Use Iface0.0.0.0         192.168.91.2    0.0.0.0         UG    0      0        0 ens3210.0.128.0      0.0.0.0         255.255.255.0   U     0      0        0 docker0192.168.91.0    0.0.0.0         255.255.255.0   U     0      0        0 ens32

    复制代码

     

    默认只有自己本身的路由,如果需要访问 10.0.129.0/24 网段,需要添加路由

     

    主机1上添加路由规则如下:

    route add -net 10.0.129.0/24 gw 192.168.91.129

     

    gw 表示下一跳地址,这里的地址就是主机2的ip地址

     

    再次查看路由,发现已经添加上了

    复制代码

    root@ubuntu:~# route -nKernel IP routing table
    Destination     Gateway         Genmask         Flags Metric Ref    Use Iface0.0.0.0         192.168.91.2    0.0.0.0         UG    0      0        0 ens3210.0.128.0      0.0.0.0         255.255.255.0   U     0      0        0 docker010.0.129.0      192.168.91.129  255.255.255.0   UG    0      0        0 ens32192.168.91.0    0.0.0.0         255.255.255.0   U     0      0        0 ens32

    复制代码

     

    主机2

    主机2上添加路由规则如下:

    route add -net 10.0.128.0/24 gw 192.168.91.128

     

    在主机1上,ping主机2的docker0地址

    root@ubuntu:~# ping 10.0.129.1 -c 1
    PING 10.0.129.1 (10.0.129.1) 56(84) bytes of data.
    bytes from 10.0.129.1: icmp_seq=1 ttl=64 time=1.35 ms
    
    --- 10.0.129.1 ping statistics ---
    packets transmitted, 1 received, 0% packet loss, time 0ms
    rtt min/avg/max/mdev = 1.355/1.355/1.355/0.000 ms

     

    在主机2上,ping主机1的docker0地址

    root@ubuntu:~# ping 10.0.128.1 -c 1
    PING 10.0.128.1 (10.0.128.1) 56(84) bytes of data.
    bytes from 10.0.128.1: icmp_seq=1 ttl=64 time=1.73 ms
    
    --- 10.0.128.1 ping statistics ---
    packets transmitted, 1 received, 0% packet loss, time 0ms
    rtt min/avg/max/mdev = 1.732/1.732/1.732/0.000 ms

     

    ok,既然docker0都通了,那么起一个docker容器,会不会也是通的的呢?测试一下吧

    在主机1上面启动一个容器,这里选用apline镜像,它只有4.5M,并且自带ping命令!

    先查看ip地址

    复制代码

    root@ubuntu:~# docker run -it alpine/ # ifconfigeth0      Link encap:Ethernet  HWaddr 02:42:0A:00:80:02
              inet addr:10.0.128.2  Bcast:0.0.0.0  Mask:255.255.255.0
              inet6 addr: fe80::42:aff:fe00:8002/64 Scope:Link
              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
              RX packets:14 errors:0 dropped:0 overruns:0 frame:0
              TX packets:7 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:0
              RX bytes:1156 (1.1 KiB)  TX bytes:578 (578.0 B)

    复制代码

     

    它的ip地址为 10.0.128.2 ,由于docker0占用了第一个ip地址。所以容器启动时,ip地址从第二个开始分配!

     

    在主机2上面启动一个容器

    复制代码

    root@ubuntu:~# docker run -it alpine/ # ifconfigeth0      Link encap:Ethernet  HWaddr 02:42:0A:00:81:02
              inet addr:10.0.129.2  Bcast:0.0.0.0  Mask:255.255.255.0
              inet6 addr: fe80::42:aff:fe00:8102/64 Scope:Link
              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
              RX packets:12 errors:0 dropped:0 overruns:0 frame:0
              TX packets:6 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:0
              RX bytes:1016 (1016.0 B)  TX bytes:508 (508.0 B)

    复制代码

     

    在主机1上的容器中 ping 主机2中的容器

    先来ping 主机2的docker0,再ping 主机2中的容器

    / # ping 10.0.129.1 -c 1
    PING 10.0.129.1 (10.0.129.1): 56 data bytes
    bytes from 10.0.129.1: seq=0 ttl=63 time=0.419 ms
    
    --- 10.0.129.1 ping statistics ---
    packets transmitted, 1 packets received, 0% packet loss
    round-trip min/avg/max = 0.419/0.419/0.419 ms
    / # ping 10.0.129.2 -c 1
    PING 10.0.129.2 (10.0.129.2): 56 data bytes
    
    --- 10.0.129.2 ping statistics ---
    packets transmitted, 0 packets received, 100% packet loss

     

    从结果中,可以发现。docker0是通的,但是主机2中的容器是不通的,为什么呢?

    Docker Bridge创建创建过程

    1.png

    1)首先宿主机上创建一对虚拟网卡veth pair设备,veth设备总是成对出现的,形成一个通信通道,数据传输就是基于这个链路的,veth设备常用来连接两个网络设备

    2)Docker将veth pair设备的一端放在容器中,并命名为eth0,然后将另一端加入docker0网桥中,可以通过brctl show命令查看

    3)从docker0字网卡中分配一个IP到给容器使用,并设置docker0的IP地址为容器默认网关

    4)此时容器IP与宿主机是可以通信的,宿主机也可以访问容器中的ip地址,在bridge模式下,连接同一网桥的容器之间可以相互通信,同时容器可以访问外网,但是其他物理机不能访问docker容器IP,需要通过NAT将容器的IP的port映射为宿主机的IP和port;

     

    在主机1上面,再开一个窗口,使用ifconfig查看

    root@ubuntu:~# ifconfig
    docker0   Link encap:Ethernet  HWaddr 02:42:8a:46:e2:eb
              inet addr:10.0.128.1  Bcast:0.0.0.0  Mask:255.255.255.0
              inet6 addr: fe80::42:8aff:fe46:e2eb/64 Scope:Link
              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
              RX packets:12 errors:0 dropped:0 overruns:0 frame:0
              TX packets:11 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:0
              RX bytes:760 (760.0 B)  TX bytes:830 (830.0 B)
    
    ens32     Link encap:Ethernet  HWaddr 00:0c:29:12:ff:d3
              inet addr:192.168.91.128  Bcast:192.168.91.255  Mask:255.255.255.0
              inet6 addr: fe80::20c:29ff:fe12:ffd3/64 Scope:Link
              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
              RX packets:689 errors:0 dropped:0 overruns:0 frame:0
              TX packets:552 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:1000
              RX bytes:60936 (60.9 KB)  TX bytes:71802 (71.8 KB)
    
    lo        Link encap:Local Loopback
              inet addr:127.0.0.1  Mask:255.0.0.0
              inet6 addr: ::1/128 Scope:Host
              UP LOOPBACK RUNNING  MTU:65536  Metric:1
              RX packets:176 errors:0 dropped:0 overruns:0 frame:0
              TX packets:176 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:1
              RX bytes:13296 (13.2 KB)  TX bytes:13296 (13.2 KB)
    
    veth077daec Link encap:Ethernet  HWaddr 7e:ef:1b:5e:b3:11
              inet6 addr: fe80::7cef:1bff:fe5e:b311/64 Scope:Link
              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
              RX packets:12 errors:0 dropped:0 overruns:0 frame:0
              TX packets:19 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:0
              RX bytes:928 (928.0 B)  TX bytes:1478 (1.4 KB)

     

    会发现有一个 veth077daec 的网卡设备。咦,这是个啥?

    当运行docker容器后,再次执行ifconfig命令可以看到会多出个网卡驱动veth开头的名字,所以补充下veth。

    veth

    Linux container 中用到一个叫做veth的东西,这是一种新的设备,专门为 container 所建。veth 从名字上来看是 Virtual ETHernet 的缩写,它的作用很简单,就是要把从一个 network namespace 发出的数据包转发到另一个 namespace。veth 设备是成对的,一个是 container 之中,另一个在 container 之外,即在真实机器上能看到的。 
    VETH设备总是成对出现,一端请求发送的数据总是从另一端以请求接受的形式出现。创建并配置正确后,向其一端输入数据,VETH会改变数据的方向并将其送入内核网络子系统,完成数据的注入,而在另一端则能读到此数据。(Namespace,其中往veth设备上任意一端上RX到的数据,都会在另一端上以TX的方式发送出去)veth工作在L2数据链路层,veth-pair设备在转发数据包过程中并不串改数据包内容。 

    1.png

    成数据的注入,而在另一端则能读到此数据。(Namespace,其中往veth设备上任意一端上RX到的数据,都会在另一端上以TX的方式发送出去)veth工作在L2数据链路层,veth-pair设备在转发数据包过程中并不串改数据包内容。 
    显然,仅有veth-pair设备,容器是无法访问网络的。因为容器发出的数据包,实质上直接进入了veth1设备的协议栈里。如果容器需要访问网络,需要使用bridge等技术,将veth1接收到的数据包通过某种方式转发出去 。 
    veth参考链接:http://blog.csdn.net/sld880311/article/details/77650937

    因此,如果要多台主机之间的docker通信,需要使用NAT转换。那么接下来,就是设置iptables规则了!

    配置iptables规则

    主机1

    在主机1上查看默认的nat 规则

    复制代码

    root@ubuntu:~# iptables -t nat -LChain PREROUTING (policy ACCEPT)
    target     prot opt source               destination
    DOCKER     all  --  anywhere             anywhere             ADDRTYPE match dst-type LOCAL
    
    Chain INPUT (policy ACCEPT)
    target     prot opt source               destination
    
    Chain OUTPUT (policy ACCEPT)
    target     prot opt source               destination
    DOCKER     all  --  anywhere            !127.0.0.0/8          ADDRTYPE match dst-type LOCAL
    
    Chain POSTROUTING (policy ACCEPT)
    target     prot opt source               destination
    MASQUERADE  all  --  10.0.128.0/24        anywhere
    
    Chain DOCKER (2 references)
    target     prot opt source               destination
    RETURN     all  --  anywhere             anywhere

    复制代码

     

    这些nat规则,都是docker帮你做的。

    增加一条规则

    iptables -t nat -I PREROUTING -s 10.0.128.0/24 -d 10.0.129.0/24 -j DNAT --to 10.0.128.1

     

    PREROUTING:可以在这里定义进行目的NAT的规则,因为路由器进行路由时只检查数据包的目的ip地址,所以为了使数据包得以正确路由,我们必须在路由之前就进行目的NAT;

     

    上面那一条路由规则是啥意思呢?就是当源地址为10.0.128.0/24网段 访问 10.0.129.0/24 时,在路由之前,将ip转换为10.0.128.1。

    注意:一定要加-d参数。如果不加,虽然docker之间可以互通,但是不能访问网站,比如百度,qq之类的!

    为什么呢?访问10.0.129.0/24 时,通过docker0网卡出去的。但是访问百度,还是通过docker0,就出不去了!

    真正连接外网的是ens32网卡,必须通过它才行!因此必须要指定-d参数!

     

    这个时候,直接ping 主机2上的docker地址

    / # ping 10.0.129.2 -c 3
    PING 10.0.129.2 (10.0.129.2): 56 data bytes
    bytes from 10.0.129.2: seq=0 ttl=64 time=0.073 ms
    bytes from 10.0.129.2: seq=1 ttl=64 time=0.143 ms
    bytes from 10.0.129.2: seq=2 ttl=64 time=0.149 ms
    
    --- 10.0.129.2 ping statistics ---
    packets transmitted, 3 packets received, 0% packet loss
    round-trip min/avg/max = 0.073/0.121/0.149 ms

     

    是可以通讯的!

    注意:iptables必须在 PREROUTING 上面做,而不是常规的 POSTROUTING。我测试在POSTROUTING做规则,始终无法通讯!

     

    主机2

    主机2上添加如下规则:

    iptables -t nat -I PREROUTING -s 10.0.129.0/24 -d 10.0.128.0/24 -j DNAT --to 10.0.129.1

     

    测试ping 主机1上的容器地址

    / # ping 10.0.128.2 -c 3
    PING 10.0.128.2 (10.0.128.2): 56 data bytes
    bytes from 10.0.128.2: seq=0 ttl=64 time=0.092 ms
    bytes from 10.0.128.2: seq=1 ttl=64 time=0.140 ms
    bytes from 10.0.128.2: seq=2 ttl=64 time=0.141 ms
    
    --- 10.0.128.2 ping statistics ---
    packets transmitted, 3 packets received, 0% packet loss
    round-trip min/avg/max = 0.092/0.124/0.141 ms

     

    也是可以通讯的!

    注意:如果发现还是不通,重启一下docker服务,应该就可以了!

     

    五、3台主机测试

    环境介绍

    操作系统服务器地址Dockerd地址
    ubuntu-16.04.5-server-amd64192.168.91.12810.0.128.2
    ubuntu-16.04.5-server-amd64192.168.91.12910.0.129.2
    ubuntu-16.04.5-server-amd64192.168.91.13110.0.131.2

     

     

     

     

     

    拓扑图

    1.png


    一键脚本

    上面已经实现了2台docker之间的通信,如果是3台呢?怎么搞?还是一样的。

    只不过每台主机都要增加2条路由规则以及2条iptables规则。

    做路由规则时,容器搞混淆,为了避免这种问题,做一个一键脚本就可以了!

     

    环境要求

    1. 每台主机已经安装好了docker,并且已经启动

    2. 请确保每台主机的 /etc/default/docker 没有被更改过。还是默认的172.17.0.2/16网段

    如果是虚拟机,直接还原快照即可!

     

    docker_dr.sh

    #!/bin/bash
    
    # 主机ip后缀清单
    hosts="128 129 131"
    
    # 循环主机
    for i in `echo $hosts`;do
        # 写入临时文件
        cat >/tmp/dockerc<<EOF
        DOCKER_OPTS=\"--bip 10.0.$i.1/24\"
    EOF
        # 远程执行命令,更改docker0网段
        ssh 192.168.91.$i "echo $(cat /tmp/dockerc)>> /etc/default/docker"
        # 重启docker服务
        ssh 192.168.91.$i "systemctl restart docker"
        # 清空nat规则
        # ssh 192.168.91.$i "sudo iptables -t nat -F"
    
        # 再次循环
        for j in `echo $hosts`;do
            # 排除自身
            if [ "$j" != "$i" ];then
                # 添加路由
                ssh 192.168.91.$i "route add -net 10.0.$j.0/24 gw 192.168.91.$j"
                # 添加nat规则
                ssh 192.168.91.$i "iptables -t nat -I PREROUTING -s 10.0.$i.0/24 -d 10.0.$j.0/24 -j DNAT --to 10.0.$i.1"
            fi
        done
        # 重启docker服务,写入默认的nat规则
        ssh 192.168.91.$i "systemctl restart docker"
    done

     

    ssh免密登录

    在主机1执行以下命令

    生成秘钥,并写入到authorized_keys

    ssh-keygen -t rsa -P "" -f ~/.ssh/id_rsacat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys

     

    复制公钥,执行以下3个命令

    ssh-copy-id 192.168.91.128ssh-copy-id 192.168.91.129ssh-copy-id 192.168.91.131

     

    正式执行 docker_dr.sh

    bash docker_dr.sh

     执行之后,是没有啥输出的

     

    3台主机都启动alpine镜像

    docker run -it alpine

     

    在主机1上面,测试访问另外2个docker地址,并测试上网,效果如下:

    / # ping 10.0.129.2 -c 2
    PING 10.0.129.2 (10.0.129.2): 56 data bytes
    bytes from 10.0.129.2: seq=0 ttl=64 time=0.078 ms
    bytes from 10.0.129.2: seq=1 ttl=64 time=0.107 ms
    
    --- 10.0.129.2 ping statistics ---
    packets transmitted, 2 packets received, 0% packet loss
    round-trip min/avg/max = 0.078/0.092/0.107 ms
    / # ping 10.0.131.2 -c 2
    PING 10.0.131.2 (10.0.131.2): 56 data bytes
    bytes from 10.0.131.2: seq=0 ttl=64 time=0.073 ms
    bytes from 10.0.131.2: seq=1 ttl=64 time=0.093 ms
    
    --- 10.0.131.2 ping statistics ---
    packets transmitted, 2 packets received, 0% packet loss
    round-trip min/avg/max = 0.073/0.083/0.093 ms
    / # ping www.baidu.com -c 2
    PING www.baidu.com (61.135.169.121): 56 data bytes
    bytes from 61.135.169.121: seq=0 ttl=127 time=25.826 ms
    bytes from 61.135.169.121: seq=1 ttl=127 time=26.172 ms
    
    --- www.baidu.com ping statistics ---
    packets transmitted, 2 packets received, 0% packet loss
    round-trip min/avg/max = 25.826/25.999/26.172 ms
    / #

     

     测试成功,大功告成!!!

     

     

    本文参考链接:

    https://www.cnblogs.com/whych/p/9595671.html

    http://blog.51cto.com/dengaosky/2067727

    https://yq.aliyun.com/articles/602107

    https://blog.csdn.net/m0_38080062/article/details/79292058?tdsourcetag=s_pcqq_aiomsg

     


关键字