1. 网络概述

Docker 容器和服务如此强大的原因之一是可以将它们连接到一起,或将它们连接到其他的非 Docker 工作负载上。Docker 容器甚至不需要知道它们部署在 Docker 上,或者它们的对端是否也是 Docker 工作负载。无论你的 Docker 主机是运行在 Linux 还是 Windows 又或者两者都有,你都可以使用 Docker 以平台无关的方式管理它们。

Docker 的网络子系统是使用驱动程序插入的。默认情况下存在多个驱动程序,它们一起提供核心网络功能,可以使用 docker network ls 命令查看默认存在的驱动程序。

[root@docker ~]# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
ef0a155fdde4   bridge    bridge    local
c4fa50b15f43   host      host      local
6a2c541b2e82   none      null      local
[root@docker ~]#

1.1. Docker 常用的网络类型

1.1.1. Bridge

默认的网络驱动程序,如果你创建容器时不指定网络驱动程序,这就是你正在创建容器的网络类型。当你的应用程序在需要通信的独立容器中运行时,通常会使用桥接网络。当启动 docker.service 服务之后,Docker 会默认创建一个名为 docker0 的虚拟网桥,所有没有指定网络驱动程序的容器都会被加入到这个网桥中。虚拟网桥就像一个物理交换机,这样网桥内的容器都连接到了一个二层网络中。Docker 会在宿主机上创建一对 veth pair 虚拟网卡设备,这一对 veth pair 虚拟网卡设备的两端分别对应容器内部的 eth0 网卡和宿主机上类似 vethxxxxxx 的虚拟网卡,可以使用 brctl show 命令查看宿主机上已经加入到 docker0 网桥下的虚拟网卡设备。虚拟网卡设备连接到 docker0 网桥后, docker0 网桥会从子网分配一个 ip 给容器,并且设置 docker0 网桥的 ip 地址为容器的默认网关。

查看 docker0 网桥上的接口信息。

[root@docker ~]# brctl show
bridge name    bridge id        STP enabled    interfaces
docker0        8000.02429451522b    no        veth00b57b0
                            veth0bf3f4c
                            veth59d431c
[root@docker ~]#

创建容器时不指定网络驱动程序,默认就是 bridge 网络类型,但是我们还可以创建自定义的 bridge 网络类型。

[root@docker ~]# docker network create app
0a4fd2947aa9dcf12786709bcce856421b33c7b2fdcc6175691be4d42cec0d07
[root@docker ~]# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
0a4fd2947aa9   app       bridge    local
ef0a155fdde4   bridge    bridge    local
c4fa50b15f43   host      host      local
6a2c541b2e82   none      null      local

创建两个容器并把它们加入到自定义的 app 网络,-d 参数可以指定创建 bridge 或者 overlay 网络类型。

[root@docker ~]# docker container run -itd --name "app1" --network app busybox
f46be3d1f5ead467e2f7e8df05f72d73e6d3a3b3aaa2d34a49a5879a99fa53d1
[root@docker ~]# docker container run -itd --name "app2" --network app busybox
7c2b79e36244d79ad8e184d916ddba23fbb026b1bdc6e3c333d7110f705695de
[root@docker ~]#

随着 Docker 网络的完善,建议大家将容器加入自定义的网络来进行连接。通过在容器内使用 ping 命令来证明容器网络的互通。

[root@docker ~]# docker ps -a
CONTAINER ID   IMAGE     COMMAND   CREATED         STATUS         PORTS     NAMES
7c2b79e36244   busybox   "sh"      4 minutes ago   Up 4 minutes             app2
f46be3d1f5ea   busybox   "sh"      5 minutes ago   Up 5 minutes             app1
[root@docker ~]# docker exec -it app1 sh
/ # ping -c 3 app2
PING app2 (172.19.0.3): 56 data bytes
64 bytes from 172.19.0.3: seq=0 ttl=64 time=0.071 ms
64 bytes from 172.19.0.3: seq=1 ttl=64 time=0.111 ms
64 bytes from 172.19.0.3: seq=2 ttl=64 time=0.107 ms

--- app2 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.071/0.096/0.111 ms
/ #

1.1.2. Host

如果容器使用 host 网络类型,则容器的网络堆栈不会与 Docker 主机隔离(容器共享主机的网络命名空间)。并且容器不会分配自己的 ip 地址,而是使用宿主机的 ip 和端口,容器的文件系统和进程等还是和宿主机隔离的。

[info] 说明

