docker 网络
环境信息
- Centos 7.9.2009
- docker-ce-19.03.15
Docker 网络模式
Bridge 模式
bridge 模式是 docker 的默认网络模式,不使用 --network
参数,就是 bridge 模式。
当 Docker 进程启动时,会在主机上创建一个名为 docker0 的虚拟网桥,默认主机上启动的 Docker 容器会连接到这个虚拟网桥上。
容器启动时,docker 会从 docker0 网桥的子网中分配一个 IP 地址给容器中的网卡。大体流程为在主机上创建一个 `veth pair`,Docker 将 veth pair
的一端放在容器中,命名为 eth0
并配置 IP,网关,路由等信息,将 veth pair
的另一端加入 docker0 网桥。
通过这种方式,主机可以跟容器通信,容器之间也可以相互通信。
Host 模式
如果启动容器的时候使用 host 模式,那么这个容器将不会获得一个独立的 Network Namespace,而是和宿主机一样在 Root Network Namespace,容器中看到的网络方面的信息和宿主机一样,容器使用的网络资源在整个 Root Network Namespace 不能出现冲突。容器将不会虚拟出自己的网卡,配置自己的 IP 等,而是使用宿主机的 IP 和端口,主机名也是使用宿主机的。但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。
host 模式下的容器可以看到宿主机上的所有网卡信息,可以直接使用宿主机 IP 或主机名与外界通信,无需额外的 NAT,也无需通过 Linux bridge 进行转发或者数据包的封装,可以访问主机上的其他任一容器。
使用如下命令参数启动 host 网络模式的容器
docker run --network host --name test1 -p 80:80 -d -it centos:centos7.9.2009 |
host 模式的容器,没有自己的 network namespace,在 root network namespace 中。进入测试容器 test1
,查看网卡、 IP 信息及端口、主机名信息,会看到和宿主机一样的信息。
ip link |
host 模式的缺点
- 容器没有自己的 network namespace ,网络和宿主机或其他使用 host 模式的容器未隔离,容易出现资源冲突,比如同一个宿主机上,使用 host 模式的容器中启动的端口不能相同。
None 模式
使用 none 模式,Docker 容器拥有自己的 Network Namespace,但是,系统并不为 Docker 容器进行任何网络配置。也就是说,这个 Docker 容器没有网卡(lo 回环网卡除外)、IP、路由等信息。需要我们自己为 Docker 容器添加网卡、配置 IP 等。
参考以下命令创建 none
模式的容器
docker run --network none --name test-none -p 82:80 -d -it centos7:my |
容器创建后,进入容器中,查看网卡和 IP 等信息,容器中默认只存在 lo
网卡,不存在其他网卡
ip add |
以下操作演示手动为容器配置网络
创建
veth pair
ip link add veth0 type veth peer name veth0_p
将
veth pair
的一端veth0
放入 docker 默认的网桥docker0
,另一端veth0_p
放入容器中首先使用命令
docker inspect test-none | grep "Pid"
找到容器对应的 PID,此处为 84040,根据此 PID 将 veth 的一端放入容器的 network namespace 中ip link set dev veth0 master docker0
ip link set dev veth0 up
ip link set veth0_p netns 84040在宿主机上面检查
veth0
,确定其已经加入网桥docker0
,并且veth0_p
已不在 root network namespace 中ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
link/ether 00:0c:29:e7:c0:27 brd ff:ff:ff:ff:ff:ff
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default
link/ether 02:42:f2:1b:dc:ea brd ff:ff:ff:ff:ff:ff
12: veth0@if11: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue master docker0 state LOWERLAYERDOWN mode DEFAULT group default qlen 1000
link/ether 16:7f:98:d8:9d:dc brd ff:ff:ff:ff:ff:ff link-netnsid 0重新进入容器,检查网卡信息,可以看到容器中已经有了网卡
veth0_p
,状态为DOWN
ip -d link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 promiscuity 0 addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
11: veth0_p@if12: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether be:f1:94:9f:b8:c9 brd ff:ff:ff:ff:ff:ff link-netnsid 0 promiscuity 0
veth addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535为容器中的网卡配置 IP 及网关等信息
为了能在宿主机对容器的 network namespace 进行操作,首先需要将容器的 network namespace 暴露出来,之后可以在宿主机通过 network namespace 名称(此处为 84040,可以自定义)操作 network namespace 。Linux network namespace 参考
ln -s /proc/84040/ns/net /var/run/netns/84040
ip netns ls
84040 (id: 0)通过 network namespace 名称(此处为 84040)配置容器中网卡的 IP 地址信息
ip netns exec 84040 ip link set dev veth0_p name eth0
ip netns exec 84040 ip link set dev eth0 up
ip netns exec 84040 ip add add 172.17.0.10/16 dev eth0
ip netns exec 84040 ip route add default via 172.17.0.1进入容器检查网络信息
ip add
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
15: eth0@if16: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 7e:36:b3:20:a1:8c brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.10/16 scope global eth0
valid_lft forever preferred_lft forever
ip route show
default via 172.17.0.1 dev eth0
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.10进入容器测试网络连接
ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=127 time=37.4 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=127 time=37.0 ms
^C
--- 8.8.8.8 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1000ms
rtt min/avg/max/mdev = 37.047/37.234/37.422/0.269 ms
Container 模式
在创建容器时通过参数 --network container:已运行的容器名称|ID
指定,处于这个模式下的 Docker 容器会共享一个网络栈,这样两个容器之间可以使用 localhost 高效快速通信。
Container 网络模式即新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围、主机名等。同样两个容器除了网络方面相同之外,其他的如文件系统、进程列表等还是隔离的。
Kubernetes 的 POD 网络采用的就是 Docker 的 container 模式网络。
Macvlan 模式
linux macvlan 网卡虚拟化技术ipvlan 模式
IPVLAN/MACVLAN 实现 Docker 和物理局域网络真正互联互通
容器网络组网类型
常见的非主机网络(host network)的容器组网类型有 L2 overlay、L3 overlay、L2 underlay、L3 underlay。
overlay 网络
overlay 网络,也称为隧道网络或覆盖网络。
overlay 网络是在传统网络(数据链路层、网络层)上虚拟出一个虚拟网络,承载虚拟网络的底层传统网络不再需要进行任何适配和变更。在容器的世界了,底层物理网络只需要承载主机的网络通信,虚拟网络只承载容器网络通信。
overlay 网络的任何协议都要求发送方对报文进行封装(在虚拟网络报文的头部添加底层物理网络的地址信息,以将报文传输到容器所在的节点),接收方对报文进行解封装,使用 UDP 进行封装时,性能损失在 50% 以上,使用 VXLAN 也会有 20%-30% 的损耗。
overlay 网络最大的优点是适用于几乎所有的网络基础架构,唯一要求是主机 IP 互通,问题是随着规模的增长,复杂度会随之增加,封包和解封包性能损坏较大且难于定位问题。
L2 overlay
传统的 L2 网络,通信双方在同一个逻辑网段内,如 172.17.1.10/16 和 172.17.2.10/16
L2 overlay 是构建在底层物理网络之上的 L2 网络,相较于传统的 L2 网络,L2 overlay 是个 大二层
(可以跨越多个数据中心,即可以跨 L3 underlay 进行 L2 通信)。
VXLAN 就是 L2 overlay 网络的典型实现,其通过在 UDP 包中封装原始的 L2 报文,实现了容器的跨主机通信。
L2 overlay 网络的容器可以在任意的宿主机间迁移而不改变其 IP 地址,这种特性使得构建在大二层 overlay 网络上的容器在动态迁移时具有很高的灵活性。
L3 overlay
L2 overlay 类似于 L2 overlay,但会在节点上增加个网关,每个节点上的容器都在同一个子网内,可以直接进行二层通信。跨接点间的通信只能通过 L3,都会经过网关转发,性能相比于 L2 overlay 弱。优点是跨节点通信的容器可以在不同的网段。
flannel 的 UDP 模式采用的就是 L3 overlay 模式。
underlay 网络
underlay 网络通常指底层网络,即传统的网络组网,主要要来区别于 overlay 网络。
L2 underlay
指传统的二层网络。 ipvlan 的 L2 模式属于 L2 underlay 类型的网络
L3 underlay
指传统的三层网络。
ipvlan 的 L3 模式、flannel 的 host-gw 模式和 Calico 的 BGP 组网都是 L3 underlay 类型的网络
容器互联
为了使容器互联,新版本建议将容器加入自定义的 Docker 网络
来连接多个容器,而不是使用 --link
参数。
从 Docker 1.10 版本开始,docker daemon 实现了一个内嵌的 DNS server,使容器可以直接通过容器名称通信。方法很简单,只要在创建容器时使用 --name
为容器命名即可。但是使用 Docker DNS 有个限制:只能在 user-defined 网络中使用。也就是说,默认的 bridge 网络是无法使用 DNS 的,所以我们就需要自定义网络。
先创建一个新的 Docker 网络
。
docker network create -d bridge my-net |
-d
参数指定 Docker 网络类型,可选 bridge
overlay
查看 Docker network
docker network ls |
新建容器并连接到刚刚新建的 Docker network
docker run -d -it --name c1 --network my-net centos |
以上命令运行了 2 个 centos
最新版本的容器,并连接到了刚刚创建的网络: --network my-net
登录到容器 c1
的终端,并 ping
容器 c2
docker exec -it c1 bash |
可以看到,容器 c1
可以直接使用容器名 c2
,来识别容器 c2
,同理,容器 c2
也可以使用同样的方式识别到容器 c1
,连接到同一个 Docker network
的容器 c1
, c2
可以互相连接
使用以下命令,可以查看 Docker network
的详细信息,包括 ip 网段,ip 分配信息等
docker network inspect my-net |
如果容器启动时使用了自定义的
Docker network
,并且启动过程中挂载了宿主机的/etc/resolv.conf
,则容器启动后无法再使用容器名和其他容器互联,比如使用以下命令启动容器c3
,c3
无法使用容器名和c1
,c2
互联,但是c1
,c2
可以使用容器名c3
连接到容器c3
docker run -d -ti --network my-net -v /etc/resolv.conf:/etc/resolv.conf --name c3 centos以上示例中,如果登录
c1
终端后,修改/etc/resolv.conf
文件,再次使用c2
,会无法连接c2
容器
如果容器启动时,需要为容器指定域名 ip 映射关系,可以使用选项 --add-host host:ip
docker run --network my-net --add-host db:172.18.0.3 --name c3 centos |
容器运行过程中,可以直接修改容器内的 /etc/hosts
文件
Docker 网络相关问题
定位 Docker 容器中的网卡和宿主机上面的 veth 的 pair 关系
在经典容器组网中,主要是使用 veth
+ bridge
的模式,容器中的 eth0
实际上和宿主机上面的某个 veth
是成对(pair)关系,要查看容器中的网卡和宿主机上面的 veth
网卡的成对关系,可以参考以下方法
方法 1
- 在目标容器中查看
$ cat /sys/class/net/eth0/iflink
60 - 在宿主机上遍历
/sys/class/net/
下面的全部目录,查看子目录中的ifindex
文件的内容,找出和容器中/sys/class/net/eth0/iflink
的值一样的veth
的名称,这样就找到了容器和主机的veth pair
的关系本示例中,宿主机上的$ cat /sys/class/net/vethc41ba34/ifindex
60vethc41ba34
和容器中的网卡是veth pair
方法 2
- 目标容器中查看从上面的命令可以看到
ip link show eth0
59: eth0@if60: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
link/ether 02:42:ac:16:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 059: eth0@if60
,其中59
是eth0
接口的 index,60
是和它成对的veth
的 index。 - 在 host 上面执行下面的命令,可以看到对应
60
的veth
网卡是哪一个ip link show | grep 60
60: vethc41ba34@if59: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-9d2a9fc0ff85 state UP mode DEFAULT group default
方法 3
通过 ethtool -S
命令列出 veth pair
对端的网卡 index
- 目标容器中执行
ethtool -S eth0
NIC statistics:
peer_ifindex: 60 - 在宿主机上面查找 index 为 60 的
veth
网卡是哪一个ip link show | grep 60
60: vethc41ba34@if59: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-9d2a9fc0ff85 state UP mode DEFAULT group default