5. Docker网络管理

1 Docker网络管理

1.1 Docker的默认网络通信

1.1.1 Docker安装后默认的网络设置

Docker服务安装完成之后, 默认在每个宿主机会生成一个名称为docker0的网卡, 其ip地址都是172.17.0.1/16
root@Ubuntu-1804-1:~# brctl show
bridge name bridge id       STP enabled interfaces
docker0     8000.0242beb7f590   no  
启动第一个容器

root@Ubuntu-1804-1:~# docker run -it --name a1 alpine sh
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
4: eth0@if5: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

宿主机生成网卡

5: vethbf94879@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether 4a:29:7d:2f:37:eb brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::4829:7dff:fe2f:37eb/64 scope link 
       valid_lft forever preferred_lft forever

root@Ubuntu-1804-1:~# brctl show
bridge name bridge id       STP enabled interfaces
docker0     8000.0242beb7f590   no      vethbf94879  # 生成虚拟网卡
启动第二个容器

 root@Ubuntu-1804-1:~# docker run -it --name a2 alpine  sh
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
6: eth0@if7: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

宿主机生成另一个虚拟网卡

7: veth9d408b6@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether 36:17:7e:a0:59:ac brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet6 fe80::3417:7eff:fea0:59ac/64 scope link 
       valid_lft forever preferred_lft forever

root@Ubuntu-1804-1:~# brctl show
bridge name bridge id       STP enabled interfaces
docker0     8000.0242beb7f590   no      veth9d408b6
                                        vethbf94879 # 新的虚拟网卡桥接在docker0上
docker0就相当于一个交换机, 用来在宿主机内部提供容器间, 以及宿主机和容器资源的互访
每启动一个容器, 会单独生成一个vethxxxx网卡, 该网卡成对出现, 相当于一半接在容器上, 一半接在了docker0, 这样容器和宿主机就可以互访
同时, 其他容器也会有vethxxxx网卡, 这样就实现了, 同一宿主机容器间, 容器和宿主机间的通信
图片.png

DNS解析

每个容器启动后, 会将自己的主机名(也就是container id)和ip地址的对应关系解析写到hosts文件里
因此, 容器可以通过主机名访问自己, 但是不能通过其他容器的主机名访问其他容器
同时, 容器用的DNS是宿主机的DNS
/ # hostname
9e24114e3d9e
/ # cat /etc/hosts
127.0.0.1   localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2  9e24114e3d9e

默认路由