由于容器使用 host 网络模式没有主机的 ip 地址,端口映射不会生效,-p 和 -P 参数都会被忽略并且产生一个错误警告:WARNING: Published ports are discarded when using host network mode

创建一个 nginx 容器,可以看到宿主机上的 80 端口被占用了。

[root@docker ~]# docker container run -itd --name "nginx" --network=host nginx:1.20
1a13399898af4d66758a0ec10f4f8e3d89cd21b15186c6cea9c161cca0d82b87
[root@docker ~]# ps -ef | grep nginx
root      260249  260228  1 14:38 pts/0    00:00:00 nginx: master process nginx -g daemon off;
systemd+  260308  260249  0 14:38 pts/0    00:00:00 nginx: worker process
systemd+  260309  260249  0 14:38 pts/0    00:00:00 nginx: worker process
root      260313  112281  0 14:38 pts/1    00:00:00 grep --color=auto nginx
[root@docker ~]# netstat -lntup | grep 80
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      260249/nginx: maste
tcp6       0      0 :::80                   :::*                    LISTEN      260249/nginx: maste
[root@docker ~]#

使用 ps 命令可以看到 nginx 进程的父进程是 containerd-shim,这也证实了 nginx 进程是容器内的。

[root@docker ~]# ps -axf | grep containerd-shim -A 1
 260701 pts/1    S+     0:00      \_ grep --color=auto containerd-shim -A 1
 260228 ?        Sl     0:00 /usr/bin/containerd-shim-runc-v2 -namespace moby -id 1a13399898af4d66758a0ec10f4f8e3d89cd21b15186c6cea9c161cca0d82b87 -address /run/containerd/containerd.sock
 260249 pts/0    Ss+    0:00  \_ nginx: master process nginx -g daemon off;
[root@docker ~]#

进入容器内部查看网卡信息会看到宿主机的网卡,这是因为容器共享了宿主机网络堆栈。

[root@docker ~]# docker container run -itd --name "busybox" --network=host busybox
ad4634aa3d4d52b7ccd086359fe20e0fe5abae5527ac6a78064a0c308dde8f09
[root@docker ~]# docker exec -it busybox /bin/sh
/ # 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: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel qlen 1000
    link/ether 00:0c:29:f9:6d:2f brd ff:ff:ff:ff:ff:ff
    inet 10.10.110.31/24 brd 10.10.110.255 scope global ens33
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fef9:6d2f/64 scope link
       valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue
    link/ether 02:42:94:51:52:2b 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:94ff:fe51:522b/64 scope link
       valid_lft forever preferred_lft forever
15: br-0a4fd2947aa9: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue
    link/ether 02:42:f8:6b:79:d0 brd ff:ff:ff:ff:ff:ff
    inet 172.19.0.1/16 brd 172.19.255.255 scope global br-0a4fd2947aa9
       valid_lft forever preferred_lft forever
    inet6 fe80::42:f8ff:fe6b:79d0/64 scope link
       valid_lft forever preferred_lft forever
/ #

1.1.3. Container

创建新容器时指定和一个已存在的容器共享一个网络命名空间,这与宿主机的网络命名空间无关。新容器不会创建自己的网卡和 ip 地址,而是共享已存在容器的网卡、ip、端口等。但是除了网络环境之外,其他的资源例如文件系统和进程等还是隔离的,这就是 container 网络类型。

创建一个 busybox 容器,用于给其他容器共享网络命名空间。

[root@docker ~]# docker container run -itd --name 'busybox' busybox
471accba2c3fef25da6a617417b8c84fb38c860aa49a4e9287dcbacd111b73f2
[root@docker ~]#

创建新的容器,指定新的容器共享前面创建的 busybox 容器的网络命名空间。

[root@docker ~]# docker container run -itd --network=container:busybox --name 'busybox-1' busybox
a1e6999c17e35a7ea32c9886f3f708985a50a8c1b8b1bc36b790b10a9db98d59
[root@docker ~]# docker container run -itd --network=container:busybox --name 'busybox-2' busybox
867915a468489dd356efbe0c1ea22430df3dd026b74267caa7576b3eec298f67
[root@docker ~]#

共享 busybox 容器网络命名空间的 busybox-1 和 busybox-2 容器,ip 的值为 \,其实容器内的 ip 地址是和 busybox 容器一样的。

