Docker是应用容器引擎,它利用虚拟化技术打包应用及其依赖。
Docker或者说虚拟化技术的出现,主要是为了解决应用能够快速构建、实施的问题。
说白了,Docker就是服务于应用的。
Docker是服务于应用的,Docker网络就是解决容器中应用的网络通信问题,让容器中的应用就像在一台独立的主机上运行一般。
所有应用共享Linux系统网络协议栈,
socket 中存储了特定的四元组: 源ip+port,目的ip+port;
Docker容器的本质是 共享内核,资源隔离、资源限制、文件系统rootfs
同一台宿主机上的Docker容器共享内核中传输 层和网络层的处理流程以及设备驱动,共享硬件资源,但是socket套接字和虚拟网络设备相互独立,需要进行隔离和限制。
Docker网络的实现主要就是如何在共享内核的基础上,实现socket的隔离,虚拟设备的隔离和通信.、
Docker的网络实现是站在巨人的肩膀上的,Docker主要是利用的操作系统的虚拟化技术,来实现不同容器的网络隔离和通信。
Linux网络协议栈中,IP层不与物理网络设备(网络驱动)直接通信,而是和抽象的网络接口(lo、eth0、eth1等)交互。这为Docker不同容器中能够模拟网络环境提供的基础。
Docker是轻量级的虚拟化,他和虚拟机的一个主要区别是不同的Docker容器共享Linux内核。
共享内核就会存在资源可见性的问题。为此,Linux支持为不同资源设置不同的namespace,不同namespace的资源相互隔离、相互不可见。
namespace其实不单单用于Linux的网络模块(network)。Linux 支持一下六种类型的命名空间:Cgroup、IPC、Network、Mount、PID、Time、User、UTS。 这六种类型都是Docker虚拟化实现资源隔离的基础。
有了namespace的概念,不同容器虚拟出独立的网络环境就变为可行。(比如我们可以在不同的容器中创建网络接口eth0.
namespace用于实现网络资源的隔离,但是Docker容器与宿主机经常需要进行通信,这就需要Linux系统中veth-pair技术的支持。
veth是虚拟网络设备接口,它总是成对出现,用于连通两个namespace。从其中一个端口发出的数据包,可以直接出现在与它对应的另一个端口上。
veth-pair解决了不同命名空间两两通信的问题(容器与容器、容器与宿主机),但是一台宿主机上可以启动大量的容器,这些容器的数据包需要汇聚到同一个网络接口才能与宿主机以外的设备通信。如果仅仅是基于veth-pair来进行数据转发配置,就过于繁琐。
Linux网络内核引入网桥bridge来实现多个网络接口之间的通信,可以将一台机器上的若干接口连通起来。在OSI网络模型中,网桥属于数据链路层。
Linux网络内核通过路由表来指定数据包的转发路径。
一台Linux主机里可能有多个虚拟网络接口、多个物理网络设备,在TCP/IP协议中,IP数据包里包含了目的地址,但是并不知道如何到达目标地址。路由表则用于指示要抵达目的地址,数据包下一条应该先到哪里去。
路由表示例:
[root@VM_16_35_centos ~]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 172.16.16.1 0.0.0.0 UG 0 0 0 eth1
169.254.0.0 0.0.0.0 255.255.0.0 U 1002 0 0 eth1
172.16.16.0 0.0.0.0 255.255.240.0 U 0 0 0 eth1
对Linux的虚拟化支持有一定了解后,我们就可以来看下Docker的网络实现了。
这里我们主要介绍下bridge网络模式。
在Docker-Daemon启动是会在宿主机创建一个bridge:docker0.
[root@VM_52_29_centos ~]# ip link show
1: lo: mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth1: mtu 1500 qdisc mq state UP mode DEFAULT qlen 1000
link/ether 52:54:00:bb:18:70 brd ff:ff:ff:ff:ff:ff
3: br-703d0323a265: mtu 1500 qdisc noqueue state DOWN mode DEFAULT
link/ether 02:42:80:e4:57:84 brd ff:ff:ff:ff:ff:ff
4: docker0: mtu 1500 qdisc noqueue state UP mode DEFAULT
link/ether 02:42:40:84:e0:47 brd ff:ff:ff:ff:ff:ff
[root@VM_52_29_centos ~]# ifconfig docker0
docker0: flags=4163 mtu 1500
inet 192.168.10.1 netmask 255.255.255.0 broadcast 192.168.10.255
ether 02:42:40:84:e0:47 txqueuelen 0 (Ethernet)
RX packets 12788 bytes 588170 (574.3 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 42200 bytes 59457226 (56.7 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
[root@VM_52_29_centos ~]#
启动容器时,Docker会为容器新增一个network-namespace,并创建veth-pair与宿主机相连。
[root@VM_52_29_centos ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fea48be1f2d1 alpine:3.15.4 "/bin/sh" 9 minutes ago Up 9 minutes friendly_tereshkova
fd21e950935d mysql:5.7 "/bin/bash" 2 years ago Up 8 weeks tencenthealth
此处启动了两个容器。
查看当前的网络接口:
[root@VM_52_29_centos ~]# ip link show
1: lo: mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth1: mtu 1500 qdisc mq state UP mode DEFAULT qlen 1000
link/ether 52:54:00:bb:18:70 brd ff:ff:ff:ff:ff:ff
3: br-703d0323a265: mtu 1500 qdisc noqueue state DOWN mode DEFAULT
link/ether 02:42:80:e4:57:84 brd ff:ff:ff:ff:ff:ff
4: docker0: mtu 1500 qdisc noqueue state UP mode DEFAULT
link/ether 02:42:40:84:e0:47 brd ff:ff:ff:ff:ff:ff
6: vethf077bca: mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT
link/ether a2:72:5a:9a:24:60 brd ff:ff:ff:ff:ff:ff
226: vethbf84100: mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT
link/ether 36:bf:28:65:73:86 brd ff:ff:ff:ff:ff:ff
可以看到新增了两个veth接口vethf077bca
, vethbf84100
, 他们分别与两个容器中的eth0形成veth-pair。
再看网桥的连接
[root@VM_52_29_centos ~]# bridge link
6: vethf077bca state UP : mtu 1500 master docker0 state forwarding priority 32 cost 2
226: vethbf84100 state UP : mtu 1500 master docker0 state forwarding priority 32 cost 2
可以看到两个veth-pair在宿主机这一端都和docker0关联。
bridge、namespace、虚拟网络接口、veth-pair都准备好后,流量如何转发呢,这就需要用到路由表了。
在分析路由表前,我们需要先看下各个虚拟网络接口关联的IP。
宿主机上的网络接口:
[root@VM_52_29_centos ~]# ifconfig
br-703d0323a265: flags=4099 mtu 1500
inet 172.25.0.1 netmask 255.255.0.0 broadcast 172.25.255.255
ether 02:42:80:e4:57:84 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
docker0: flags=4163 mtu 1500
inet 192.168.10.1 netmask 255.255.255.0 broadcast 192.168.10.255
ether 02:42:40:84:e0:47 txqueuelen 0 (Ethernet)
RX packets 12788 bytes 588170 (574.3 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 42200 bytes 59457226 (56.7 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
eth1: flags=4163 mtu 1500
inet 9.134.52.29 netmask 255.255.252.0 broadcast 9.134.55.255
ether 52:54:00:bb:18:70 txqueuelen 1000 (Ethernet)
RX packets 22843799 bytes 4344974378 (4.0 GiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 23575652 bytes 5537180006 (5.1 GiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73 mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
loop txqueuelen 0 (Local Loopback)
RX packets 4483879 bytes 3456236895 (3.2 GiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 4483879 bytes 3456236895 (3.2 GiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
vethbf84100: flags=4163 mtu 1500
ether 36:bf:28:65:73:86 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
vethf077bca: flags=4163 mtu 1500
ether a2:72:5a:9a:24:60 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 92 bytes 4094 (3.9 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
可以看到,宿主机自身的网络接口lo、eth0分别绑定了IP:127.0.0.1
, 9.134.52.29
.
网桥Docker0也绑定了IP: 192.168.10.1。
而veth设备vethbf84100、vethf077bca并没有IP,因为他们并不与外界直接通信,而是通过Docker0来转发流量。Docker0与vethbf84100、vethf077bca流量互通。
容器中的网络接口:
[root@VM_52_29_centos ~]# docker exec -it fd21e950935d /bin/bash
eth1: error fetching interface information: Device not found
[root@fd21e950935d /]# ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:C0:A8:0A:02
inet addr:192.168.10.2 Bcast:192.168.10.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:92 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:4094 (3.9 KiB) 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:0
RX bytes:0 (0.0 b) TX bytes:0 (0.0 b)
[root@fd21e950935d /]#
可以看到容器中的eth0与宿主机的docker0在同一个网段。
接下来看下路由表
宿主机上的路由表:
[root@VM_52_29_centos ~]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 9.134.52.1 0.0.0.0 UG 0 0 0 eth1
9.0.0.0 9.134.52.1 255.0.0.0 UG 0 0 0 eth1
9.134.52.0 0.0.0.0 255.255.252.0 U 0 0 0 eth1
10.0.0.0 9.134.52.1 255.0.0.0 UG 0 0 0 eth1
100.64.0.0 9.134.52.1 255.192.0.0 UG 0 0 0 eth1
169.254.0.0 0.0.0.0 255.255.0.0 U 1002 0 0 eth1
172.16.0.0 9.134.52.1 255.240.0.0 UG 0 0 0 eth1
172.25.0.0 0.0.0.0 255.255.0.0 U 0 0 0 br-703d0323a265
192.168.0.0 9.134.52.1 255.255.0.0 UG 0 0 0 eth1
192.168.10.0 0.0.0.0 255.255.255.0 U 0 0 0 docker0
[root@VM_52_29_centos ~]#
当docker0上的包,目的地址不是192.168.10.0/24时,表示目的地址在相同网段,不需要经过网关,Flags=U(不含G)。
其他网络接口上的包按普通流程处理。
在看下容器内的路由表:
[root@fd21e950935d /]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.10.1 0.0.0.0 UG 0 0 0 eth0
192.168.10.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
[root@fd21e950935d /]#
当目的地址为192.168.10.0/24时,目的地址在相同网段,不需要经过网关。Flags=U(不含G)
当目的地址为其他地址时,流量转发需要经过网关,Flags=UG。
Networking overview
理解 Linux 网络栈(1):Linux 网络协议栈简单总结
linux 路由表设置 之 route 指令详解