默认网关指向了eth0网卡, 通过容器自己的eth0网卡, 可以到docker0, 进而从宿主机的eth0, 通过SNAT访问外网. 因此容器启动后也是可以访问互联网的, 因为会做SNAT转换
/ # route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.17.0.1      0.0.0.0         UG    0      0        0 eth0
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 eth0
root@Ubuntu-1804-1:~# iptables -vnL -t nat
Chain PREROUTING (policy ACCEPT 19 packets, 1831 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    4   208 DOCKER     all  --  *      *       0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL

1.1.2 禁止同一个宿主机内的容器间的通信

默认情况下:

同一个宿主机内的容器间可以互访, dockerd 守护进程启动时, --icc 参数激活了该功能
如果设置 --icc=false, 就可以禁止同一个宿主机内不同容器的互访
修改service文件

ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --icc=false
systemctl daemon-reload
systemctl restart docker

1.1.3 不同的宿主机之间的容器默认是不能互访的

[root@Ubuntu-1804-2:~]# docker run -it --name b3 busybox sh # 从另一个宿主机上启个容器
/ # ping 172.17.0.3 # 0.3是Ubuntu-1上的容器
PING 172.17.0.3 (172.17.0.3): 56 data bytes

原理分析

1. 宿主机内部的容器, 在访问外部网络时, 经过docker0桥接网卡, 再经过宿主机的eth0网卡, 会经过SNAT转换, 假如容器的ip是172.17.0.3/16, 那么经过SNAT转换后, 源地址就变成了宿主机eth的ip, 也就是10.0.0.19
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:0c:29:fd:e6:6f brd ff:ff:ff:ff:ff:ff
    inet 10.0.0.19/24 brd 10.0.0.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fefd:e66f/64 scope link 
       valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:be:b7:f5:90 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:beff:feb7:f590/64 scope link 
       valid_lft forever preferred_lft forever
因此, 通过SNAT转换后, 容器是可以访问互联网的, 因为宿主机eth0,也会被SNAT转换成为Windows的ip地址, 而Windows是有默认路由的
但是, 如果想访问另一台宿主机的容器是不行的, 因为经过宿主机eth0的SNAT转换变成了10.0.0.19, 目的ip是172.17.0.3, 此时宿主机上去往172.17.0.0网络的路由是走本机的docker0桥接网卡, 因此无法去到另一台宿主机的内部网络
如果另一台宿主机的容器网络和本地容器网络不同, 那么也是无法访问的, 因此本地宿主机是没有另一台宿主机内部容器网络的路由的
root@Ubuntu-1804-1:~# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         10.0.0.2        0.0.0.0         UG    0      0        0 eth0
10.0.0.0        0.0.0.0         255.255.255.0   U     0      0        0 eth0
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 docker0
图片.png
想实现不同宿主机内部的容器互联, 简单做法就是让宿主机的eth0桥接到docker0上, 这样就不需要NAT转换了, 整个链路都是在同一个网段, 也就可以互通的, 但前提是两个容器网络是在同一个网段, 并且容器的ip不同. 如果是不同网段, 那么还需要三层路由

1.1.4 自定义容器ip地址

默认情况下, 安装docker后, 宿主机会生成docker0网卡, 默认的网段是172.17.0.0/16
如果, 企业内本身用的就是172.17网段, 或者某些业务已经用了该网段的地址, 那么就会和容器地址产生冲突, 此时需要自定义容器的ip
  1. 创建自定义桥接网卡
root@Ubuntu-1804-1:~# apt -y install bridge-utils
root@Ubuntu-1804-1:~# brctl addbr br0  # 创建自定义的桥接网卡
root@Ubuntu-1804-1:~# ip a a 192.168.100.1/24 dev br0 # 给网卡配置ip地址
root@Ubuntu-1804-1:~# brctl show
bridge name bridge id       STP enabled interfaces
br0     8000.000000000000   no       # 新的桥接网卡
docker0     8000.0242beb7f590   no  
16: br0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether aa:b7:fc:2e:bd:0e brd ff:ff:ff:ff:ff:ff
    inet 192.168.100.1/24 scope global br0
       valid_lft forever preferred_lft forever
root@Ubuntu-1804-1:~# ip link set br0 up # 创建后的网卡时DOWN状态, 需要手动激活
利用ifconfig查看网卡状态, 因为ifconfig只显示激活的网卡

eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.0.0.19  netmask 255.255.255.0  broadcast 10.0.0.255
        inet6 fe80::20c:29ff:fefd:e66f  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:fd:e6:6f  txqueuelen 1000  (Ethernet)
        RX packets 31953  bytes 13337148 (13.3 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 24836  bytes 1835984 (1.8 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
  1. 修改docker的service文件, 让容器桥接在新的br0网卡上
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock -b br0 
root@Ubuntu-1804-1:~# systemctl daemon-reload
root@Ubuntu-1804-1:~# systemctl restart docker
  1. 启动容器测试
root@Ubuntu-1804-1:~# docker run --rm alpine ip a 
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
17: eth0@if18: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:c0:a8:64:02 brd ff:ff:ff:ff:ff:ff
    inet 192.168.100.2/24 brd 192.168.100.255 scope global eth0
       valid_lft forever preferred_lft forever
此时容器启动会用br0作为桥接网卡, 其工作机制和桥接在docker0上是一样的, 只不过换了网段而已
这种方法是新增了一个虚拟网卡, 让容器桥接在这个新网卡上
也可以直接修改docker0的网络, 可以修改service文件或者daemon.json文件

1.2 容器名称互联

新建容器时, docker会自动给容器分配名称,  容器id, ip地址, 导致容器名称, 容器id和ip是不固定的
要想实现区分不同的容器, 实现和确定的目标容器通信, 就需要给容器起个固定的名称, 容器之间通过固定的名称实现确定目标的通信
两种固定名称:
- 容器名称
- 容器名称的别名

1.2.1 容器名称介绍

即实现同一个宿主机上的容器之间可以通过自定义的容器名称相互访问
比如, 一个业务前端静态页面是nginx提供, 动态页面由tomcat提供, 另外还需要lb, 比如haproxy
由于容器在启动的时候其内部ip地址是dhcp随机分配的, 而且容器重启ip会变化
因此需要给容器起个固定的名称, 让各个容器间通过固定的主机名去互访
默认情况下, 容器只能通过自己的hostname(也就是container id)和自己通信, 不能通过其他容器的hostname(container id)访问其他容器
因为hosts文件中, 只记录了自己的hostname(container id)和自己的ip对应关系

1.2.2 容器名称实现

docker run创建容器时, 用--link选项实现容器名称的引用
--link list    Add link to another container
格式:
docker run --name <容器名称>   先创建指定名称的容器
docker run --link <要与之通信的容器id或容器名称> 再创建容器时引用上面容器的名称
  1. 启动容器a1
root@Ubuntu-1804-1:~# docker run -it --name a1 alpine  sh # 容器名称为a1
/ # hostname
39c14d8029e9 # 容器ID
/ # cat /etc/hosts
127.0.0.1   localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2  39c14d8029e9
  1. 启动容器a2, 引用a1
root@Ubuntu-1804-1:~# docker run -it --link a1 --name a2 alpine sh
/ # hostname
e457f75974fa
/ # ping a1 
PING a1 (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.139 ms  # 可以ping通
64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.323 ms
  1. 启动容器时, 如果用--link指定了目标容器的名称, 那么会把目标容器的名称, 容器id和ip地址对应关系写到自己的hosts文件, 因此可以ping通对方容器
/ # cat /etc/hosts
127.0.0.1   localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2  a1 39c14d8029e9
172.17.0.3  e457f75974fa
此时, 后创建的a2是可以通过容器名称访问a1, 但是a1是没法通过名称访问a2的, 因为a1在创建时没有指定a2
c1
/ # ping a2
ping: bad address 'a2'
  1. 重新创建a1, 指向a2
root@Ubuntu-1804-1:~# docker run -it --link a2 --name a1 alpine  sh
/ # ping a2
PING a2 (172.17.0.3): 56 data bytes
64 bytes from 172.17.0.3: seq=0 ttl=64 time=0.071 ms
64 bytes from 172.17.0.3: seq=1 ttl=64 time=0.128 ms

/ # cat /etc/hosts
127.0.0.1   localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.3  a2 251e49b611a3
172.17.0.2  acdee0f5a48a
此时, 两个容器都可以通过名称与对方通信, 并且即使a1删除后重新创建, 其ip和容器id也是不变的, 这样a2上原本保存的hosts信息还生效的
不过, 如果是复杂的环境, 重新创建a1, 那么其容器id和ip就不一定是保持不变的了, 容器id和ip一旦变了, 那么a2上的hosts信息也就失效了
这种方法是先创建一个容器
然后让第二个容器引用第一个容器
再删除第一个容器, 重新创建, 同时引用第二个容器
因此, 以上方法只能指向单项通信, 即后创建的容器通过先创建的容器的id与之通信
第一个容器在创建时即使指定了第二个容器的名称, 但是因为第二个容器本身还没有创建, 那么第一个容器是无法启动的
同时, 先创建的容器, 一旦删除, 再创建, 其容器id和ip都有可能发生变化, 会导致后创建的容器的hosts文件信息失效
一旦第一个容器a1删除后, 又创建了另一个容器a3, 那么a3就会占用原本a1的ip地址, 这时即使a1重新创建,那么它的ip地址也就不是原来的了, 因为,原来的ip会被a3占用
此时, a2仍然可以ping a1, 但是这时的a1其实指向了a3, 因为ip已经被a3占用了
  1. 此时, a2可以手动修改hosts文件, 把ip地址换成a1新的ip地址, 但是这种方式过于笨拙, 因此, 可以重新创建a2, 创建时指向a1, 这时a2拿到的就是a1新的ip

注意: 如果被引用的容器地址发生变化, 那么必须重启引用了它的所有容器, 才能让容器指向新的地址. 因此, 重新创建第二个容器的目的就是重新和被引用的容器建立连接, 获取其名称和ip关系, 写到本地的hosts文件. 不过重新创建第二个容器, 那它的ip地址也可能发生变化, 导致a1的hosts信息失效

过程演示:

1. 创建第一个容器a1, ip为172.17.0.2

root@Ubuntu-1804-1:~# docker run --rm -it --name a1 alpine
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
31: eth0@if32: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
2. 创建第二个容器a2, 引用a1, a2 ip为172.17.0.3

root@Ubuntu-1804-1:~# docker run --rm -it --link a1 --name a2 alpine
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
33: eth0@if34: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
3. 测试a2利用名称ping a1

/ # ping a1
PING a1 (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.207 ms
64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.077 ms
/ # cat /etc/hosts
127.0.0.1   localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2  a1 931294bafddc
172.17.0.3  4f9fcb700113
4. 删除a1, 创建a3, 可以看到此时a3占用了172.17.0.2

root@Ubuntu-1804-1:~# docker run --rm -it --name a3 alpine
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
35: eth0@if36: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
5. 此时虽然a2还可以ping通a1, 但是其实ping的就是a3了

/ # ping a1
PING a1 (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.142 ms
64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.073 ms

/ # cat /etc/hosts
127.0.0.1   localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2  a1 931294bafddc
172.17.0.3  4f9fcb700113
6. 再次创建a1, ip变成了172.17.0.4

root@Ubuntu-1804-1:~# docker run --rm -it --name a1 alpine
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
37: eth0@if38: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:ac:11:00:04 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.4/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
7. 重新创建a2, a2会引用a1新的ip地址172.17.0.4

root@Ubuntu-1804-1:~# docker run --rm -it --link a1 --name a2 alpine
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
39: eth0@if40: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

/ # cat /etc/hosts
127.0.0.1   localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.4  a1 00dd0730c2d1
172.17.0.3  90bdca5f7b5c
8. 此时a2在ping a1, 就是新的地址了, 也就是真正的a1

PING a1 (172.17.0.4): 56 data bytes
64 bytes from 172.17.0.4: seq=0 ttl=64 time=0.192 ms
  1. 还有一种情况, a1如果重新创建时, 修改了名字, 比如改成了aa1, 那么a2就必须重新创建, 同时创建命令的--link 要改成 --link aa1, 也很繁琐. 另外, 容器内的服务, 比如配置文件, 如果引用的是容器名, 那么配置文件和代码也要重新改
解决方案:
利用容器别名实现:

比如: a1是被引用的容器, a2需要引用a1, 那么a2在创建时, 就要给a1的容器名字起个别名, 之后a2内部的代码都指向这个别名即可
一旦a1重新创建, 并且改了名字, 那么a2在重新创建时 --link 只需要修改a1的新名字, 而别名是不变的, 那么a2内部的代码还是可以通过别名访问的a1

实现过程

1. 创建a1, ip为172.17.0.2

root@Ubuntu-1804-1:~# docker run --rm -it --name a1 alpine
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
41: eth0@if42: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
2. 创建a2, 利用别名, a2 ip为172.17.0.3

root@Ubuntu-1804-1:~# docker run --rm -it --link a1:a1-alias --name a2 alpine
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
43: eth0@if44: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
3. a2通过别名和本名都可以访问a1

/ # ping a1
PING a1 (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.361 ms
64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.108 ms

/ # ping a1-alias
PING a1-alias (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.107 ms
/ # cat /etc/hosts
127.0.0.1   localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2  a1-alias 4388e50b27b6 a1 # 实际a2把a1和别名都加入到了hosts文件
172.17.0.3  f3408668b688
4. 重新创建a1, 改名为a11, ip还是172.17.0.2, 因为此时并没有其他容器占用该ip

root@Ubuntu-1804-1:~# docker run --rm -it --name a11 alpine
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
45: eth0@if46: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
5. 重新创建a2, ip还是172.17.0.3

root@Ubuntu-1804-1:~# docker run --rm -it --link a11:a1-alias --name a2 alpine # 新的容器要指向a11, 但是别名不变
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
47: eth0@if48: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
6. a2还是可以ping通a11, 可以用a11或者a1-alias

/ # ping a11
PING a11 (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.072 ms

/ # ping a1-alias
PING a1-alias (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.132 ms
64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.115 ms
/ # cat /etc/hosts
127.0.0.1   localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2  a1-alias 32c528788b79 a11
172.17.0.3  f2e48a8e4e17
7. 此外, 可以给一个容器起多个别名

docker run --rm -it --link a11:"a1-alias a11-alias a111-alias"--name a2 alpine
总结: 此方案只能实现容器镜像内的代码和配置文件不需要修改, 统一利用别名, 但是容器创建时还是要用docker run重新制定引用的容器的实际名字

1.3 Docker网络连接模式

1.3.1 网络模式介绍

Docker的网络支持5种网络模式

none
bridge
container
host
network-name: 自定义模式, 引用前面的模式
  • 范例: 查看默认的网络模式
root@Ubuntu-1804-1:~# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
32b1af5d5917        bridge              bridge              local
c3c657af3b7b        host                host                local
92067dcb99f5        none                null                local

1.3.2 网络模式的指定

  • 默认新建的容器使用Bridge模式, 创建容器时, docker run命令使用以下选项指定网络模式
docker run --network <mode>
docker run --net=<mode>

<mode>: 可以是以下值
none
bridge
host
container: <容器名称或容器ID>
<自定义网络名称>

1.3.3 bridge桥接网络模式

图片.png
实现NAT功能, 需要宿主机开启ip_forward, 不过宿主机安装docker后, 会自动开启ip_forward
net.ipv4.ip_forward=1
图片.png
本模式是docker的默认模式, 创建容器时不指定模式, 就是用桥接模式
此模式下, 创建容器后会分配给容器一个ip地址, 并且将容器连接到一个虚拟网桥(默认docker0), 通过SNAT与外界通信, 外界应用如果想访问宿主机内部容器, 需要使用DNAT, 启动容器, 用-p|P进行容器的端口发布
桥接网络的特点:
网络资源隔离: 不同宿主机之间的容器时无法直接通信的, 各自使用独立的网络
无需手动配置: 容器默认自动获取172.17.0.0/16的ip地址, 此地址可以修改
可访问外网: 利用宿主机的物理网卡, 通过SNAT访问外网
外部主机无法直接访问容器: 可以通过配置DNAT接受外网的访问
性能较低: 因为访问需要通过NAT, 网络转换带来额外的消耗
端口管理繁琐: 每个容器必须手动指定一个端口, 容易产生端口冲突
  • 查看bridge模式信息
docker network inspect bridge

修改桥接模式默认的网段地址

之前修改-b, 是添加了一个虚拟桥接网卡, 让容器都桥接在新的虚拟网卡上, 进而获得自定义的ip地址
这里的修改网段只是修改默认网段ip, 并不会创建桥接网卡, 还是用的docker0
方法1: 修改docker的service文件

ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --bip=192.168.70.100/24  # 这里要写一个ip地址, 表示这个地址是给docker0用的, 而容器的网络是这个docker0所在的网段

systemctl daemon-reload
systemctl restart docker
修改后, 容器的ip地址为192.168.70.0网段

root@Ubuntu-1804-1:~# docker run --rm -it --name a1 alpine sh
/ # ip a
51: eth0@if52: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:c0:a8:46:01 brd ff:ff:ff:ff:ff:ff
    inet 192.168.70.1/24 brd 192.168.70.255 scope global eth0
       valid_lft forever preferred_lft forever

docker0的ip也变成了指定的ip

3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:be:b7:f5:90 brd ff:ff:ff:ff:ff:ff
    inet 192.168.70.100/24 brd 192.168.70.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:beff:feb7:f590/64 scope link 
       valid_lft forever preferred_lft forever
方法2: 修改docker配置文件/etc/docker/daemon.json(修改该文件会导致网络配置被永久保存, 即使恢复后重启也无法复原, 只能修改service文件复原)

取消service文件中de--bip
systemctl daemon-reload
root@Ubuntu-1804-1:~# vim /etc/docker/daemon.json 

{
  "registry-mirrors": ["https://odzb6i12.mirror.aliyuncs.com"],
  "bip":"192.168.60.1/24"                                                                                                                                                                         
}
systemctl restart docker
docker0的地址变成了60.1

3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:be:b7:f5:90 brd ff:ff:ff:ff:ff:ff
    inet 192.168.60.1/24 brd 192.168.60.255 scope global docker0

容器也是60网段

53: eth0@if54: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:c0:a8:3c:02 brd ff:ff:ff:ff:ff:ff
    inet 192.168.60.2/24 brd 192.168.60.255 scope global eth0
       valid_lft forever preferred_lft forever

1.3.4 host网络模式

图片.png
如果指定了host模式启动容器, 那么新创建的容器不会创建自己的虚拟网卡, 而是直接使用宿主机的网卡进和ip地址和端口号
因此, 在容器中查看ip地址就是宿主机, 访问容器的时候直接使用宿主机的ip+容器定义的端口号即可
不过容器内除网络以外的其他资源: 如, 文件系统, 系统进程等仍然是和宿主机保持隔离的
此模式由于直接使用宿主机的网络, 无需NAT转换, 因此网络性能最好
但是各个容器内使用的端口不能相同, 适用于运行容器端口比较固定的业务
host模式下, 容器会直接使用宿主机的ip地址, 外部访问容器时访问的就是宿主机的ip:容器的端口, 因此, 一旦容器的端口相同, 比如都是web服务,  那么就会产生冲突

host网络模式特点:

使用参数 --network host指定
共享宿主机网络
网络性能无损耗
网络故障排除相对简单
各个容器网络无隔离
网络资源无法分别统计
端口管理困难: 容器产生端口冲突
不支持端口映射

实现host模式:

root@Ubuntu-1804-1:~# docker run --rm -it --network host --name web1 busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel qlen 1000
    link/ether 00:0c:29:fd:e6:6f brd ff:ff:ff:ff:ff:ff
    inet 10.0.0.19/24 brd 10.0.0.255 scope global eth0  # 使用宿主机的ip地址
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fefd:e66f/64 scope link 
       valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue 
    link/ether 02:42:4b:8a:ac:6f brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
端口号也是用的宿主机的
/ # netstat -tuanlp
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      -
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -
tcp        0      0 10.0.0.19:22            10.0.0.1:59422          ESTABLISHED -
tcp        0      0 10.0.0.19:22            10.0.0.1:59425          ESTABLISHED -
tcp        0      0 10.0.0.19:22            10.0.0.1:59426          ESTABLISHED -
tcp        0      0 10.0.0.19:22            10.0.0.1:59424          ESTABLISHED -
tcp        0      0 10.0.0.19:22            10.0.0.1:59419          ESTABLISHED -
tcp        0      0 :::22                   :::*                    LISTEN      -
udp        0      0 127.0.0.53:53           0.0.0.0:*                           -
运行httpd服务

/ # httpd
/ # netstat -tuanlp
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      -
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -
tcp        0      0 10.0.0.19:22            10.0.0.1:59422          ESTABLISHED -
tcp        0      0 10.0.0.19:22            10.0.0.1:59425          ESTABLISHED -
tcp        0      0 10.0.0.19:22            10.0.0.1:59426          ESTABLISHED -
tcp        0      0 10.0.0.19:22            10.0.0.1:59424          ESTABLISHED -
tcp        0      0 10.0.0.19:22            10.0.0.1:59419          ESTABLISHED -
tcp        0      0 :::22                   :::*                    LISTEN      -
tcp        0      0 :::80                   :::*                    LISTEN      10/httpd # 开启80服务
宿主机开启80端口
root@Ubuntu-1804-1:~# ss -ntl
State                    Recv-Q                    Send-Q                                         Local Address:Port                                         Peer Address:Port                    
LISTEN                   0                         128                                            127.0.0.53%lo:53                                                0.0.0.0:*                       
LISTEN                   0                         128                                                  0.0.0.0:22                                                0.0.0.0:*                       
LISTEN                   0                         128                                                     [::]:22                                                   [::]:*                       
LISTEN                   0                         9                                                          *:80                                                      *:*   
给容器建个默认页面, 测试外部主机访问

/ # echo 'webpage in docker' > index.html
[root@Ubuntu-1804-2:~]# curl 10.0.0.19
webpage in docker

1.3.5 none模式

在使用none模式, Docker容器不会进行任何网络配置, 没有网卡, 没有ip, 也没有路由
因此, 无法与外界通信, 需要手动添加网卡配置ip等, 极少使用
  • none模式特点
- 使用参数 --network none指定
- 默认无网络功能, 无法和外部通信

实现none模式

root@Ubuntu-1804-1:~# docker run --rm -it --network none -p 80:80 --name web-none busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
/ # cat /etc/hosts
127.0.0.1   localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
/ # ping www.baidu.com
ping: bad address 'www.baidu.com'

1.3.6 container模式

使用此模式创建的容器指定和一个已经存在的容器共享一个网络, 而不是和宿主机共享网
新创建的容器不会创建自己的网卡也不会配置自己的ip, 而是和一个被指定的已经存在的容器共享ip和端口范围
因此这个容器的端口不能和被指定容器的端口冲突, 除了网络之外的文件系统, 进程信息等仍然保持相互隔离, 两个容器的进程可以通过lo网卡进行通信

container模式特点

使用参数 --network container:名称或ID 指定
与宿主机网络空间隔离
容器间共享网络空间
适合频繁的容器间的网络通信
直接使用对方的网络, 较少使用

container模式实现

1. 创建第一个容器
启动一个none模式的容器, 没有ip
root@Ubuntu-1804-1:~# docker run --rm -it --network none --name server1 busybox 
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
2. 创建第二个容器
启动第二个容器, 第二个容器的镜像可以和第一个不一样, 因为只是共享网络, 和镜像本身无关
root@Ubuntu-1804-1:~# docker run --rm -it --name server2 --network container:server1 alpine
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
3. server1上开启http服务, 观察server2的端口
server2共享了server1的端口, 开启了80
/ # netstat -tuanlp 
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 :::80                   :::*                    LISTEN      -
4. server1开启httpd, 创建默认页面, 测试server2可以通过127.0.0.1访问
Server1
/ # echo 'index.html' > index.html
/ # httpd -f 
Server2
/ # wget -qO - 127.0.0.1
index.html

1.3.7 自定义网络模式

除了4种网络模式外, 还可以自定义网络, 使用自定义的网段, 网关信息等, 自定义网络其实就是利用4种网络模型的一种, 自定义其网络配置, 本质并没有变
注意: 自定义网络可以直接通过容器名进行相互的访问, 而无需使用 --link
可以使用自定义网络模式, 实现不同集群应用的独立网络管理, 而互不影响, 而且在同一个网络中, 可以直接利用容器名相互访问, 非常便利

1.3.7.1 自定义网络的实现

  • 创建自定义网络
docker network create -d <mode> --subnet <CIDR> --gateway <网关> <自定义网络名称>
  • 查看自定义网络信息
docker network inspect <自定义网络名称或网络ID>
  • 引用自定义网络
docker run --network <自定义网络名称> <镜像名称>
  • 删除自定义网络
docker network rm <自定义网络名称或网络ID>

1.3.7.2 实现自定义网络

  1. 创建自定义网络
root@Ubuntu-1804-1:~# docker network create -d bridge --subnet 172.27.0.0/16 --gateway 172.27.0.1 test-net 
root@Ubuntu-1804-1:~# docker inspect test-net
...
            "Config": [
                {
                    "Subnet": "172.27.0.0/16",
                    "Gateway": "172.27.0.1"
                }
            ]
...
创建自定义网络会生成单独的虚拟网卡

4: br-a0e19def29ea: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:fc:f8:90:35 brd ff:ff:ff:ff:ff:ff
    inet 172.27.0.1/16 brd 172.27.255.255 scope global br-a0e19def29ea
       valid_lft forever preferred_lft forever
  1. 基于新的桥接网络创建容器
root@Ubuntu-1804-1:~# docker run --rm -it --network test-net --name b1 busybox sh
/ # ip a
5: eth0@if6: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether 02:42:ac:1b:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.27.0.2/16 brd 172.27.255.255 scope global eth0
       valid_lft forever preferred_lft forever
/ # ping www.baidu.com
PING www.baidu.com (39.156.66.14): 56 data bytes
64 bytes from 39.156.66.14: seq=0 ttl=127 time=95.187 ms
自定义网络只能基于桥接模式实现
1. host模式每个宿主机只允许一个实例存在, 安装docker后, 宿主机本身就会有一个桥接, 一个host和一个none实例
2. none模式不支持自定义网络
3. container模式也是无法创建, 会报错
Error response from daemon: plugin "container" not found
  1. 再启动一个容器b2, 测试b1和b2之间可以基于容器名称通信
root@Ubuntu-1804-1:~# docker run --rm -it --network test-net --name b2 busybox sh
/ # ping b1
PING b1 (172.27.0.2): 56 data bytes
64 bytes from 172.27.0.2: seq=0 ttl=64 time=0.252 ms
64 bytes from 172.27.0.2: seq=1 ttl=64 time=0.120 ms

/ # ping b2
PING b2 (172.27.0.3): 56 data bytes
64 bytes from 172.27.0.3: seq=0 ttl=64 time=0.180 ms
64 bytes from 172.27.0.3: seq=1 ttl=64 time=0.123 ms

1.3.8 实现同一个宿主机内, 不同网络的容器通信

启动两个容器, 一个使用自定义网络, 一个使用默认的bridge网络, 默认是无法通信的

实验环境:

3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:4b:8a:ac:6f brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0  # 默认的桥接网络, 使用172.17.0.1/16网段
       valid_lft forever preferred_lft forever
4: br-a0e19def29ea: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:fc:f8:90:35 brd ff:ff:ff:ff:ff:ff
    inet 172.27.0.1/16 brd 172.27.255.255 scope global br-a0e19def29ea  # 自定义的网络, 用172.27.0.1/16网络
       valid_lft forever preferred_lft forever
    inet6 fe80::42:fcff:fef8:9035/64 scope link 
       valid_lft forever preferred_lft forever
图片.png
  1. 创建一个容器, 基于默认的bridge网络
root@Ubuntu-1804-1:~# docker run -it --rm --name container1 alpine sh 
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
9: eth0@if10: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
  1. 创建另一个容器, 基于自定义的bridge网络
root@Ubuntu-1804-1:~# docker network ls
a0e19def29ea        test-net            bridge              local
                    "Subnet": "172.27.0.0/16",
                    "Gateway": "172.27.0.1"
root@Ubuntu-1804-1:~# docker run -it --rm --network test-net --name container2 busybox
/ # ip ar
11: eth0@if12: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether 02:42:ac:1b:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.27.0.2/16 brd 172.27.255.255 scope global eth0
       valid_lft forever preferred_lft forever
  1. 此时, 两个容器间是无法ping通的
/ # ping 172.27.0.2
PING 172.27.0.2 (172.27.0.2): 56 data bytes

/ # ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2): 56 data bytes

同一个宿主机内, 不同的网段里的容器无法通信的原因

不同网段的容器间通信, 会被iptables阻止, 因为是内部的通信, 不涉及出网, 所以不涉及nat表
而filter表支持input, output和forward链, 而两个容器间的通信是靠的forward链, 因为, 容器是和另一个容器通信, 并不是和宿主机通信
  • 检查宿主机filter表的forward链
FORWARD链规定了, 无论是docker0网卡还是自定义的桥接网卡, 当有流量发送到该网卡时, 只要目标不是本网卡, 那就会被DROP掉

root@Ubuntu-1804-1:~# iptables -S
-P INPUT ACCEPT
-P FORWARD DROP  # FORWARD链默认就是拒绝所有
-P OUTPUT ACCEPT
-N DOCKER
-N DOCKER-ISOLATION-STAGE-1
-N DOCKER-ISOLATION-STAGE-2
-N DOCKER-USER
-A FORWARD -j DOCKER-USER
-A FORWARD -j DOCKER-ISOLATION-STAGE-1
-A FORWARD -o br-a0e19def29ea -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -o br-a0e19def29ea -j DOCKER
-A FORWARD -i br-a0e19def29ea ! -o br-a0e19def29ea -j ACCEPT
-A FORWARD -i br-a0e19def29ea -o br-a0e19def29ea -j ACCEPT
-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -o docker0 -j DOCKER
-A FORWARD -i docker0 ! -o docker0 -j ACCEPT
-A FORWARD -i docker0 -o docker0 -j ACCEPT
-A DOCKER-ISOLATION-STAGE-1 -i br-a0e19def29ea ! -o br-a0e19def29ea -j DOCKER-ISOLATION-STAGE-2
-A DOCKER-ISOLATION-STAGE-1 -i docker0 ! -o docker0 -j DOCKER-ISOLATION-STAGE-2
-A DOCKER-ISOLATION-STAGE-1 -j RETURN
-A DOCKER-ISOLATION-STAGE-2 -o br-a0e19def29ea -j DROP
-A DOCKER-ISOLATION-STAGE-2 -o docker0 -j DROP
-A DOCKER-ISOLATION-STAGE-2 -j RETURN
-A DOCKER-USER -j RETURN

解决方法:1

  1. 修改防火墙, 在FORWARD第一条插入允许所有
root@Ubuntu-1804-1:~# iptables -I FORWARD -j ACCEPT
/ # ping 172.27.0.2
PING 172.27.0.2 (172.27.0.2): 56 data bytes
64 bytes from 172.27.0.2: seq=530 ttl=63 time=0.524 ms
64 bytes from 172.27.0.2: seq=531 ttl=63 time=0.146 ms
64 bytes from 172.27.0.2: seq=532 ttl=63 time=0.174 ms

/ # ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: seq=499 ttl=63 time=0.385 ms
64 bytes from 172.17.0.2: seq=500 ttl=63 time=0.342 ms
64 bytes from 172.17.0.2: seq=501 ttl=63 time=0.131 ms

解决方案2:

  1. 把容器互相加入到对让所在的桥接网络里
这种方法会在容器内生成一个新的虚拟网卡, 而该虚拟网卡的ip地址, 是对方容器所在网段的, 这样就可以实现彼此通信
先取消之前加的iptables

root@Ubuntu-1804-1:~# iptables -D FORWARD 1
  1. 使用docker network connect NETWORK CONTAINER命令
root@Ubuntu-1804-1:~# brctl show 
bridge name bridge id       STP enabled interfaces
br-a0e19def29ea     8000.0242fcf89035   no      veth6a8c2ad
docker0     8000.02424b8aac6f   no      veth71da627
root@Ubuntu-1804-1:~# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
11396ffd55a2        bridge              bridge              local # 172.17.0.1/16
c3c657af3b7b        host                host                local
92067dcb99f5        none                null                local
a0e19def29ea        test-net            bridge              local # 172.27.0.1/16
container1属于默认的bridge网络, 需要被加入到 test-net桥接网络
container2属于自定义的bridge网络, 需要被加入到bridge默认桥接网络
root@Ubuntu-1804-1:~# docker network connect test-net container1
root@Ubuntu-1804-1:~# docker network connect bridge container2
Container1中, 创建了虚拟网卡, 地址是Container2的
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
9: eth0@if10: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
13: eth1@if14: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:ac:1b:00:03 brd ff:ff:ff:ff:ff:ff
    inet 172.27.0.3/16 brd 172.27.255.255 scope global eth1
       valid_lft forever preferred_lft forever
Container2中, 创建了新的网卡, 地址是Container1的
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
11: eth0@if12: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether 02:42:ac:1b:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.27.0.2/16 brd 172.27.255.255 scope global eth0
       valid_lft forever preferred_lft forever
15: eth1@if16: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.3/16 brd 172.17.255.255 scope global eth1
       valid_lft forever preferred_lft forever

1.4 实现跨宿主机之间的容器互联

图片.png

1.4.1 不同容器处在相同的网段, 只是ip不同

默认情况下, 安装了docker后, 宿主机上的eth0网卡, 和docker0网卡是没有桥接在一起的, 因此, 不同宿主机上的容器是无法互访的 
因此, 需要做的就是, 在宿主机上, 把eth0网卡, 桥接到docker0网卡, 即可实现不同宿主机上容器的互访
这是最简单的方案, 不过要求不同宿主机上的容器所处的网段是相同的, 否则还额外需要三层路由
另外, 一旦宿主机的eth0网卡桥接到了docker0, 那么Windows就无法连接到虚拟机了, 去往虚拟机VMnet8的路由会失效

方案实现:

先确保两个宿主机的docker0都是默认的172.17.0.0/16网卡, 这种通信方式要求不同容器的宿主机是在相同的网段, 但是ip地址不同
  1. ubuntu1上启动容器a1, 拿到ip172.17.0.2/16
root@Ubuntu-1804-1:~# docker run --rm -it --name a1 alpine 
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
7: eth0@if8: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
  1. ubuntu1上再启动一个容器a2, 拿到ip地址172.17.0.3/16
root@Ubuntu-1804-1:~# docker run --rm -it --name a2 alpine 
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
9: eth0@if10: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
  1. ubuntu2启动一个容器b1, 拿到ip地址172.17.0.2/16, 这样就在两个宿主机上构建了两个拥有同一网段不同ip的容器
[root@Ubuntu-1804-2:~]# docker run --rm -it --name b1 busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
6: eth0@if7: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
  1. 将ubuntu1上的a1关掉, 避免和ubuntu2上的b1地址冲突, 默认情况下, 两个容器是无法互ping的
/ # ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2): 56 data bytes

/ # ping 172.17.0.3
PING 172.17.0.3 (172.17.0.3): 56 data bytes
  1. 分别将宿主机的eth0桥接到各自的docker0网卡上
两个宿主机分别执行, 这时Windows就会和虚拟机断开, 因为VMnet8的路由会失效
brctl addif docker0 eth0
  1. 到VMware上验证, 可见不同宿主机, 同一个网段, 不同ip的容器实现了互访
图片.png
图片.png
此方案要求两台宿主机的容器是处于同一个网段的, 否则如果是不同的网段, 还需要添加三层路由
而真正的企业环境一般不同宿主机内部的容器网络有可能是不同的,因此以上方法用途不是很大

1.4.2 不同容器处在不同的网段

容器的跨主机互联指的是A宿主机的容器可以访问B宿主机上的容器, 但是前提是保证各宿主机之间的网络是可以相同通信的, 然后各个容器才可以通过宿主机访问到对方的容器

默认情况下, 由于宿主机的eth0网卡和容器的桥接网卡docker0是断开没有连接的, 所以不同宿主机内的容器才无法通信

实现原理: 在宿主机做一个网络路由就可以实现A宿主机的容器访问B主机的容器的目的

此方法只适合小型环境, 复杂的架构还是要基于k8s实现

  • 步骤1: 修改宿主机的网段, 实现两个宿主机的容器网络不同
ubuntu-1
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock  --bip=172.17.100.1/24  
ubuntu-2
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock  --bip=172.17.200.1/24 
systemctl daemon-reload
systemctl restart docker
  • 步骤2: 第一个宿主机ubuntu-1启动一个容器
root@Ubuntu-1804-1:~# docker run -it --name server1 --rm alpine sh
/ # ip a
11: eth0@if12: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:ac:11:64:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.100.2/24 brd 172.17.100.255 scope global eth0
       valid_lft forever preferred_lft forever

第二个宿主机ubuntu-2启动一个容器

[root@Ubuntu-1804-2:~]# docker run -it --name server2 --rm alpine sh
/ # ip a
8: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:ac:11:c8:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.200.2/24 brd 172.17.200.255 scope global eth0
       valid_lft forever preferred_lft forever
  • 这时两个容器是无法互访的, 因为这时实际有五个网段, 两个宿主机的网段,两个容器的网段, 还有Windows的VMnet8, 而容器要想访问另一个宿主机内的容器, 要在宿主机上添加去往另一端容器网络的路由, gw下一条指向另一个宿主机的eth0. 默认情况, 宿主机是没有去往对方内部容器网段的路由的

  • 步骤3: 在各宿主机添加静态路由, 目标网络是对方容器的网段, 网关指向对方宿主机的eth0地址

ubuntu-1
root@Ubuntu-1804-1:~# route add -net 172.17.200.0/24 gw 10.0.0.29
root@Ubuntu-1804-1:~# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         10.0.0.2        0.0.0.0         UG    0      0        0 eth0
10.0.0.0        0.0.0.0         255.255.255.0   U     0      0        0 eth0
172.17.100.0    0.0.0.0         255.255.255.0   U     0      0        0 docker0
172.17.200.0    10.0.0.29       255.255.255.0   UG    0      0        0 eth0

ubuntu-2
[root@Ubuntu-1804-2:~]# route add -net 172.17.100.0/24 gw 10.0.0.19
[root@Ubuntu-1804-2:~]# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         10.0.0.2        0.0.0.0         UG    0      0        0 eth0
10.0.0.0        0.0.0.0         255.255.255.0   U     0      0        0 eth0
172.16.100.0    10.0.0.19       255.255.255.0   UG    0      0        0 eth0
172.17.200.0    0.0.0.0         255.255.255.0   U     0      0        0 docker0
  • 步骤4: 修改各宿主机的防火墙规则, 因为默认会有DROP规则来禁止来自外部容器的流量进入本地
两台宿主机都添加允许10.0.0.0/24网段, 因为容器访问外部会经过SNAT转换为宿主机的eth0地址
iptables -A FORWARD -s 10.0.0.0/24 -j ACCEPT
  • 步骤5: 测试可以互ping即可
图片.png

1.5 利用Open vSwitch实现跨主机的容器之间网络互联

Open vSwitch可以利用GRE隧道实现容器跨主机跨互联网通信
图片.png
图片.png
  1. 修改两台主机的docker0, 使用不同的网段
第一台主机
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --bip=172.17.1.1/24
root@ovs1:~# systemctl daemon-reload
root@ovs1:~# systemctl restart docker
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:0c:29:fd:e6:6f brd ff:ff:ff:ff:ff:ff
    inet 10.0.0.19/24 brd 10.0.0.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fefd:e66f/64 scope link 
       valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:fe:00:f9:0f brd ff:ff:ff:ff:ff:ff
    inet 172.17.1.1/24 brd 172.17.1.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:feff:fe00:f90f/64 scope link 
       valid_lft forever preferred_lft forever
第二台主机
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --bip=172.27.1.1/24
[root@ovs2:~]# systemctl daemon-reload
[root@ovs2:~]# systemctl restart docker
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:0c:29:16:cb:fe brd ff:ff:ff:ff:ff:ff
    inet 10.0.0.29/24 brd 10.0.0.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fe16:cbfe/64 scope link 
       valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:7f:58:47:98 brd ff:ff:ff:ff:ff:ff
    inet 172.27.1.1/24 brd 172.27.1.255 scope global docker0
       valid_lft forever preferred_lft forever
  1. 在两个宿主机安装Open vSwith和bridge-utils
第一台主机
root@ovs1:~# apt -y install openvswitch-switch bridge-utils
root@ovs1:~# ps -e | grep ovs
 12463 ?        00:00:00 ovsdb-server
 12524 ?        00:00:00 ovs-vswitchd
root@ovs1:~# ovs-appctl --version
ovs-appctl (Open vSwitch) 2.9.8
root@ovs1:~# ovs-ofctl --version
ovs-ofctl (Open vSwitch) 2.9.8
OpenFlow versions 0x1:0x5
root@ovs1:~# brctl show 
bridge name bridge id       STP enabled interfaces
docker0     8000.0242fe00f90f   no
第二台主机
[root@ovs2:~]# apt -y install openvswitch-switch bridge-utils
[root@ovs2:~]# ps -e | grep ovs
 13148 ?        00:00:00 ovsdb-server
 13210 ?        00:00:00 ovs-vswitchd
[root@ovs2:~]# ovs-appctl --version
ovs-appctl (Open vSwitch) 2.9.8
[root@ovs2:~]# ovs-ofctl --version
ovs-ofctl (Open vSwitch) 2.9.8
OpenFlow versions 0x1:0x5
[root@ovs2:~]# brctl show
bridge name bridge id       STP enabled interfaces
docker0     8000.02427f584798
  1. 安装了Open vSwitch后, 并不会产生新的桥接网卡, 而是需要手动添加网卡
第一台主机

root@ovs1:~# ovs-vsctl add-br obr0
root@ovs1:~# ip link set  obr0 up
root@ovs1:~# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:0c:29:fd:e6:6f brd ff:ff:ff:ff:ff:ff
    inet 10.0.0.19/24 brd 10.0.0.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fefd:e66f/64 scope link 
       valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:fe:00:f9:0f brd ff:ff:ff:ff:ff:ff
    inet 172.17.1.1/24 brd 172.17.1.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:feff:fe00:f90f/64 scope link 
       valid_lft forever preferred_lft forever
6: ovs-system: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether ce:4f:74:61:38:b3 brd ff:ff:ff:ff:ff:ff
7: obr0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
    link/ether e2:8e:2b:77:83:4e brd ff:ff:ff:ff:ff:ff
    inet6 fe80::e08e:2bff:fe77:834e/64 scope link 
       valid_lft forever preferred_lft forever
第二台主机

[root@ovs2:~]# ovs-vsctl add-br obr0
[root@ovs2:~]# ip link set dev obr0 up
[root@ovs2:~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:0c:29:16:cb:fe brd ff:ff:ff:ff:ff:ff
    inet 10.0.0.29/24 brd 10.0.0.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fe16:cbfe/64 scope link 
       valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:7f:58:47:98 brd ff:ff:ff:ff:ff:ff
    inet 172.27.1.1/24 brd 172.27.1.255 scope global docker0
       valid_lft forever preferred_lft forever
4: ovs-system: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether aa:40:db:49:c0:26 brd ff:ff:ff:ff:ff:ff
5: obr0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
    link/ether 6e:73:e7:c5:ee:46 brd ff:ff:ff:ff:ff:ff
    inet6 fe80::6c73:e7ff:fec5:ee46/64 scope link 
       valid_lft forever preferred_lft forever
  1. 在两个宿主机创建gre隧道, (remote_ip为对端宿主机的ip)
第一台主机

root@ovs1:~# ovs-vsctl add-port obr0 gre0 -- set interface gre0 type=gre options:remote_ip=10.0.0.29
root@ovs1:~# ovs-vsctl list-ports obr0
gre0
root@ovs1:~# ovs-vsctl show
a6e41dc1-69ff-4ba0-a5bb-febdcb103d2b
    Bridge "obr0" # 桥接网卡
        Port "gre0"
            Interface "gre0"
                type: gre
                options: {remote_ip="10.0.0.29"}  # 对端ip地址
        Port "obr0"
            Interface "obr0"
                type: internal
    ovs_version: "2.9.8"
第二台主机

[root@ovs2:~]# ovs-vsctl add-port obr0 gre0 -- set interface gre0 type=gre options:remote_ip=10.0.0.19
[root@ovs2:~]# ovs-vsctl list-ports obr0
gre0
c5448eb7-e0c9-4210-a331-ef4687bfb000
    Bridge "obr0" # 桥接网卡
        Port "obr0"
            Interface "obr0"
                type: internal
        Port "gre0"
            Interface "gre0"
                type: gre
                options: {remote_ip="10.0.0.19"} # 对端ip地址
    ovs_version: "2.9.8"
  1. 在两个宿主机将obr0作为接口假如docker0网桥
第一台主机
root@ovs1:~# brctl addif docker0 obr0
root@ovs1:~# brctl show
bridge name bridge id       STP enabled interfaces
docker0     8000.0242fe00f90f   no      obr0
第二台主机
[root@ovs2:~]# brctl addif docker0 obr0
[root@ovs2:~]# brctl show
bridge name bridge id       STP enabled interfaces
docker0     8000.02427f584798   no      obr0
  1. 两个宿主机添加静态路由(网段为对端docker网段)
第一台主机添加对端的docker网段

root@ovs1:~# ip route add 172.27.1.0/24 dev docker0
root@ovs1:~# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         10.0.0.2        0.0.0.0         UG    0      0        0 eth0
10.0.0.0        0.0.0.0         255.255.255.0   U     0      0        0 eth0
172.17.1.0      0.0.0.0         255.255.255.0   U     0      0        0 docker0
172.27.1.0      0.0.0.0         255.255.255.0   U     0      0        0 docker0
第二台主机

[root@ovs2:~]# ip route add 172.17.1.0/24 dev docker0
[root@ovs2:~]# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         10.0.0.2        0.0.0.0         UG    0      0        0 eth0
10.0.0.0        0.0.0.0         255.255.255.0   U     0      0        0 eth0
172.17.1.0      0.0.0.0         255.255.255.0   U     0      0        0 docker0
172.27.1.0      0.0.0.0         255.255.255.0   U     0      0        0 docker0
  1. 跨主机访问测试
root@ovs1:~# docker run -it alpine sh
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
2: gre0@NONE: <NOARP> mtu 1476 qdisc noop state DOWN qlen 1000
    link/gre 0.0.0.0 brd 0.0.0.0
3: gretap0@NONE: <BROADCAST,MULTICAST> mtu 1462 qdisc noop state DOWN qlen 1000
    link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff
4: erspan0@NONE: <BROADCAST,MULTICAST> mtu 1450 qdisc noop state DOWN qlen 1000
    link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff
12: eth0@if13: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:ac:11:01:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.1.2/24 brd 172.17.1.255 scope global eth0
       valid_lft forever preferred_lft forever
[root@ovs2:~]# docker run -it alpine sh
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
2: gre0@NONE: <NOARP> mtu 1476 qdisc noop state DOWN qlen 1000
    link/gre 0.0.0.0 brd 0.0.0.0
3: gretap0@NONE: <BROADCAST,MULTICAST> mtu 1462 qdisc noop state DOWN qlen 1000
    link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff
4: erspan0@NONE: <BROADCAST,MULTICAST> mtu 1450 qdisc noop state DOWN qlen 1000
    link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff
10: eth0@if11: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:ac:1b:01:02 brd ff:ff:ff:ff:ff:ff
    inet 172.27.1.2/24 brd 172.27.1.255 scope global eth0
       valid_lft forever preferred_lft forever
/ # ping 172.27.1.2
PING 172.27.1.2 (172.27.1.2): 56 data bytes
64 bytes from 172.27.1.2: seq=0 ttl=63 time=0.685 ms
64 bytes from 172.27.1.2: seq=1 ttl=63 time=0.580 ms
/ # ping 172.17.1.2
PING 172.17.1.2 (172.17.1.2): 56 data bytes
64 bytes from 172.17.1.2: seq=0 ttl=63 time=4.701 ms
64 bytes from 172.17.1.2: seq=1 ttl=63 time=0.939 ms

2 Docker网络的总结

1. 四种网络模式

1.1 bridge模式

这是Docker默认的网络模式, 宿主机安装Docker后, 会生成一个桥接网卡docker0, 默认的网段为172.17.0.0/16.
启动容器后, 会生成一个虚拟网卡, 该虚拟网卡相当于一半接在了宿主机docker0上, 另一个接在了容器上, 并且给容器分配一个ip地址. 
利用这个虚拟网卡, 容器可以和宿主机通信, 而容器和外部网络通信, 会经过宿主机网卡eth0做SNAT转换
外部主机如果要访问容器内部资源, 需要启动容器时暴露容器内部端口, 使用-p|P, 基于DNAT将容器发布出来
bridge模式下, 宿主机内部不同的容器可以互相访问, 因为,每个容器都会获得一个虚拟网卡, 这个虚拟网卡的另一半都是接在docker0上, 因此, 彼此之间可以互相访问
但是, 这时不同宿主机之间的容器是无法互访的, 因为在宿主机上是没有去往另一个主机的容器网络的路由的, 桥接模式下, 每个宿主机的容器网络都是默认用172.17.0.0/16网络
而在宿主机上去往该网络的路由的出口是docker0, 因此, 当一个宿主机的容器要访问另一个宿主机容器时, docker0会认为访问的是自己本地的网络, 所以无法通信

1.2 none模式

none模式启动容器后, 是没有网络的, 没有ip,没有DNS, 需要手动配置网络, 基本不会用

1.3 host模式

host模式启动容器后, 容器会使用宿主机的网络资源, 包括ip地址, 端口和DNS, 这种模式下, 网络性能最好
因为, 容器访问外网, 外网访问容器都是直接访问的宿主机的ip, 不用经过NAT转换
host模式下, 不支持端口发布, 因为本身就是用的宿主机网络资源
host模式只适用于对网络性能要求高的情况, 不过容易造成端口冲突, 因为每个容器用的都是宿主机的ip, ip是相同的, 而如果容器使用的端口都是相同的,比如httpd和nginx, 那么就会冲突

1.4 container模式

containter模式要求至少启动两个容器
容器a先启动, 可以指定网络模式, 而b容器启动时, 需要指定引用a容器的网络模式
因此, b容器会使用a容器的网络资源, 包括ip和端口号

1.5 自定义网络模式

自定义网络模式本质还是四种网络模式, 只不过是自定义了网络资源, 比如ip网段, 网关地址
自定义网络模式, 只支持桥接网络模式, 因为none模式是没有网络的, 不支持自定义网络, 无法创建
而host模式限制每个宿主机只能有一个host实例, 安装docker后, 宿主机本身就会有一个host实例
自定义网络的优势是, 基于bridge模式创建, 那么同一个网段内的不同容器可以使用容器名称进行互联, 无需额外用--link配置
而且, 即使某个容器重新创建,  只要被加入到了对方的自定义网段内, 就可以恢复通信, 其他的容器无需重新创建

2. 同一个宿主机内, 同一网段不同的容器如何互访

不同的容器的网卡都是桥接在docker0上的, 因此可以互访, 无需配置

3. 同一个宿主机内, 不同网络的容器如何互访

默认情况下, 同一宿主机内不同的网段的容器默认是不能互访的
因为, 在宿主机上的filter表的FORWARD链上进行了设置, 只允许每个网段内的容器访问内部资源, 不能出去
如果想要实现同一个宿主机内不同网络的容器互访, 可以修改防火墙配置, 添加允许规则 
或者使用docker network connect, 把每个容器加到对方所在的桥接网络, 这样在容器内部会生成一个新得虚拟网卡, 该网卡拥有对方容器网段的ip地址, 这样就可以实现通信了

4. 不同宿主机内的容器, 如何互访

默认情况下, 不同宿主机内的容器是不能互访的
如果不同宿主机内的容器是相同网段, 不同的ip, 可以直接把宿主机的eth0桥接在docker0上, 这样两端就相当于完全串联在了一起, 因为是同一个网络所以是可以互联的. 这时宿主机本身是互通的, 因为在同一个网络. 该方法一般不会使用, 因为对容器内部的网段有要求, 必须是同一个网段
不同宿主机的容器处在不同的网段如何互访
默认不能互访是因为宿主机上没有去往对方宿主机内部容器网络的路由, 同时在宿主机的filter表FORWARD链也有默认DROP规则
想要实现互联, 需要在每个宿主机上添加去往对方容器网络的静态路由, 网关指向对方宿主机的eth0
同时要在自己的防火墙fiter的FORWARD链追加一条允许所有来自宿主机所在网段的流量
开源的解决方案是Open vSwitch, 可以利用GRE隧道技术, 实现容器跨宿主机, 跨互联网互访
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容