[root@docker ~]# docker inspect --format='{{.NetworkSettings.Networks.bridge.IPAddress}}' busybox
172.17.0.2
[root@docker ~]# docker inspect --format='{{.NetworkSettings.Networks.bridge.IPAddress}}' busybox-1
<no value>
[root@docker ~]# docker inspect --format='{{.NetworkSettings.Networks.bridge.IPAddress}}' busybox-2
<no value>
[root@docker ~]#

[root@docker ~]# docker container exec -it busybox ifconfig eth0
eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:02
          inet addr:172.17.0.2  Bcast:172.17.255.255  Mask:255.255.0.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:120 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:8672 (8.4 KiB)  TX bytes:0 (0.0 B)

[root@docker ~]# docker container exec -it busybox-1 ifconfig eth0
eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:02
          inet addr:172.17.0.2  Bcast:172.17.255.255  Mask:255.255.0.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:120 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:8672 (8.4 KiB)  TX bytes:0 (0.0 B)

[root@docker ~]# docker container exec -it busybox-2 ifconfig eth0
eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:02
          inet addr:172.17.0.2  Bcast:172.17.255.255  Mask:255.255.0.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:120 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:8672 (8.4 KiB)  TX bytes:0 (0.0 B)
[root@docker ~]#

1.1.4. None

none 模式会使容器拥有独立的 network namespace,就是不为 Docker 容器进行任何网络配置。容器内部只有 loopback 网络设备,这将网络创建的责任完全交给用户。Docker 开发者可以在这基础上做出更多的网络定制,这种方式可以实现更加灵活复杂的网络。

[root@docker ~]# docker container run -it --network=none --name 'busybox-3' busybox
/ # ifconfig -a
lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  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:1000
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

/ #

1.2. Flannel 实现 Docker 跨主机通信

flannel 是一种基于 overlay 网络的跨主机容器网络解决方案。就是将 TCP 数据包封装在另一种网络包里面进行路由转发和通信,flannel 是 CoreOS 团队针对 Kubernetes 设计的一个网络规划服务,让集群中的不同节点主机创建的容器都具有全集群唯一的虚拟 ip 地址,flannel 使用 go 语言编写。

flannel 为每个 host 分配一个 subnet,容器从这个 subnet 中分配 ip,这些 ip 可以在 host 间路由,容器间无需使用 nat 和端口映射即可实现跨主机通信。每个 subnet 都是从一个更大的 ip 池中划分的,flannel 会在每个主机上运行一个叫 flanneld 的 agent,其职责就是从 ip 池中分配 subnet。etcd 相当于一个数据库,flannel 使用 etcd 存放网络配置和已分配的 subnet、host、ip 等信息。这就是 flannel 的原理。

node software Operating System docker version
10.10.110.31(master) etcd、flannel、docker Ubuntu 20.04.3 LTS 19.03.12
10.10.110.32(slave) flannel、docker Ubuntu 20.04.3 LTS 19.03.12

master 节点安装 etcd

root@docker1:~# apt install -y etcd

root@docker1:~# vim /etc/default/etcd # 修改 master 默认的 127.0.0.1 地址以供 slave 节点访问 etcd
ETCD_LISTEN_CLIENT_URLS="http://10.10.110.8:2379"
ETCD_ADVERTISE_CLIENT_URLS="http://10.10.110.8:2379"

root@docker1:~# systemctl restart etcd
root@docker1:~# netstat -lntup | grep 2379
tcp        0      0 10.10.110.8:2379        0.0.0.0:*               LISTEN      95984/etcd

# 配置 etcd 的子网必须指定使用 V2 版本因为 flannel 目前不支持 etcd V3 版本
ETCDCTL_API=2 etcdctl --endpoints="http://10.10.110.8:2379" set /atomic.io/network/config '{ "Network":"172.17.0.0/16", "Backend": {"Type": "vxlan"}} '

master 节点安装 flannel

root@docker1:~# apt install -y flannel

root@docker1:~# cat > /etc/systemd/system/flanneld.service << EOF
[Unit]
Description=Flanneld overlay address etcd agent
After=network.target
After=network-online.target
Wants=network-online.target
After=etcd.service
Before=docker.service

[Service]
Type=notify
ExecStart=/usr/bin/flannel --etcd-endpoints=http://10.10.110.8:2379 --etcd-prefix=/atomic.io/network/ --iface=ens33
Restart=on-failure

[Install]
WantedBy=multi-user.target
RequiredBy=docker.service
EOF

root@docker1:~# systemctl start flanneld.service

slave 节点安装 flannel

root@docker1:~# apt install -y flannel

root@docker1:~# cat > /etc/systemd/system/flanneld.service << EOF
[Unit]
Description=Flanneld overlay address etcd agent
After=network.target
After=network-online.target
Wants=network-online.target
After=etcd.service
Before=docker.service

[Service]
Type=notify
ExecStart=/usr/bin/flannel --etcd-endpoints=http://10.10.110.8:2379 --etcd-prefix=/atomic.io/network/ --iface=ens33 # slave 也是连接到 master 的 etcd
Restart=on-failure

[Install]
WantedBy=multi-user.target
RequiredBy=docker.service
EOF

root@docker1:~# systemctl start flanneld.service

配置 docker 使用 flannel 网络,master 和 slave 都需要进行配置让 flannel 管理 docker 的网络

vim /lib/systemd/system/docker.service
EnvironmentFile=/run/flannel/docker # 加载这个文件里面的变量,这个文件记录了 flannel 分配的子网信息
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock $DOCKER_NETWORK_OPTIONS # 使用上面文件的变量,启动容器时指定使用 flannel 分配的子网去配置容器的网络

iptables -P FORWARD ACCEPT
systemctl daemon-reload
systemctl restart flanneld.service
systemctl restart docker.service

如果没有 /run/flannel/docker 这个文件,可以使用 mk-docker-opts.sh -k DOCKER_NETWORK_OPTIONS -d /run/flannel/docker 命令生成。

# docker0 网卡的网段变成了 flannel.1 网卡的子网时,就说明配置成功

root@docker1:~# ip a s docker0
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    link/ether 02:42:6d:41:48:2a brd ff:ff:ff:ff:ff:ff
    inet 172.17.19.1/24 brd 172.17.19.255 scope global docker0
       valid_lft forever preferred_lft forever
root@docker1:~# ip a s flannel.1
4: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default
    link/ether 3e:f4:3d:f3:0d:4d brd ff:ff:ff:ff:ff:ff
    inet 172.17.19.0/32 scope global flannel.1
       valid_lft forever preferred_lft forever
    inet6 fe80::3cf4:3dff:fef3:d4d/64 scope link
       valid_lft forever preferred_lft forever
root@docker1:~#

root@docker2:~# ip a s docker0
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    link/ether 02:42:4c:d8:64:b0 brd ff:ff:ff:ff:ff:ff
    inet 172.17.71.1/24 brd 172.17.71.255 scope global docker0
       valid_lft forever preferred_lft forever
root@docker2:~# ip a s flannel.1
4: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default
    link/ether 12:0e:69:a9:8d:09 brd ff:ff:ff:ff:ff:ff
    inet 172.17.71.0/32 scope global flannel.1
       valid_lft forever preferred_lft forever
    inet6 fe80::100e:69ff:fea9:8d09/64 scope link
       valid_lft forever preferred_lft forever
root@docker2:~#

分别在 master 节点和 slave 节点创建容器进行验证。

root@docker1:~# docker run -it busybox sh
/ # ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:13:02
          inet addr:172.17.19.2  Bcast:172.17.19.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1450  Metric:1
          RX packets:8 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:736 (736.0 B)  TX bytes:0 (0.0 B)

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  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:1000
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

/ # ping -c3 172.17.71.2
PING 172.17.71.2 (172.17.71.2): 56 data bytes
64 bytes from 172.17.71.2: seq=0 ttl=62 time=0.720 ms
64 bytes from 172.17.71.2: seq=1 ttl=62 time=0.810 ms
64 bytes from 172.17.71.2: seq=2 ttl=62 time=0.731 ms

--- 172.17.71.2 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.720/0.753/0.810 ms
/ #

root@docker2:~# docker run -it busybox sh
/ # ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:47:02
          inet addr:172.17.71.2  Bcast:172.17.71.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1450  Metric:1
          RX packets:9 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:806 (806.0 B)  TX bytes:0 (0.0 B)

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  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:1000
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

/ # ping -c3 172.17.19.2
PING 172.17.19.2 (172.17.19.2): 56 data bytes
64 bytes from 172.17.19.2: seq=0 ttl=62 time=4.305 ms
64 bytes from 172.17.19.2: seq=1 ttl=62 time=0.858 ms
64 bytes from 172.17.19.2: seq=2 ttl=62 time=0.798 ms

--- 172.17.19.2 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.798/1.987/4.305 ms
/ #
Copyright © 荒原饮露 2019 all right reserved,powered by Gitbook该文件修订时间: 2022-03-28 11:17:11