在2年之前对容器化技术之Linux Namespace 的相关知识进行了学习和梳理,了解了Linux内核如何支持虚拟化的相关技术特性;本文开始对Docker是如何进行容器网络管理的进行阐述;阐述的内容路径主要参考:Docker 官方的Network 相关的知识图谱,对容器网络进行深入浅出的解析。
通过这篇文章,希望能够对容器和主机之间是如何进行网络管理的,同一个主机的容器之间,跨主机的容器之间是如何通过现有的underlay网络实现容器层面的互通的。
Docker 网络类型 容器网络是指容器之间或与Docker工作负载之外的其他工作负载进行连接和通信的能力。
容器默认启用网络能力,它们可以进行出站连接,容器不知道自己连接到哪种网络,也不知道它们的对等方是否也是Docker工作负载。启用网络驱动的容器能看到一个具有IP地址、网关、路由表、DNS服务和其他网络详细信息的网络接口。
Docker容器网络支持以下几种默认的网络驱动 类型:
桥接网络,Docker的默认网络驱动程序,它在Docker主机内创建一个内部网络,允许容器之间以及与主机机器进行通信。桥接网络从私有子网中为容器分配IP地址,并使用Docker内置的DHCP服务器来管理IP分配。
主机网络驱动程序,Docker容器共享主机机器的网络命名空间。这意味着容器可以直接访问主机的网络接口和端口,Container和主机共享网络堆栈。当应用程序需要直接访问主机的网络资源或性能至关重要时,此驱动程序非常有用。
无网络驱动网络,将容器与任何网络访问隔离开来,包括主机和其他容器。当容器不需要网络访问或出于安全原因需要网络隔离时,此驱动程序非常有用。
在不同主机上的Docker守护程序之间能够进行通信。这在多主机环境中特别有用,其中容器需要在不同物理或虚拟机之间进行通信。overlay
网络使用VXLAN(虚拟可扩展局域网)技术封装和路由主机之间的网络流量。
IPvlan网络对IPv4和IPv6地址提供了完全控制。它们允许您创建子网并将IP地址直接分配给容器,与默认的桥接网络相比提供了更多的灵活性和控制。根据您的网络需求,IPvlan网络可以配置为L2(第二层)或L3(第三层)模式。
Macvlan驱动程序为每个容器分配唯一的MAC地址,使它们看起来像是网络上的独立物理设备。当应用程序需要直接访问物理网络或需要绕过Docker默认网络堆栈的场景时,此驱动程序非常有用。
Linux网络类型 在介绍Docker网络类型之前,我们先看一下Linux本身支持的网络设备类型,Linux为了满足虚拟化,网络隔离,流量控制等各种复杂不同场景的网络需求,引入了各种不同的网络技术,除了我们常见的ethernet
和loopback
两种网卡,我们可以通过ip link
工具创建和管理如下Linux已经支持的网络设备类型:
bridge (以太网桥设备)
内核引入时间:大约在Linux 2.2内核版本中引入(1999年)。
功能:bridge
设备充当以太网桥,用于将多个网络接口连接在一起,使它们像在同一个局域网中一样工作。
实际应用:广泛应用于虚拟化环境,如KVM、QEMU、VirtualBox等,用于将虚拟机或容器的虚拟网卡桥接到宿主机的物理网卡上,实现不同网络间的通信。
can (控制器局域网络接口)
内核引入时间:大约在Linux 2.4内核版本中引入(2001年)。
功能:用于支持控制器局域网(CAN),这是一种专为汽车、工业和嵌入式系统设计的通信协议。
实际应用:主要用于汽车电子系统(如汽车的车载网络),工业自动化(如PLC控制系统)等领域。
dummy (虚拟网络接口)
内核引入时间:大约在Linux 2.0内核版本中引入(1996年)。
功能:不进行实际的数据传输,主要用于网络配置和测试。
实际应用:用于模拟网络设备,进行网络配置和测试,一些网络服务的测试环境中也会使用。
ifb (中间功能块设备)
内核引入时间:大约在Linux 2.6内核版本中引入(2005年)。
功能:用于网络流量的转发和控制,通常用于流量整形和流量控制。
实际应用:在需要对网络流量进行监控和控制的场景中使用,如流量整形和网络性能测试。
ipoib (基于Infiniband的IP设备)
内核引入时间:大约在Linux 2.6内核版本中引入(2005年)。
功能:在Infiniband网络上传输IP数据包,实现高带宽和低延迟的网络通信。
实际应用:用于高性能计算(HPC)和数据中心网络,特别是在需要低延迟和高带宽的应用中。
macvlan (基于链路层地址的虚拟接口)
内核引入时间:大约在Linux 2.6.22内核版本中引入(2007年)。
功能:将一个物理接口分割成多个虚拟接口,每个虚拟接口都有独立的MAC地址。
实际应用:在容器网络中,通过给每个容器分配一个独立的MAC地址,使其可以像独立的设备一样连接到网络。
vcan (虚拟局域CAN接口)
内核引入时间:大约在Linux 3.0内核版本中引入(2011年)。
功能:用于模拟和测试控制器局域网络(CAN)。
实际应用:用于CAN网络的开发和测试,不需要实际的CAN硬件,如汽车电子系统的开发测试环境。
veth (虚拟以太网接口)
内核引入时间:大约在Linux 2.6.24内核版本中引入(2008年)。
功能:成对存在,一个接口连接到一个网络命名空间,另一个接口连接到另一个网络命名空间。
实际应用:用于容器(如Docker、Kubernetes)和虚拟机之间的网络连接,实现网络隔离和通信。
vlan (基于802.1q标签的虚拟局域网接口)
内核引入时间:大约在Linux 2.2内核版本中引入(1999年)。
功能:在同一物理网络上创建多个逻辑网络,通过802.1q VLAN标签区分。
实际应用:用于网络隔离和分段,提高网络安全性和管理效率,广泛应用于企业网络。
vxlan (虚拟扩展局域网)
内核引入时间:大约在Linux 3.7内核版本中引入(2012年)。
功能:在物理网络上创建虚拟网络隧道,实现跨数据中心的虚拟网络连接。
实际应用:用于大规模云计算环境和数据中心的虚拟网络,如OpenStack中的虚拟网络实现。
ip6tnl (IPv4/IPv6 over IPv6的虚拟隧道接口)
内核引入时间:大约在Linux 2.6内核版本中引入(2005年)。
功能:在IPv6网络上传输IPv4或IPv6数据包,实现不同协议之间的互通。
实际应用:用于IPv4和IPv6网络的互联互通,如IPv6过渡技术中的应用。
ipip (IPv4 over IPv4的虚拟隧道接口)
内核引入时间:大约在Linux 2.2内核版本中引入(1999年)。
功能:在IPv4网络上传输IPv4数据包,用于创建隧道和实现网络隔离。
实际应用:用于构建VPN和其他隧道网络,实现远程网络连接和安全通信。
sit (IPv6 over IPv4的虚拟隧道接口)
内核引入时间:大约在Linux 2.2内核版本中引入(1999年)。
功能:在IPv4网络上传输IPv6数据包,实现IPv6网络的连接。
实际应用:用于IPv6网络的部署和过渡,如6to4和6rd等过渡技术。
在网络层面 中,bridge
network是一种链路层设备,用于在网络段之间转发流量。bridge可以是硬件设备,也可以是在主机内核中运行的软件设备。
在Docker 中,bridge network使用软件桥接,它允许连接到同一个bridge network的容器之间可以进行通信,同时,保证了未连接同一bridge network的容器网络之间的隔离。Docker桥接驱动程序会自动在主机中安装规则,以便不同桥接网络上的容器不能直接相互通信。
Bridge network只适用于在同一Docker daemon主机上运行的容器 。要在不同Docker守护进程的主机上运行的容器之间进行通信,您可以在操作系统级别管理路由,或者使用overlay network .。
当启动Docker dameon后,会自动创建一个 default bridge network ,启动的容器会自动连接此桥接网络,当然,我们也可以创建自定义的bridge network,用户定义的桥接网络,优先级高于默认的bridge network。
默认bridge
网络 如下,本机启动Docker dameon后,可以看到默认创建的bridge
驱动类型的桥接网络。
1 2 3 4 5 $ sudo docker network ls NETWORK ID NAME DRIVER SCOPE a1c6775b1ee5 bridge bridge local a09d489d2abd host host local ef2dbca65b2a none null local
可以通过docker network inspect
来查看对应的bridge网络的具体信息,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 [ { "Name" : "bridge" , "Id" : "a1c6775b1ee5708162e562d994f84538a29ab5ab5e3e0cfe05f831dc4c8b81d5" , "Created" : "2022-10-17T15:05:57.378344235+08:00" , "Scope" : "local" , "Driver" : "bridge" , "EnableIPv6" : false , "IPAM" : { "Driver" : "default" , "Options" : null, "Config" : [ { "Subnet" : "192.168.10.0/24" , "Gateway" : "192.168.10.1" } ] }, "Internal" : false , "Attachable" : false , "Ingress" : false , "ConfigFrom" : { "Network" : "" }, "ConfigOnly" : false , "Containers" : {}, "Options" : { "com.docker.network.bridge.default_bridge" : "true" , "com.docker.network.bridge.enable_icc" : "true" , "com.docker.network.bridge.enable_ip_masquerade" : "true" , "com.docker.network.bridge.host_binding_ipv4" : "0.0.0.0" , "com.docker.network.bridge.name" : "docker0" , "com.docker.network.driver.mtu" : "1500" }, "Labels" : {} } ]
其中比较重要的几个信息包括:
网络设备类型信息:"Driver": "bridge"
,桥接网络;
bridge
的网络的配置信息,其中主要包括子网网段信息:"Subnet": "192.168.10.0/24"
;
是否和外部网络互通: "Internal": false
接着我们运行一个CentOS的image,如下:
1 2 3 4 5 6 $ docker run -d -it centos 082a9748b88c408c25693ccd54ed8857ee627a7e5bfee2e9954bd30cc2c38f1f $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 082a9748b88c centos "/bin/bash" 2 seconds ago Up 1 second stupefied_pare
然后我们再查看一下默认的bridge网络的信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 $ docker inspect a1c6775b1ee5 [ { "Name" : "bridge" , "Id" : "a1c6775b1ee5708162e562d994f84538a29ab5ab5e3e0cfe05f831dc4c8b81d5" , "Created" : "2022-10-17T15:05:57.378344235+08:00" , "Scope" : "local" , "Driver" : "bridge" , "EnableIPv6" : false , "IPAM" : { "Driver" : "default" , "Options" : null, "Config" : [ { "Subnet" : "192.168.10.0/24" , "Gateway" : "192.168.10.1" } ] }, "Internal" : false , "Attachable" : false , "Ingress" : false , "ConfigFrom" : { "Network" : "" }, "ConfigOnly" : false , "Containers" : { "082a9748b88c408c25693ccd54ed8857ee627a7e5bfee2e9954bd30cc2c38f1f" : { "Name" : "stupefied_pare" , "EndpointID" : "328398eb1759b4bcf56cbcf664d2092a9c3e5e066abd08b5f96e57a913de6c9f" , "MacAddress" : "02:42:c0:a8:0a:02" , "IPv4Address" : "192.168.10.2/24" , "IPv6Address" : "" } }, "Options" : { "com.docker.network.bridge.default_bridge" : "true" , "com.docker.network.bridge.enable_icc" : "true" , "com.docker.network.bridge.enable_ip_masquerade" : "true" , "com.docker.network.bridge.host_binding_ipv4" : "0.0.0.0" , "com.docker.network.bridge.name" : "docker0" , "com.docker.network.driver.mtu" : "1500" }, "Labels" : {} } ]
可以看到默认的bridge网络的信息中的Containers
字段新增了刚刚启动的centos
名为stupefied_pare
的容器信息,里面包括了本bridge设备为该容器分配的网络地址信息:192.168.10.2/24
。
自定义bridge
网络 我们可以通过docker network create
命令来创建自定义的bridge
网络信息,并设置其相关的属性。如下简单创建一个自定义的bridge
网络的命令(详细的选项参考create
命令):
1 $ docker network create --driver=bridge --subnet=192.168.0.0/24 test-bridge
我们可以查看刚刚创建的bridge网络:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 $ docker network ls -f 'driver=bridge' NETWORK ID NAME DRIVER SCOPE a1c6775b1ee5 bridge bridge local 76e84e49f1f9 test-bridge bridge local $ docker network ls -f 'name=test-bridge' NETWORK ID NAME DRIVER SCOPE 76e84e49f1f9 test-bridge bridge local $ docker network inspect 76e84e49f1f9 [ { "Name" : "test-bridge" , "Id" : "76e84e49f1f9773cb2296f29cf4b5be9d2483aa87095dfa6db0a71fe1b76dcb5" , "Created" : "2024-10-20T00:59:38.219280696+08:00" , "Scope" : "local" , "Driver" : "bridge" , "EnableIPv6" : false , "IPAM" : { "Driver" : "default" , "Options" : {}, "Config" : [ { "Subnet" : "192.168.0.0/24" } ] }, "Internal" : false , "Attachable" : false , "Ingress" : false , "ConfigFrom" : { "Network" : "" }, "ConfigOnly" : false , "Containers" : {}, "Options" : {}, "Labels" : {} } ]
当我们创建,删除一个自定义的bridge网络,或者将一个容器从自定义的bridge网络中connect,disconnect的时候都发生了什么? Docker会通过特定操作系统的工具来管理底层网络基础设施,例如在Linux上添加和删除bridge设备,或配置iptables
。当然这些都是实现细节,Docker可以完全用来管理自定义网络,而不用关注这些实现细节。
后面在分析「跨Network Namespace的通信」一节中,会仔细分析Docker是如何通过iptables来进行网络管理的。
bridge
网络选项前面我们在查看Docker默认创建的bridge
网络的详细信息时,看到有一些复杂的选项配置,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 $ docker network inspect a1c6775b1ee5 [ { "Name" : "bridge" , "Id" : "a1c6775b1ee5708162e562d994f84538a29ab5ab5e3e0cfe05f831dc4c8b81d5" , ... "Options" : { "com.docker.network.bridge.default_bridge" : "true" , "com.docker.network.bridge.enable_icc" : "true" , "com.docker.network.bridge.enable_ip_masquerade" : "true" , "com.docker.network.bridge.host_binding_ipv4" : "0.0.0.0" , "com.docker.network.bridge.name" : "docker0" , "com.docker.network.driver.mtu" : "1500" }, ... } ]
下面主要介绍一下这些参数:
"com.docker.network.bridge.name"
bridge网络对应的主机网卡的名字;下面是我主机上的三个bridge网络的网卡设备信息
1 2 3 4 5 6 7 8 9 10 $ ifconfig -v br-2f8183e15eda: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.20.1 netmask 255.255.255.0 broadcast 192.168.20.255 ... br-76e84e49f1f9: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500 inet 192.168.0.1 netmask 255.255.255.0 broadcast 192.168.0.255 ... docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.10.1 netmask 255.255.255.0 broadcast 192.168.10.255 ...
"com.docker.network.bridge.host_binding_ipv4"
表示默认绑定的主机的网卡地址。如果启动Container的时候,暴露的端口没有指定主机网卡地址,例如:-p 80
or -p 8080:80
,那么Docker会通过host_binding_ipv4
的参数来监听对应主机网卡对应的端口,host_binding_ipv4: "0.0.0.0"
表示默认绑定主机的所有IPv4和IPv6的网卡地址。忽略它的名字,它可以用来绑定IPv6 ,例如::
表示Container暴露的端口只绑定所有的IPv6的网卡。
如果暴露端口的时候指定绑定的主机的ip,就会使用显示指定的主机地址,例如:-p 0.0.0.0:8080:80
限制只能绑定本机的所有IPv4网卡。
com.docker.network.bridge.enable_icc
控制着 同一个Docker 网络中容器间通信(Inter-Container Communication, ICC)。默认是通的,如果设置为false,那么即使在同一个network中,也是无法通信的,实现原理是通过 iptables FORWARD 链规则实现:
如下创建一个enable_icc=false
的bridge网络:
1 $docker network create --opt "com.docker.network.bridge.enable_icc" ="false" --driver=bridge --subnet=192.168.20.0/24 no_icc_bridge
查看iptables的FORWARD链中关于最新创建的网卡br-2f8183e15eda
规则如下:
1 2 3 4 5 $ iptables -L FORWARD -n -v|grep br-2f8183e15eda 0 0 ACCEPT all -- * br-2f8183e15eda 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED 0 0 DOCKER all -- * br-2f8183e15eda 0.0.0.0/0 0.0.0.0/0 0 0 ACCEPT all -- br-2f8183e15eda !br-2f8183e15eda 0.0.0.0/0 0.0.0.0/0 0 0 DROP all -- br-2f8183e15eda br-2f8183e15eda 0.0.0.0/0 0.0.0.0/0
然后我们创建两个连接到no_icc_bridge
网络的Container,进行网络测试,结果如下:
1 2 3 4 5 6 7 8 9 $ docker run -d -it --network no_icc_bridge centos $ docker run -it --network no_icc_bridge centos ping 192.168.20.2 PING 192.168.20.2 (192.168.20.2) 56(84) bytes of data. ^C --- 192.168.20.2 ping statistics --- 16 packets transmitted, 0 received, 100% packet loss, time 15120ms
我们创建一个连接到默认的bridge
网络的Container,进行网络测试,结果就是如下正常的:
1 2 3 4 5 6 7 8 $ docker run -it --network bridge centos ping 192.168.10.2 PING 192.168.10.2 (192.168.10.2) 56(84) bytes of data. 64 bytes from 192.168.10.2: icmp_seq=1 ttl=64 time=0.090 ms 64 bytes from 192.168.10.2: icmp_seq=2 ttl=64 time=0.042 ms 64 bytes from 192.168.10.2: icmp_seq=3 ttl=64 time=0.051 ms ^C --- 192.168.10.2 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 1998ms
"com.docker.network.bridge.enable_ip_masquerade"
它控制 Docker 的 IP 伪装功能。一种 NAT(网络地址转换)技术,它允许容器通过宿主机的 IP 地址访问外部网络。如下,我的环境,Docker会默认创建如下iptables规则:
1 2 3 4 5 $ iptables -t nat -L POSTROUTING -n -v Chain POSTROUTING (policy ACCEPT 15987 packets, 1178K bytes) pkts bytes target prot opt in out source destination 0 0 MASQUERADE all -- * !br-76e84e49f1f9 192.168.0.0/24 0.0.0.0/0 54 3469 MASQUERADE all -- * !docker0 192.168.10.0/24 0.0.0.0/0
通过POSTROUTING
链可以实现容器的网络包在离开主机时进行NAT地址转换:
1 2 3 网络包处理流程 [数据包] -> PREROUTING -> 路由选择 -> FORWARD -> POSTROUTING -> [发出] -> INPUT -> 本地进程 -> OUTPUT ->
network connect 不同的bridge网络的Container之间是不通的,我们可以通过docker network connect
将不同bridge网络的Container连接到同一个网络,然后进行通信。
如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 $ docker ps -q | xargs -n 1 docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}} {{end}} {{.Name}} {{.NetworkSettings.Networks}}' 192.168.0.2 /confident_shockley map[test-bridge:0xc4205d6000] 192.168.10.2 /stupefied_pare map[bridge:0xc420490cc0] $ docker exec -it confident_shockley ping 192.168.10.2 PING 192.168.10.2 (192.168.10.2) 56(84) bytes of data. ^C --- 192.168.10.2 ping statistics --- 3 packets transmitted, 0 received, 100% packet loss, time 2008ms $ docker network connect bridge confident_shockley $ docker ps -q | xargs -n 1 docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}} {{end}} {{.Name}} {{.NetworkSettings.Networks}}' 192.168.10.3 192.168.0.2 /confident_shockley map[bridge:0xc4203b0cc0 test-bridge:0xc4203b0d80] 192.168.10.2 /stupefied_pare map[bridge:0xc42049ecc0] $ docker exec -it confident_shockley ping 192.168.10.2 PING 192.168.10.2 (192.168.10.2) 56(84) bytes of data. 64 bytes from 192.168.10.2: icmp_seq=1 ttl=64 time=0.084 ms 64 bytes from 192.168.10.2: icmp_seq=2 ttl=64 time=0.037 ms ^C --- 192.168.10.2 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1000ms rtt min/avg/max/mdev = 0.037/0.060/0.084/0.024 ms
前面我们提到:当我们将一个容器从bridge网络中connect,disconnect的时候,Docker会通过配置iptables来进行相关网络策略配置 。那具体是如何实现的呢?后面我们会详细介绍
Port Mapping Docker 的 Port Mapping(端口映射) 是容器网络的核心机制之一,用于将容器内部的网络端口映射到宿主机的端口,从而允许外部通过宿主机访问容器内的服务。其设计原理与容器网络隔离、NAT(网络地址转换)技术密切相关。通过Port Mapping可以做到:
将容器内部端口映射到宿主机端口
实现容器服务的外部访问
解决容器网络隔离问题
使用格式如下:
1 $ docker run -p 8000:80 -d nginx
选项 -p
的语法格式为: HOST_PORT:CLIENT_PORT
,含义为:绑定容器CLIENT_PORT
到主机的HOST_PORT
,主机的网卡信息可以忽略,默认是由前面介绍过的Docker Network的"com.docker.network.bridge.host_binding_ipv4"
参数决定,默认是主机的所有IPV4/IPV6网卡。
如下是执行Port Mapping后的nginx的Container信息:
1 2 3 $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 5f03a252f5e0 nginx "/docker-entrypoint.…" 6 seconds ago Up 5 seconds 0.0.0.0:8000->80/tcp, [::]:8000->80/tcp festive_haibt
如下是进行端口映射后,Docker通过iptables写入的映射规则:
主要关键点:
ADDRTYPE match dst-type LOCAL
:作为扩展匹配条件,匹配目标地址为宿主机本地 IP时,跳转到DOCKER链进行匹配;
跳入DOCKER链后,从非docker0
网卡流入的流量,目标地址是8000
tcp端口的请求,全部进行DNAT转换,目标重定向到172.17.0.2:80
,即nginx容器内的的80端口。
Docker 的 Port Mapping 通过 NAT 和网络命名空间隔离,实现了容器服务的灵活暴露。其设计平衡了隔离性、安全性与易用性,是容器化应用与外部通信的基石。理解这一机制有助于排查网络问题(如端口冲突、防火墙限制)和优化容器部署。
自定义bridge和默认bridge的差异 用户定义和默认 bridge network之间的差异 如下:
用户自定义的桥接网络提供容器之间的自动DNS解析。
运行在默认bridge network上的容器之间只能通过IP地址进行通信,除非使用 --link
选项(已经被标记过时),而自定义的桥接网络容器之间可以通过名字或者别名进行相互解析并通信。
例如,有一个Web前端和一个数据库后端组成的应用程序,可以将容器命名为web和db,那么web容器就可以通过名称db连接到数据库容器,而不需要关心它们运行在哪个Docker主机上。如果在默认桥接网络上运行相同的应用程序堆栈,需要手动创建容器之间的链接(使用过时的 --link
选项),并且需要在两个方向上创建,这样在有多个容器需要通信时就变得比较复杂。另一种方法是操纵容器内的/etc/hosts
文件,但是这样会造成难以调试的问题。
所有运行时没有设置--network
选项的容器,都连接默认的bridge network,这就带来无关的服务之间都是可通信的潜在风险。而使用自定义的bridge network,只有显示连接到此bridge network的容器才可以相互通信。
容器可以在运行时attach或者detach用户自定义的桥接网络,而如果detach默认的桥接网络,则需要停止容器。
用户自定义的桥接网络是可配置的
默认的桥接网络,所有的容器共用相同的配置,如果修改默认的配置,所有的使用默认桥接网络的容器都会生效,例如MTU,iptables
的规则。除此之外,修改默认的桥接网络配置,需要重启容器才可以生效。
用户自定义的桥接网络,通过docker network create
来进行创建和配置,每个桥接网络都是独立的,可以根据需要进行创建和配置。
最初,在两个容器之间共享环境变量的唯一方法是通过使用--link
flag 参数,但是这种方式的变量共享无法使用在用户定义的桥接网络。然而,有更好的方法来共享环境变量。例如:
多个容器可以使用Docker卷挂载包含共享信息的文件或目录。
多个容器可以一起使用docker-compose启动,而compose文件可以定义共享变量。
可以使用swarm服务代替独立容器,并利用共享的secrets 和 configs .
主机网络模式,如果Docker Container采用此种网络模式,Docker容器共享主机机器的网络命名空间,即共享网络协议栈。这意味着容器可以直接访问主机的网络接口和端口。此模式下Container不在单独分配IP地址,直接使用主机的IP地址进行通信,例如Container启动时绑定80端口,此时通过主机IP直接可以进行访问。
host
网络模式下,端口映射将会失效 ,即 -p
, --publish
, -P
, and --publish-all
将会失效,并发出告警,如下:
1 2 3 $ docker run -d -it --network host -p 80:80 centos WARNING: Published ports are discarded when using host network mode cb73418ed0b43d2c202275af8e2d0a6e72aab8b0ede4a02723aafb29e2c96be1
可以直接通过主机IP进行Container的端口访问,不需要进行端口映射,如下:
1 2 3 4 5 $ docker exec -it cb73418ed0b4 nc -l 80 $nc 9.134.15.244 80
host
网络模式在两种情况下比较有用:
优化程序性能 :Container应用程序直接访问主机的网络栈,对性能至关重要。相比容器网络,流量不需要在veth,bridge网卡进行转发,也不需要主机通过NAT来进行流量转换;这里能提高多少吞吐量?ChatGPT说30%,但是没有来源 。
Container应用程序需要绑定和处理大量范围的端口 。
host
网络模式也有很大的缺点:
安全风险 :容器完全共享主机网络命名空间,没有网络层隔离;
端口冲突 :容器直接使用主机端口,多个容器不能绑定同一端口,需要手动协调端口分配;
网络资源竞争 :所有容器共享网络带宽,无法限制单个容器网络资源,容器可能相互影响网络性能;
迁移和可移植性差 :例如端口冲突;
host
网络模式支持情况:Docker Desktop 4.34及以后的版本,以及Linux版本的Docker Engine;
none网络模式,是无网卡模式,会让Container的网络和主机网络彻底隔离,即Container不能访问外界任何网络;适用于当容器不需要网络访问或出于安全原因需要网络隔离时。
其实现方式,就是为Container创建独立的Network Namespace,但是不创建对应的bridge
和veth
进行网络的打通,如下:Container内的网卡只有lo
网卡:
1 2 3 4 5 $ docker run -it --network none centos ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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
后面我们手动通过ip
命令来实现跨Network Namespace通信后,就会知道none
网卡模式就是ip netns add ns-1
创建了一个隔离的ns,并没进行网络桥接。
跨Network Namespace的通信 前面我们知道默认的Container通过bridge网卡来进行网络管理,那Container是如何通过bridge网卡和主机上其他的进程进行通信的呢?
在容器化技术之Linux Namespace 一文中,我们知道Namespace是Container实现资源隔离的基础 ;Linux Namespace 对系统的全局资源进行了抽象,让Namespace中的进程组可以认为他们拥有隔离的全局资源 ;对于Namespace拥有的全局资源进行修改,只有本Namespace中的进程可见,其他Namespace的进程是无感知的;
其中Network Namespace 就是Linux针对全局资源Network Device按Namespace进行隔离,可以隔离出:网卡设备,堆栈,端口 等网络资源。这样Container可以很方便的使用自己虚拟出的网卡资源,和其他Namespace项目隔离,互不影响;
我们前面知道:当我们创建,删除一个自定义的bridge网络,或者将一个容器从自定义的bridge网络中connect,disconnect的时候都发生了什么? Docker会通过操作系统的工具在Linux上添加和删除bridge设备,或配置iptables
。那具体一个基于bridge网络的Container是如何和其他Container进行通信,以及如何通过Host和外界进行通信的呢?
我们来看一下创建bridge网络,然后运行Container,Container和主机上的其他进程通信的实现过程:
创建bridge network 如下:
1 $docker network create --driver=bridge --subnet=192.168.0.0/24 test-bridge
然后我们可以看到,系统创建了一个新的网卡:
1 2 3 4 5 6 7 8 $ ifconfig br-e6705c4803fb: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500 inet 192.168.0.1 netmask 255.255.255.0 broadcast 192.168.0.255 ether 02:42:5a:01:14:87 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
然后Docker会自动创建对应的iptables规则,以控制网络流量并实现隔离。
FILTER表 如下是查看iptables默认filter表的所有Chain的格式化输出:
1 $ iptables -nvL |cut -f -9|column -t| sed 's/^Chain/\n&/g' |sed '/^Chain/ s/[ \t]\{1,\}/ /g' |sed '/^[0-9]/ s/[ \t]\{1,\}/ /10g' |sed -e 's/\(br-e6705c4803fb\|DOCKER-USER\|DOCKER-ISOLATION-STAGE-1\|DOCKER-ISOLATION-STAGE-2\)/\x1b[31m&\x1b[0m/g'
我们详细看一下filter表的所有关于新创建的br-e6705c4803fb
虚拟网卡的规则:
首先是Chain级别,我们看到Docker创建了三个相关的Chain:
DOCKER-ISOLATION-STAGE-1 :这是Docker创建的一个iptables链,用于处理Docker容器之间的网络隔离。它是流量隔离的第一阶段。
DOCKER-ISOLATION-STAGE-2 :这是第二阶段的隔离链,用于进一步控制容器的流量。
DOCKER-USER :这是用户定义的链,Docker允许用户在此链上添加自定义的iptables规则。Docker会在此链的开头插入规则,以便用户可以在Docker创建的规则之前或之后添加自己的规则。
我们可以看到,所有需要经过转发的的流量经过FORWARD链时,按照从上至下的优先级:
第一个规则,匹配所有数据包,流量都会跳转到DOCKER-USER 链,目前看默认创建的此Chain包含的Rule的动作只是返回;
第一个规则返回后,匹配上了第二个规则,同样匹配所有数据包,会自动跳转DOCKER-ISOLATION-STAGE-1 链,这是Docker流量管理的第一阶段
DOCKER-ISOLATION-STAGE-1 链里面有三条规则:
规则1:入站数据包来自于br-e6705c4803fb
(我上面刚刚通过docker network create创建的
),出站数据包不非同一个网络桥(!br-e6705c4803fb
)。也就是说从br-e6705c4803fb
网卡发出的所有跨网络流量,都会转发到DOCKER-ISOLATION-STAGE-2 链进行第二阶段处理;
规则2:入站数据包来自于docker0
(docker默认创建的的网卡),出站数据包非同一个网络桥(!docker0
)。也就是说从docker0
网卡发出的所有跨网络流量,都会转发到DOCKER-ISOLATION-STAGE-2 链进行第二阶段处理;
规则3:匹配所有数据包,没有命中上述规则的的流量,直接返回上一层链的规则调用处;
从上面知道,能跳转到 DOCKER-ISOLATION-STAGE-2链的流量都是Docker创建的network的流量,并且是跨网卡的流量才会跳转到这里 。
DOCKER-ISOLATION-STAGE-2 链里面有三条规则:
规则1:出站数据包流向br-e6705c4803fb
,直接丢弃;
规则2:出站数据包流向docker0
,直接丢弃;
规则3:匹配所有数据包,没有命中上述规则的的流量,直接返回上一层链的规则调用处;
基于DOCKER-ISOLATION-STAGE-2 链里面的三条规则,我们知道,Docker如何通过iptables实现不同network之间的网络隔离 。
有关Docker如何在Linux上操作iptables
规则的信息,请参Packet filtering and firewalls .
NAT表 前面介绍docker network创建的bridge网络的默认网络选项的时候,我们知道enable_ip_masquerade
,它控制 Docker 的 IP 伪装功能。一种 NAT(网络地址转换)技术,它允许容器通过宿主机的 IP 地址访问外部网络。如下,Docker在创建一个brdige网卡时,会默认创建如下iptables规则:
通过POSTROUTING
链 (处理离开系统的数据包,在它们被路由选择后但在实际发送之前)可以实现容器的网络包在离开主机时 进行NAT地址转换。
Docker 容器网络中的请求通过非 docker0 和非br接口(如 eth0)发送出去时,使用该出口接口的 IP 地址替换源 IP 地址。这允许 Docker 容器通过主机访问外部网络。
运行基于bridge的Container 如下:基于上面创建的test-bridge
网卡,启动一个Container,如下:
1 $ docker run -d -it --network test-bridge centos
我们会发现,多了一个veth
的网卡:
1 2 3 4 5 6 7 $ifconfig veth5186333: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 ether 1e:3d:ce:45:66:99 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
那什么是veth
网卡,为什么会新增这个网卡?
讨论veth
之前,我们先看一下之前了解过Network Namespace ,我们知道Linux为了隔离网络资源,提供了Network Namespace的功能,可以让每个Container拥有独立的网络栈,不同Namespace的Container之间/Container和主机进程之间是不能直接通信,如下我们看一下启动容器内所看到的网卡资源:
1 2 3 4 5 6 7 8 9 $ docker exec -it 0b14786e369b ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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 29: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link /ether 02:42:c0:a8:00:02 brd ff:ff:ff:ff:ff:ff inet 192.168.0.2/24 brd 192.168.0.255 scope global eth0 valid_lft forever preferred_lft forever
然后我们再看一下主机的网卡资源:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 $ ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN 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: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP qlen 1000 link /ether 52:54:00:49:10:54 brd ff:ff:ff:ff:ff:ff inet 9.134.15.244/20 brd 9.134.15.255 scope global eth1 valid_lft forever preferred_lft forever 3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN link /ether 02:42:20:8e:14:09 brd ff:ff:ff:ff:ff:ff inet 192.168.10.1/24 brd 192.168.10.255 scope global docker0 valid_lft forever preferred_lft forever 28: br-e6705c4803fb: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP link /ether 02:42:5a:01:14:87 brd ff:ff:ff:ff:ff:ff inet 192.168.0.1/24 brd 192.168.0.255 scope global br-e6705c4803fb valid_lft forever preferred_lft forever 30: veth5186333: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-e6705c4803fb state UP link /ether 1e:3d:ce:45:66:99 brd ff:ff:ff:ff:ff:ff
可见,Container所在的Network Namespace和主机不是一个,也可以通过lsns
命令来查看,这里不展开了。那Container中的进程如何和主机的进程进行通信呢,这里就回到veth
网卡的作用。
Veth Linux内核提供的虚拟设备之一:veth
(Virtual Ethernet)虚拟以太网接口,通常用于连接不同Network Namespace ,它是一对虚拟网络接口设备(类似双绞线),一个接口连接到一个网络命名空间,另一个接口连接到另一个网络命名空间。这种方式允许网络数据在同一台主机的不同网络命名空间之间传输,非常适合容器、虚拟机以及网络隔离的应用场景。
在上面我们看到,启动Container后,我们看到主机创建了一个veth5186333
的虚拟网卡,网卡信息可以通过上面的ip addr
查看,这里我们单独列出来veth5186333
的信息:
1 2 30: veth5186333: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-e6705c4803fb state UP link /ether 1e:3d:ce:45:66:99 brd ff:ff:ff:ff:ff:ff
veth
网卡的 master
字段表示该接口所属的主设备 (通常是一个桥接设备 bridge
)。例如上面的veth5186333
的master
字段为br-e6705c4803fb
,我们看一下此veth的对端信息,这里可以通过ethtool
查看对端网卡的索引号,如下:
1 2 3 $ ethtool -S veth5186333 NIC statistics: peer_ifindex: 29
网卡的索引号Linux内核用来表示网络接口的唯一标识符,就是我们前面通过ip
命令输出的网卡最前面的数字,该编号通常从 1 开始,随着系统中的接口数量增加而递增。Linux 系统中,网络接口的索引号可以通过以下方法查看:
1 $ cat /sys/class/net/<interface>/ifindex
/sys/class/net/
是 Linux 系统的一个虚拟文件系统目录,用于存放系统中所有网络接口的信息。每个网络接口在该目录中对应一个子目录,这些子目录包含了该接口的各种属性和配置信息。它是 Linux sysfs
文件系统的一部分,提供了一种方便的方式来查看和操作内核中的设备信息。如下,目前我的主机中的网卡设备信息如下:
1 2 3 4 5 6 7 total 0 lrwxrwxrwx 1 root root 0 Nov 5 14:53 br-e6705c4803fb -> ../../devices/virtual/net/br-e6705c4803fb/ lrwxrwxrwx 1 root root 0 Oct 17 2022 docker0 -> ../../devices/virtual/net/docker0/ lrwxrwxrwx 1 root root 0 Oct 17 2022 eth1 -> ../../devices/pci0000:00/0000:00:05.0/virtio0/net/eth1/ lrwxrwxrwx 1 root root 0 Oct 17 2022 lo -> ../../devices/virtual/net/lo/ lrwxrwxrwx 1 root root 0 Nov 6 01:10 veth5186333 -> ../../devices/virtual/net/veth5186333/
每个网络接口的索引号存储在对应网卡目录的ifindex
文件中。你可以读取该文件查看特定接口的索引号。
1 2 $cat /sys/class/net/veth5186333/iflink 30
通过上面我们知道,veth
虚拟网卡连接了Container中的eth0
和主机网络中的br-e6705c4803fb
bridge网卡。
其实上面表述并不准确,创建veth
的时候必须成对创建,也就是说veth
网卡是一对 ,所以我们经常会看到veth pair
的描述 ,那为什么我们主机上只看到一个veth5186333
网卡呢?那是因为veth pair
的另一端被移入了Container中,并命名为eth0
了。如下:
1 2 3 $ docker exec -it 0b14786e369b ethtool -i eth0 driver: veth ...
所以比较准确的描述是:veth pair
通过将veth
一端置入Container中(作为容器内的默认eth网卡),另一段连接主机中的bridge
网卡 ,实现了Container的Network NS和主机网络的Network NS的打通,以实现Container网络和主机网络以及其他Container网络的联通和隔离。如下示意图:
这里我们梳理一下 bridge
和 veth
的关系:
bridge
网卡,本质是一个虚拟的二层交换机,主要负责转发数据包,维护MAC地址表,一个bridge可以连接多个网络设备(包括物理网卡和虚拟网卡)
veth
网卡,只能成对创建的虚拟网卡,一端发送的数据会出现在另一端,主要用于连接不同的网络命名空间,就像一根网线的两端
所以:bridge像一个交换机提供交换功能,veth像一根网线连接容器到bridge ,两者配合实现了容器的网络互联。
如下是测试数据在主机网络和Container网络如何经过 bridge
和 veth
转发的:
1 2 3 4 5 $docker exec -it 0b14786e369b nc -l 8080$ nc 192.168.0.2 8080
如下是TCP三次握手的数据在 bridge
和 veth pair
之间转发的过程,我按照数据包转发时间顺序标注了序号:从tcpdump的抓包时间可知,通过bridge和veth结构的跨NS的流量延迟在20us左右,即us级别 。
如下示意图:
可以简述为:
nc
进程的握手数据包进入主机的协议栈;
查找路由表,发现目标地址在br-e6705c4803fb
网段,转发到br-e6705c4803fb
网卡;
br-e6705c4803fb
网卡根据目标IP查找MAC地址,转发到对应的veth pair
的主端:veth5186333
;
veth5186333
网卡将数据包转发到veth pair
另一端的Container的eth0
中,实现跨Network Namespace的通信;
Container中的eth0
网卡,将数据转发给本机的nc
进程;
Container之间通信 我们再启动一个基于test-bridge
的Container,如下:
1 $ docker run -d -it --network test-bridge centos
此时会自动创建另一个veth用来打通Container的Network NS和主机网络的Network NS,如下是最新的网卡信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 $ ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN 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: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP qlen 1000 link /ether 52:54:00:49:10:54 brd ff:ff:ff:ff:ff:ff inet 9.134.15.244/20 brd 9.134.15.255 scope global eth1 valid_lft forever preferred_lft forever 3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN link /ether 02:42:20:8e:14:09 brd ff:ff:ff:ff:ff:ff inet 192.168.10.1/24 brd 192.168.10.255 scope global docker0 valid_lft forever preferred_lft forever 28: br-e6705c4803fb: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP link /ether 02:42:5a:01:14:87 brd ff:ff:ff:ff:ff:ff inet 192.168.0.1/24 brd 192.168.0.255 scope global br-e6705c4803fb valid_lft forever preferred_lft forever 30: veth5186333: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-e6705c4803fb state UP link /ether 1e:3d:ce:45:66:99 brd ff:ff:ff:ff:ff:ff 38: veth4fd0b28: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-e6705c4803fb state UP link /ether b6:5d:8a:c2:9c:17 brd ff:ff:ff:ff:ff:ff
新启动容器内网卡信息如下:
1 2 3 4 5 6 7 8 9 $ docker exec -it 41763f201e2d ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link /ether 02:42:c0:a8:00:03 brd ff:ff:ff:ff:ff:ff inet 192.168.0.3/24 brd 192.168.0.255 scope global eth0 valid_lft forever preferred_lft forever
前面我们知道了通过 bridge
和 veth
是如何实现:主机网络和Container网络互通的,同理基于同一个 bridge
的多个Container之间也是默认可以通信的,原理如下:
1 2 3 4 5 6 7 8 9 容器1 namespace 主机 namespace 容器2 namespace +----------------+ +---------------------+ +----------------+ | | | | | | | eth0 | | docker0 | | eth0 | | ↑ | | (bridge) | | ↑ | | | | | ↗ ↖ | | | | | | | | ↗ ↖ | | | | | veth1的一端 | |veth1另一端 veth2另一端| | veth2的一端 | +----------------+ +---------------------+ +----------------+
但基于多个不同 bridge
的多个Container之间网络是隔离的 ,在前面我们介绍了Docker在创建 bridge
网卡的时候会配置iptables,让不同的 bridge
网络天然的进行隔离。
如下是基于test-bridge
的docker network创建的另一个Container是如何通过此bridge
和 veth
进行通信的:
如下,Container2和Container1通信的流量转发示意图:
整个转发大致流程如下:
Container2 的nc
进程的数据包进入容器的协议栈;
查找路由表,其实只有eth0
网卡的路由表,所有流量都通过eth0
转发;
Container2容器内的eth0
网卡其实是veth5186333
的另一端,所以流量会直接转发到主机网卡veth5186333
;
进入主机协议栈后,查找路由表,目标192.168.0.2
的流量的转发目标为br-e6705c4803fb
网卡,所以会将流量转发到br-e6705c4803fb
网卡;
br-e6705c4803fb
网卡根据目标IP查找MAC地址,转发到对应的veth pair
的主端:veth5186333
;
veth5186333
网卡将数据包转发到veth pair
另一端的Container1的eth0
中,实现跨Network Namespace的通信;
Container1中的eth0
网卡,将数据转发给本机的nc
进程;
其实,上面介绍的跨Container之间的通信实现,也被用在了Kubernetes中同一个Nodes中的跨Pods的Container之间的通信,如下是通过Veth
和Bridge
进行通信的示意图:
手动实现跨NS的通信 前面我们在介绍veth和bridge网卡相关的概念的时候,都是通过Docker命令来管理这两个网卡的,Docker通过调用系统工具来进行网络的隔离和管理的相关操作,对用户屏蔽了底层虚拟化和网络的相关细节,那么为了更好的理解Docker是如何在Linux上进行网络管理的,我们接下来通过强大的ip
命令来进行network namespace,veth和bridge的相关创建和管理。
ip
命令是 Linux 系统中用于配置网络接口、路由和网络相关设置的强大工具 。它是 net-tools
工具集的替代品,属于 iproute2
软件包。
net-tools
工具集:包含:网络接口管理:ifconfig
, 路由管理:router
,ARP管理:arp
,网络状态监控:netstat
,自2001年起,Linux社区已经对其停止维护;
iproute2
工具集:新一代的网络配置工具,是现代 Linux 网络管理的瑞士军刀,提供了强大、灵活的网络配置能力,主要包括:网络管理工具:ip
,套接字管理:ss
,流量管理:tc
;网桥管理:bridge
(包含在ip link
);
如下,我们通过ip
命令来创建network namespace,veth,bridge,然后进行网络的联通测试:
先通过ip
工具在主机的当前ns下创建一个新的bridge网卡,并配置IP,然后启用;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 $ip link add bridge-test-1 type bridge$ip addr add 192.168.20.1/24 dev bridge-test-1$ip link set bridge-test-1 up$ip addr... 41: bridge-test-1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN link /ether be:7c:c6:74:2f:f1 brd ff:ff:ff:ff:ff:ff inet 192.168.20.1/24 scope global bridge-test-1 valid_lft forever preferred_lft forever $ ping 192.168.20.1 PING 192.168.20.1 (192.168.20.1) 56(84) bytes of data. 64 bytes from 192.168.20.1: icmp_seq=1 ttl=64 time=0.044 ms 64 bytes from 192.168.20.1: icmp_seq=2 ttl=64 time=0.034 ms
通过ip
工具创建一个新的network namespace,然后我们可以查看该network namespace下默认只有一个lo
环回虚拟网卡;并测试新的ns下,和主机的bridge-test-1
的网络连通性;
1 2 3 4 5 6 7 8 9 $ip netns add netns-test-1$ip netns exec netns-test-1 ip addr1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN link /loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 $ip netns exec netns-test-1 ping 192.168.20.1connect: Network is unreachable
通过ip
工具在主机ns中创建一对veth
网卡:veth-test-1-1
和veth-test-1-2
,并启用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 $ip link add veth-test-1-1 type veth peer name veth-test-1-2$ip link set veth-test-1-1 up$ip link set veth-test-1-2 up$ip link 39: veth-test-1-2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT qlen 1000 link /ether 16:c2:bc:5a:fd:84 brd ff:ff:ff:ff:ff:ff 40: veth-test-1-1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT qlen 1000 link /ether 92:53:d1:77:09:52 brd ff:ff:ff:ff:ff:ff $ ethtool -S veth-test-1-1 NIC statistics: peer_ifindex: 39 $ ethtool -S veth-test-1-2 NIC statistics: peer_ifindex: 40
通过ip
工具将veth
的一端绑定到主机的bridge-test-1
网卡,另一端移入创建的netns-test-1
的network namespace中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 $ip link set veth-test-1-1 master bridge-test-1$ip link set veth-test-1-2 netns netns-test-1$ip addr40: veth-test-1-1: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast master bridge-test-1 state DOWN qlen 1000 link /ether 92:53:d1:77:09:52 brd ff:ff:ff:ff:ff:ff $ ip netns exec netns-test-1 ip addr 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN link /loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 39: veth-test-1-2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000 link /ether 16:c2:bc:5a:fd:84 brd ff:ff:ff:ff:ff:ff
通过ip
工具重新配置移入到netns-test-1
的network namespace中veth网卡的一端:veth-test-1-2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 $ip netns exec netns-test-1 ip link set veth-test-1-2 name eth0$ ip netns exec netns-test-1 ip addr add 192.168.20.4/24 dev eth0 $ip netns exec netns-test-1 ip link set eth0 up$ ip netns exec netns-test-1 ip addr 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN link /loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 39: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000 link /ether 16:c2:bc:5a:fd:84 brd ff:ff:ff:ff:ff:ff inet 192.168.20.4/24 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::14c2:bcff:fe5a:fd84/64 scope link valid_lft forever preferred_lft forever
通过上面的配置,我们就打通了新创建的network namespace和主机的network namepsace ,如下,可以ping通主机bridge-test-1了。
1 2 3 4 $ ip netns exec netns-test-1 ping 192.168.20.1 PING 192.168.0.1 (192.168.0.1) 56(84) bytes of data. 64 bytes from 192.168.0.1: icmp_seq=1 ttl=64 time=0.082 ms 64 bytes from 192.168.0.1: icmp_seq=2 ttl=64 time=0.059 ms
删除创建的ns和bridge网络
1 2 3 4 5 $ip netns delete netns-test-1$ip link delete bridge-test-1
上面这就是一个完整的手动配置过程。Docker实际上就是通过类似的操作,自动化地为每个容器创建和配置网络。主要区别是:
Docker使用自己的libnetwork库通过netlink系统调用来进行网络管理;
自动分配IP地址和自动管理接口名称;
提供DNS服务;
自动管理网络生命周期;
overlay
网络模式提供了在不同Docker daemon主机上的Container进行通信的能力。overlay
网络,顾名思义,位于主机特定的网络之上 ,连接到此网络的容器之间能够进行安全的通信,Docker 透明地处理每个数据包的路由,确保数据包正确地在 Docker daemon 主机和目标容器之间传输。
这在多主机环境中特别有用,其中容器需要在不同物理或虚拟机之间进行通信。overlay
网络使用VXLAN(虚拟可扩展局域网)技术封装和路由主机之间的网络流量。
和用户自定义的bridge
网络一样,我们可以通过docker network create
创建自定义的overlay
网络,Service(Docker Swarm集群的服务,一组Container的抽象,类似K8S中的Service)和Container可以同时连接多个网络进行通信。
overlay
网络常用于连接多个Docker Swarm Services,但是它也可以连接运行在不同Docker主机的独立的Container,此种standalone
模式,也需要通过Swarm mode来进行主机的连接。下面会介绍standalone
模式的overlay
模式网络的使用,其他关于Swarm Services的使用,详细可以参考:Manage Swarm service networks 。
vxlan 介绍Docker的overlay
网络之前,我们先看一下其实现的底层的网络技术:vxlan
。
vxlan
:Virtual Extensible LAN ,虚拟局域网扩展,是一种虚拟网络技术,旨在解决传统局域网(LAN)在数据中心规模和灵活性上的局限性。它广泛应用于云计算和大规模数据中心环境中,用于支持虚拟机和容器的动态迁移和多租户隔离。
vxlan技术已经作为RFC7348 正式标准,于2014年发布,它的核心是通过在一个Layer 3层 网络上覆盖一个虚拟的Layer 2层 网络来解决虚拟化技术发展带来的大规模虚拟网络设备通信的需求。
在介绍vxlan技术中有几个关键属于:
VXLAN Segment :一个VXLAN Layer 2 网络,有一组共同VNI的设备组成,类似于传统网络中的VLAN。每个VXLAN Segment都具有独立的广播域,可以隔离不同的租户或应用程序流量
VNI :VXLAN Network Identifier (or VXLAN Segment ID),vxlan网络标识符,24-bit长度,可以表示1600万个逻辑网络即Segment,
VTEP :VXLAN Tunnel End Point. VXLAN隧道的端点,用于在VXLAN网络中封装和解封装数据包,它是VXLAN架构中的关键组件,它可以存在于:物理交换机,虚拟交换机,主机中。
VXLAN Gateway:处理多个VXLANs之间的流量转发,连接 VXLAN 网络与传统网络。
VXLAN Frame格式 下面我们从协议结构看一下VXLAN是如何实现在主机的Layer 3层 网络上实现虚拟的Layer 2层 网络进行通信的:
首先看最底层以太网的头的格式,这里VXLAN并不会在这一层有任何标记,目的MAC地址可能是VTEP封装后的目标真实主机的MAC地址
1 2 3 4 5 6 7 8 9 10 11 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Outer Destination MAC Address | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Outer Destination MAC Address | Outer Source MAC Address | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Outer Source MAC Address | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |OptnlEthtype = C-Tag 802.1Q | Outer.VLAN Tag Information | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Ethertype = 0x0800 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
然后是IP网络层的协议头,是VTEP封装后,真实通信的主机的地址:
1 2 3 4 5 6 7 8 9 10 11 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |Version| IHL |Type of Service| Total Length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Identification |Flags| Fragment Offset | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Time to Live |Protocl=17(UDP)| Header Checksum | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Outer Source IPv4 Address | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Outer Destination IPv4 Address | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
然后是传输层的协议头,这里先划重点:VXLAN在Layer 3层采用UDP进行协议封装 ,以提供高效的的数据传输能力,且IANA为了VXLAN分配了4789 熟知端口,用来VXLAN的隧道流量传输 ;
1 2 3 4 5 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Source Port | Dest Port = VXLAN Port | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | UDP Length | UDP Checksum | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
这里按照RFC7348 标准,VXLAN封装的UDP Checksum应该为0 ,这样应该也是为了提供转发性能;
接下来就是VXLAN的协议头了,如下:
1 2 3 4 5 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |R|R|R|R|I|R|R|R| Reserved | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | VXLAN Network Identifier (VNI) | Reserved | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
主要包括:
Flag:8个bits,I
位必须置为1,表示有效的VNI,其他7bits是预留字段;
VNI:24个bits,唯一标识一个VXLAN Segment,前面介绍过;
Reserved:24个bits和8bits,保留字段,传输过程中必须置为0;
接下来就是具体内部网络的完整包了同样包括:Inner Ethernet Header + Inner IP Header + Inner IP Payload
,如下是经过VXLAN封装后的完整的以太网链路层的协议包格式:
手动创建VXLAN 下面我们通过ip
命令来手动创建一个VXLAN网络进行简单的通信,如下:
在Host1上,创建一个名为vxlan-test
的VXLAN网卡,VNI为8888,隧道端点使用设备eth0
进行传输,remote
指定单播点对点的VXLAN隧道的目标,即远程VTEP的地址,local
即VTEP的本端地址;
1 2 3 $ ip link add vxlan-test type vxlan id 8888 dev eth0 remote 10.206.0.12 local 10.206.0.37 dstport 4789 $ ip addr add 192.168.1.1/24 dev vxlan-test $ ip link set up dev vxlan-test
启用vxlan-test
后,可以看到对应网卡的信息如下:
1 2 3 4 5 6 7 $ ip add 19: vxlan-test: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default qlen 1000 link /ether fe:64:03:68:cc:cb brd ff:ff:ff:ff:ff:ff inet 192.168.1.1/24 scope global vxlan-test valid_lft forever preferred_lft forever inet6 fe80::fc64:3ff:fe68:cccb/64 scope link valid_lft forever preferred_lft forever
在Host2上,同样创建名为vxlan-test
的VXLAN网卡,所有参数和Host1一样,只是把VTEP两端的地址调换一下,即remote
和local
调换,如下:
1 2 3 $ ip link add vxlan-test type vxlan id 8888 dev eth0 remote 10.206.0.37 local 10.206.0.12 dstport 4789 $ ip addr add 192.168.1.2/24 dev vxlan-test $ ip link set up dev vxlan-test
启用vxlan-test
后,同样可以看到对应网卡的信息如下:
1 2 3 4 5 6 24: vxlan-test: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default qlen 1000 link /ether ce:47:7f:57:20:98 brd ff:ff:ff:ff:ff:ff inet 192.168.1.2/24 scope global vxlan-test valid_lft forever preferred_lft forever inet6 fe80::cc47:7fff:fe57:2098/64 scope link valid_lft forever preferred_lft forever
接下来我们就可以在Host1上直接测试是否可以ping通基于VXLAN网络的Layer 2的内部网络,如下:
1 2 3 4 5 6 7 8 $ ping 192.168.1.2 PING 192.168.1.2 (192.168.1.2) 56(84) bytes of data. 64 bytes from 192.168.1.2: icmp_seq=1 ttl=64 time=0.211 ms 64 bytes from 192.168.1.2: icmp_seq=2 ttl=64 time=0.206 ms ^C --- 192.168.1.2 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1002ms rtt min/avg/max/mdev = 0.206/0.208/0.211/0.002 ms
如下,我们在Host2上进行抓包可以比较清楚的看到,VXLAN是如何在主机的Layer 3层 网络上实现虚拟的Layer 2层 网络通信的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 $ tcpdump -ni eth0 port 4789 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes 00:41:58.021949 IP 10.206.0.37.34258 > 10.206.0.12.4789: VXLAN, flags [I] (0x08), vni 8888 IP 192.168.1.1 > 192.168.1.2: ICMP echo request, id 7, seq 1, length 64 00:41:58.022085 IP 10.206.0.12.52816 > 10.206.0.37.4789: VXLAN, flags [I] (0x08), vni 8888 IP 192.168.1.2 > 192.168.1.1: ICMP echo reply, id 7, seq 1, length 64 00:42:03.186250 IP 10.206.0.12.40462 > 10.206.0.37.4789: VXLAN, flags [I] (0x08), vni 8888 ARP, Request who-has 192.168.1.1 tell 192.168.1.2, length 28 00:42:03.186487 IP 10.206.0.37.47323 > 10.206.0.12.4789: VXLAN, flags [I] (0x08), vni 8888 ARP, Reply 192.168.1.1 is-at fe:64:03:68:cc:cb, length 28 00:42:03.230061 IP 10.206.0.37.47323 > 10.206.0.12.4789: VXLAN, flags [I] (0x08), vni 8888 ARP, Request who-has 192.168.1.2 tell 192.168.1.1, length 28 00:42:03.230147 IP 10.206.0.12.40462 > 10.206.0.37.4789: VXLAN, flags [I] (0x08), vni 8888 ARP, Reply 192.168.1.2 is-at ce:47:7f:57:20:98, length 28
在上面使用VXLAN进行跨机的通信的时候,我们在Host2上进行抓包可以清楚的看到ARP地址解析协议请求,但是我们在主机上是看不到ARP表的,因为VXLAN的ARP解析是在VTEP(虚拟隧道端点)层面 进行的,所以主机上可能看不到传统的ARP表项。MAC地址表记录了远程主机的MAC地址和对应的VTEP信息,这样可以实现跨网段的通信。
Docker Swarm集群 使用overlay
网络模式前,还是需要初始化docker swarm集群,可以参考官方:Swarm Mode ,简单操作如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 $ docker swarm init --default-addr-pool 192.166.0.0/16 --force-new-cluster Swarm initialized: current node (3yvp1wuoqx2nhqtksv3luwo5v) is now a manager. To add a worker to this swarm, run the following command : docker swarm join --token SWMTKN-1-4unwf5q31qkq4jvj9o5p4lfxx670j1cqsdv2e1ou0r090zh4w3-eoi2nv809n1e0ys3vkwewf82f 10.206.0.37:2377 To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions. $ docker swarm join --token SWMTKN-1-4unwf5q31qkq4jvj9o5p4lfxx670j1cqsdv2e1ou0r090zh4w3-eoi2nv809n1e0ys3vkwewf82f 10.206.0.37:2377 This node joined a swarm as a worker.
上面初始化后,我们可以在Swarm的管理节点查看集群中的Node列表,如下:
1 2 3 4 $ docker node ls ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE mrz8ofttzouczuq42wo7jvfcg VM-0-12-ubuntu Ready Active 24.0.7 3yvp1wuoqx2nhqtksv3luwo5v * VM-0-37-ubuntu Ready Active Leader 24.0.7
Swarm集群网络 Swarm集群初始化完成后,我们可以看到新创建了两个网络如下:
1 2 3 4 $ docker network ls NETWORK ID NAME DRIVER SCOPE ca269ad68a60 docker_gwbridge bridge local lqchwltv5w1c ingress overlay swarm
这两个网络是Swarm集群初始化自动创建的,这两个网络是Swarm集群的Service & Container通信的基础:
docker_gwbridge
:此网络看名字就知道为bridge
网络,专门用来连接overlay
网络和Container的主机网络,Container最终通过该bridge
和外部进行通信,和前面我们介绍bridge
如何打通跨Namespace的功能是一致的。
ingress
:此网络为overlay
网络模式,是Swarm初始化是自动创建的特殊的overlay
网络,用来在对Swarm集群中的Serivces进行服务发现 和负载均衡 。当Swarm的任一Node收到一个到某Service暴露端口的请求后,会将请求交由IPVS
模块,IPVS
模块会根据目标Service配置的路由算法,选择其中一个IP,通过此ingress
网络交由到对应的Node上。
我们看一下这两个新创建的Docker network的详细信息,这里先看一下docker_gwbridge
的bridge
网络详细信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 $ docker inspect docker_gwbridge [ { "Name" : "docker_gwbridge" , "Id" : "ca269ad68a60fa39f9de71602679484f45d6eca8f630918f23d599dc88ea955d" , "Scope" : "local" , "Driver" : "bridge" , "IPAM" : { ... "Config" : [ { "Subnet" : "172.18.0.0/16" , "Gateway" : "172.18.0.1" } ] }, ... "Containers" : { "ingress-sbox" : { "Name" : "gateway_ingress-sbox" , "EndpointID" : "21c14a19f293bbf0d4937a5b6afb3a56ea79a61595e29fca526c24168c897f8e" , "MacAddress" : "02:42:ac:12:00:02" , "IPv4Address" : "172.18.0.2/16" , "IPv6Address" : "" } }, } ]
再看一下名为ingress
的overlay
的网络信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 $ docker inspect ingress [ { "Name" : "ingress" , "Id" : "lqchwltv5w1ctjo9nt1cjrvjw" , "Scope" : "swarm" , "Driver" : "overlay" , "IPAM" : { "Config" : [ { "Subnet" : "192.166.0.0/24" , "Gateway" : "192.166.0.1" } ] }, ... "Containers" : { "ingress-sbox" : { "Name" : "ingress-endpoint" , "EndpointID" : "0aa396d7381de9b69273eb9e2ba50c5ccc4cf00818dd81be83f05f47ac5fe69f" , "MacAddress" : "02:42:c0:a6:00:02" , "IPv4Address" : "192.166.0.2/24" , "IPv6Address" : "" } }, "Options" : { "com.docker.network.driver.overlay.vxlanid_list" : "4096" }, "Labels" : {}, "Peers" : [ { "Name" : "83472d9a8b9f" , "IP" : "10.206.0.37" }, { "Name" : "a118c4fd6ae8" , "IP" : "10.206.0.12" } ] } ]
我们看一下ingress
网络的信息:
vxlanid_list
:是VXLAN的Segment ID,即VNI,唯一标识一个VXLAN网络的,用于标识该Swarm集群;
Peers
:Swarm集群的Node列表信息,里面就是Node的物理IP地址,用于Service跨级路由;
除此以为,我们可以看到ingress
网卡和docker_gwbridge
网卡都已经Attach了一个名为:ingress-sbox
的容器 ,这个容器是干什么的? 但是我们在本机上为什么看不到该容器 。
带着疑问,那我们接下来在看一下,主机也多了两个网卡信息:
1 2 3 4 5 6 7 8 9 10 11 $ ip addr 7: docker_gwbridge: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link /ether 02:42:f1:3f:62:59 brd ff:ff:ff:ff:ff:ff inet 172.18.0.1/16 brd 172.18.255.255 scope global docker_gwbridge valid_lft forever preferred_lft forever inet6 fe80::42:f1ff:fe3f:6259/64 scope link valid_lft forever preferred_lft forever 24: vethe92990a@if23: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker_gwbridge state UP group default link /ether 12:00:be:e1:f3:40 brd ff:ff:ff:ff:ff:ff link-netns ingress_sbox inet6 fe80::1000:beff:fee1:f340/64 scope link valid_lft forever preferred_lft forever
可以看到,Docker network对应的docker_gwbridge
的主机网卡bridge
网络,但是这里有两个疑问 :
overlay
网络对应的网卡去哪了? 没有对应的overlay
网络的网卡,只是额外多了一个veth
虚拟网卡vethe92990a
;
vethe92990a
网卡干嘛的,另一端在哪? 对应的veth pair
的主端连接的是docker_gwbridge
网卡,另一端的网卡索引为23
,但是当前主机的Network Namespace并没有看到此网卡;
官方的手册Swarm Networking 也没介绍相关的网络拓扑和说明,上面几个疑问,我们逐一解答:
Docker-tutorial 这篇文章介绍了ingress-sbox
的容器 ,是Docker创建并维护的特殊Network Namspace ,目的是为了提供:服务发现 ,流量路由 和负载均衡 ,它是 Swarm Service网络的大脑,它运行在所有的Swarm Node上,主要通过内核IPVS
模块进行流量转发,IPVS
模块会根据目标Service配置的路由算法,选择其中一个IP,通过ingress
网络交由到对应的Node上。
当你创建一个Swarm服务并发布端口时,这些端口会被绑定到ingress-sbox
,无论请求从哪个节点进入,ingress网络都能确保流量被正确路由到运行服务的容器上,保证负载均衡。
虽然在docker_gwbridge
和ingress
网络信息的Containers中,ingress-sbox
被归类为Container,但是技术上是一个Network Namespace,只包含网络配置,用于网络隔离和路由。由于是Docker创建的特殊Network Namspace ,没有关联任何进程,是一个持久化的Namespace ,所以我们通过lsns
查看不到该Network Namespace:
lsns is not able to see persistent namespaces without processes where the namespace instance is held by a bind mount to /proc/pid/ns/type.
但是我们通过ip netns
也查不到该Network Namespace的存在,是什么原因呢?通过ip netns
手册发现它是查看/var/run/netns
目录下的有名的Network Namespace。
By convention a named network namespace is an object at /var/run/netns/NAME that can be opened. The file descriptor resulting from opening /var/run/netns/NAME refers to the specified network namespace. Holding that file descriptor open keeps the network namespace alive. The file descriptor can be used with the setns(2) system call to change the network namespace associated with a task.
通过上面的lsns
和ip netns
都查看不到ingress-sbox
Network Namespace,那Docker Swarm创建的这个特殊Network Namspace 到底在哪里?从Docker Swarm Container Networking 我们知道ingress-sbox
Network Namespace存在于/var/run/docker/netns
目录下,由Docker daemon独立管理的Network Namespace:如下:
1 2 $ ls /var/run/docker/netns 1-lqchwltv5w ingress_sbox
为了方便通过ip netns
进行管理,我们将Docker独立管理的这两个ns,链接到公共的netns目录下,如下:
1 2 3 4 $ ln -s /var/run/docker/netns/* /var/run/netns/ $ ip netns list 1-lqchwltv5w (id : 0) ingress_sbox (id : 1)
然后我们看一下这两个Network Namespace里面的网卡信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 $ ip netns exec ingress_sbox ip addr 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 21: eth0@if22: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default link /ether 02:42:c0:a6:00:02 brd ff:ff:ff:ff:ff:ff link-netns 1-lqchwltv5w inet 192.166.0.2/24 brd 192.166.0.255 scope global eth0 valid_lft forever preferred_lft forever 23: eth1@if24: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link /ether 02:42:ac:12:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 1 inet 172.18.0.2/16 brd 172.18.255.255 scope global eth1 valid_lft forever preferred_lft forever $ ip netns exec 1-lqchwltv5w ip addr 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 2: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default link /ether 0e:9b:40:d0:9f:24 brd ff:ff:ff:ff:ff:ff inet 192.166.0.1/24 brd 192.166.0.255 scope global br0 valid_lft forever preferred_lft forever 20: vxlan0@if20: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master br0 state UNKNOWN group default link /ether fe:40:0b:1d:01:3c brd ff:ff:ff:ff:ff:ff link-netnsid 0 22: veth0@if21: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master br0 state UP group default link /ether 0e:9b:40:d0:9f:24 brd ff:ff:ff:ff:ff:ff link-netns ingress_sbox
Standalone Containers基于overlay网络 由于Swarm Service的网络相对比较复杂,这里我们先通过在Swarm集群中运行基于overlay
的Standalone Container 来观察overlay
网络是如何工作的。Stanalone模式可以不创建swarm集群的情况下,使用overlay网络来实现不同容器跨Docker主机的通信能力。
首先:我们在Swarm集群的管理节点Host1(只能在manger node上创建overlay网络)上创建一个overlay
的网络,如下:
1 2 $docker network create -d overlay --attachable overlay-test-1bv8sznvlx2p1vs2lauds8zdj6
创建成功后我们可以看到有以下两个overlay
的docker网卡资源,其实名为ingress
的网络是docker swarm在初始化的时候创建的:
1 2 3 4 $ docker network ls NETWORK ID NAME DRIVER SCOPE lqchwltv5w1c ingress overlay swarm vfchbsa266tu overlay-test-1 overlay swarm
我们看一下overlay-test-1
网络的信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 $ docker network inspect overlay-test-1 [ { "Name" : "overlay-test-1" , "Id" : "bv8sznvlx2p1vs2lauds8zdj6" , "Created" : "2024-11-29T04:17:57.123285722Z" , "Scope" : "swarm" , "Driver" : "overlay" , "EnableIPv6" : false , "IPAM" : { "Driver" : "default" , "Options" : null, "Config" : [ { "Subnet" : "192.166.1.0/24" , "Gateway" : "192.166.1.1" } ] }, "Internal" : false , "Attachable" : true , "Ingress" : false , "ConfigFrom" : { "Network" : "" }, "ConfigOnly" : false , "Containers" : null, "Options" : { "com.docker.network.driver.overlay.vxlanid_list" : "4097" }, "Labels" : null } ]
其中vxlanid_list
:是VXLAN的Segment ID,即VNI,唯一标识一个VXLAN网络的,用于标识该Swarm集群;
接下来在Host1上启动一个Container连接到此overlay
网络:
1 2 $ docker run -d -it --network overlay-test-1 --name container-host1 centos 15e42ce8fd6b3616af5f3a7a2c3a6479e54ae1d9c25b6ecf32035de9b5552a66
启动容器成功后,我们会发现Host1上多了一张网卡信息:
1 2 3 4 5 $ ip addr 45: veth602d3d0@if44: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker_gwbridge state UP group default link /ether c2:cd :57:10:37:f3 brd ff:ff:ff:ff:ff:ff link-netns 519973c21f1e inet6 fe80::c0cd:57ff:fe10:37f3/64 scope link valid_lft forever preferred_lft forever
然后通过ip netns
可以查看多出来三个Network Namespace:
1 2 3 4 5 6 $ ip netns 519973c21f1e (id : 4) 1-bv8sznvlx2 (id : 2) lb_bv8sznvlx (id : 3) 1-lqchwltv5w (id : 0) ingress_sbox (id : 1)
我们看一下新增的三个Network Namespace的网卡信息,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 $ ip netns exec 519973c21f1e ip addr 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 42: eth0@if43: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default link /ether 02:42:c0:a6:02:04 brd ff:ff:ff:ff:ff:ff link-netns 1-bv8sznvlx2 inet 192.166.2.4/24 brd 192.166.2.255 scope global eth0 valid_lft forever preferred_lft forever 44: eth1@if45: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link /ether 02:42:ac:12:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 1 inet 172.18.0.3/16 brd 172.18.255.255 scope global eth1 valid_lft forever preferred_lft forever $ ip netns exec 1-bv8sznvlx2 ip addr 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 2: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default link /ether 62:86:34:cb:b2:7c brd ff:ff:ff:ff:ff:ff inet 192.166.2.1/24 brd 192.166.2.255 scope global br0 valid_lft forever preferred_lft forever 39: vxlan0@if39: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master br0 state UNKNOWN group default link /ether 62:86:34:cb:b2:7c brd ff:ff:ff:ff:ff:ff link-netnsid 0 41: veth0@if40: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master br0 state UP group default link /ether 6a:0c:45:74:0c:80 brd ff:ff:ff:ff:ff:ff link-netns lb_bv8sznvlx 43: veth1@if42: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master br0 state UP group default link /ether a6:54:1b:ed:66:26 brd ff:ff:ff:ff:ff:ff link-netns 519973c21f1e $ ip netns exec lb_bv8sznvlx ip addr 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 40: eth0@if41: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default link /ether 02:42:c0:a6:02:05 brd ff:ff:ff:ff:ff:ff link-netns 1-bv8sznvlx2 inet 192.166.2.5/24 brd 192.166.2.255 scope global eth0 valid_lft forever preferred_lft forever
新创建的container-host1
容器,带来的网卡相关的拓扑如下:
新增的三个netns对应的功能为:
519973c21f1e
:容器自身的网络栈(eth0、路由、DNS),分配Overlay网络的IP地址;
1-bv8sznvlx2
:Overlay网络的核心组件,负责跨主机通信的底层实现。用于管理以下内容:
VXLAN隧道接口(如vxlan0):封装和解封装跨主机通信的数据包。
路由表:定义如何将流量路由到其他主机或容器。
网络网关:作为容器流量的出口,处理容器与外部网络的通信。
lb_bv8sznvlx
:即使未使用Swarm模式,Docker Overlay网络也会预置负载均衡组件,用于未来可能的服务扩展(如Swarm服务)。
我们可以看到基于overlay-test-1
的overlay
网络创建的standalone Container和普通基于bridge
网络运行的Container有比较明显的区别:Container内有两张veth
网卡:eth1
和eth0
,它们的功能分别是:
eth1
:和Docker Swarm集群外 的网络通信,它和docker_gwbridge
分配在一个网段;例如主机可以通过此网卡访问此Container;
eth0
:和Docker Swarm集群内 的网络通信,它分配的是overlay-test-1
网卡的网段;所有基于此overlay
网络的Container都通过此网卡来和此Container通信;
接着我们在Host2上查看Docker网络信息,此时并没有发现有Swarm集群中的Host1上创建的overl-test-1
网络:但是我们此时在Host2上启动Container是可以加入``overl-test-1网络
,如下:
1 2 $docker run -d -it --network overlay-test-1 --name container-host2 centosda1d5347d581be0d404bdaedff93969a92536ae35d40d188e5cbcddcdb837faa
启动成功后,我们会发现Host2上的Docker网卡信息多了一个网卡,如下:
1 2 3 4 5 6 7 8 $docker network ls NETWORK ID NAME DRIVER SCOPE 3c8b727d19cc bridge bridge local 53c59b41d257 docker_gwbridge bridge local 6d6a34280d8e host host local lqchwltv5w1c ingress overlay swarm f87b4f8c44ef none null local vfchbsa266tu overlay-test-1 overlay swarm
且整体的Network Namespace和container-host-1
的分布是一致的,如上图,下面我们在Host2上通过Container Name来ping Host1的容器网络,如下:
1 2 3 4 5 6 7 8 $ docker exec -it container-host2 ping container-host1 PING container-host1 (192.166.2.4) 56(84) bytes of data. 64 bytes from container-host1.overlay-test-1 (192.166.2.4): icmp_seq=1 ttl=64 time=0.437 ms 64 bytes from container-host1.overlay-test-1 (192.166.2.4): icmp_seq=2 ttl=64 time=0.365 ms ^C --- container-host1 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1004ms rtt min/avg/max/mdev = 0.365/0.401/0.437/0.036 ms
我们在container-host1
的Host1上抓取对外的eth0
网卡的VXLAN监听的默认4789端口信息,如下:
1 2 3 4 5 6 7 8 9 10 11 $ tcpdump -i eth0 -nn -v port 4789 tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes 16:34:03.398521 IP (tos 0x0, ttl 64, id 51392, offset 0, flags [none], proto UDP (17), length 134) 10.206.0.12.35998 > 10.206.0.37.4789: VXLAN, flags [I] (0x08), vni 4098 IP (tos 0x0, ttl 64, id 32603, offset 0, flags [DF], proto ICMP (1), length 84) 192.166.2.6 > 192.166.2.4: ICMP echo request, id 8, seq 1, length 64 16:34:03.398706 IP (tos 0x0, ttl 64, id 3343, offset 0, flags [none], proto UDP (17), length 134) 10.206.0.37.46162 > 10.206.0.12.4789: VXLAN, flags [I] (0x08), vni 4098 IP (tos 0x0, ttl 64, id 53624, offset 0, flags [none], proto ICMP (1), length 84) 192.166.2.4 > 192.166.2.6: ICMP echo reply, id 8, seq 1, length 64
然后经过针对上面各个网卡信息的监听,会发现,ICMP流量经过VXLAN的VTEP解封装后,交由1-bv8sznvlx2
Network Namespace的vxlan0
网卡,如下:
1 2 3 4 5 6 $ ip netns exec 1-bv8sznvlx2 tcpdump -i vxlan0 -nn -v tcpdump: listening on vxlan0, link-type EN10MB (Ethernet), capture size 262144 bytes 16:34:03.398646 IP (tos 0x0, ttl 64, id 32603, offset 0, flags [DF], proto ICMP (1), length 84) 192.166.2.6 > 192.166.2.4: ICMP echo request, id 8, seq 1, length 64 16:34:03.398692 IP (tos 0x0, ttl 64, id 53624, offset 0, flags [none], proto ICMP (1), length 84) 192.166.2.4 > 192.166.2.6: ICMP echo reply, id 8, seq 1, length 64
然后流量经过1-bv8sznvlx2
的br0
路由到veth1
,然后直接转发到519973c21f1e
的veth pair
的另一个端:eth0
:
1 2 3 4 5 6 $ ip netns exec 519973c21f1e tcpdump -i eth0 -nn -v tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes 16:34:03.398660 IP (tos 0x0, ttl 64, id 32603, offset 0, flags [DF], proto ICMP (1), length 84) 192.166.2.6 > 192.166.2.4: ICMP echo request, id 8, seq 1, length 64 16:34:03.398688 IP (tos 0x0, ttl 64, id 53624, offset 0, flags [none], proto ICMP (1), length 84) 192.166.2.4 > 192.166.2.6: ICMP echo reply, id 8, seq 1, length 64
Serivces网络 之前我们提过Swarm的Service,Service提供了服务发现、负载均衡和自动伸缩等功能,通过配置副本数、端口映射、环境变量等关键参数来管理服务的状态和行为,下面我们看一下Swarm如何管理Services的:
1 2 3 4 5 6 7 8 9 10 $ docker service create --name service-test-1 \ --replicas 3 \ --publish 8888:80 \ centos ping google.com 7dz61hopc61fbhksde0gnro8h overall progress: 3 out of 3 tasks 1/3: running [==================================================>] 2/3: running [==================================================>] 3/3: running [==================================================>] verify: Service converged
我们看一下Service的三个副本分布如下:Host1上启动了一个副本,Host2上启动了二个副本,
1 2 3 4 5 $ docker service ps service-test-1 ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS ws8tn9zaichj service-test-1.1 centos:latest VM-0-12-ubuntu Running Running 10 minutes ago wk499rom7wy2 service-test-1.2 centos:latest VM-0-37-ubuntu Running Running 10 minutes ago 99xfjf4gg5di service-test-1.3 centos:latest VM-0-12-ubuntu Running Running 10 minutes ago
我们先看一下Host1上的网卡和Network Namespace的变化:
1 2 3 4 5 6 7 8 9 10 11 $ ip addr 2037: veth8245c0d@if2036: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker_gwbridge state UP group default link /ether 96:3b:eb:7e:55:a0 brd ff:ff:ff:ff:ff:ff link-netns 48c24cf61ee4 inet6 fe80::943b:ebff:fe7e:55a0/64 scope link valid_lft forever preferred_lft forever $ ip netns 48c24cf61ee4 (id : 2) 1-lqchwltv5w (id : 0) ingress_sbox (id : 1)
Host2上的网卡和Network Namespace的变化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 $ ip addr 1275: veth41c2673@if1274: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker_gwbridge state UP group default link /ether 3a:e6:71:37:07:c2 brd ff:ff:ff:ff:ff:ff link-netns bbfe3164ce2a inet6 fe80::38e6:71ff:fe37:7c2/64 scope link valid_lft forever preferred_lft forever 1279: veth0f16c1f@if1278: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker_gwbridge state UP group default link /ether 9a:9e:19:fe:9b:8c brd ff:ff:ff:ff:ff:ff link-netns 8181d9bbc1f4 inet6 fe80::989e:19ff:fefe:9b8c/64 scope link valid_lft forever preferred_lft forever $ ip netns bbfe3164ce2a (id : 2) 8181d9bbc1f4 (id : 3) ingress_sbox (id : 1) 1-lqchwltv5w (id : 0)
下面我们看一下Host1的Network Namespace及其网卡的结构图如下:
ingress-sbox
Network Namespace:为了Swarm Serivce提供了服务发现 ,流量路由 和负载均衡 ,主要通过内核IPVS
模块进行流量转发,IPVS
模块会根据目标Service配置的路由算法,选择其中一个IP,通过ingress
网络交由到对应的Node上。
1-lqchwltv5w
Network Namespace:提供了VXLAN基于三层网络的内网通信能力,所有内网流量都经过此Namespace的网卡,要么经过VXLAN进行跨机转发,要么通过bridge转发至本机的Container。
48c24cf61ee4
Network Namespace:新创建的Service在本机的一个副本即一个Container,链接了主机和1-lqchwltv5w
两个Network Namespace。
知道了上面的整体结构后,我们再看一下Host1上主机Network Namespace的iptables
的变化,如下nat
表的Chain和Rule变化:
我们看到PREROUTING 链和OUTPUT链都有自定义链DOCKER-INGRESS,用来过滤进入本机(入站路由前 )的数据和出站(出站路由前 )数据,匹配目标地址类型为 LOCAL 的所有协议数据包,且本地目标端口为8888
的数据包,全部转发到172.18.0.2:8888
。
如下是filter
表的Chain和Rule变化:
我们看FORWARD
链(不是发给本机的数据包,需要通过本机进行路由转发的数据包)的自定义链DOCKER-INGRESS
链的规则如下:
允许入站且目标端口是8888
端口流量;
允许已建立的、且来源的端口是 8888
端口的 TCP 连接;
1 $ ip netns exec ingress_sbox iptables -nvL -t mangle | cut -f -9|column -t| sed 's/^Chain/\n&/g' |sed '/^Chain/ s/[ \t]\{1,\}/ /g' |sed '/^[0-9]/ s/[ \t]\{1,\}/ /10g' |sed -e 's/\(192.166.0\|8888\)/\x1b[31m&\x1b[0m/g'
再看一下Host1上ingress_sbox
的Network Namespace的iptables
信息,如下nat
表的Chain和Rule变化:
从上面的POSTROUTING链的SNAT规则可以得知,最终到Serivce对外端口的流量信息会通过IPVS模块进行SNAT转换后转发到192.166.0.2
(1-lqchwltv5w
Network Namespace),我们看一下IPVS的路由规则信息如下:
1 2 3 4 5 6 7 8 $ ip netns exec ingress_sbox ipvsadm --list --numeric IP Virtual Server version 1.2.1 (size=4096) Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn FWM 774 rr -> 192.166.0.65:0 Masq 1 0 0 -> 192.166.0.66:0 Masq 1 0 0 -> 192.166.0.67:0 Masq 1 0 0
我们知道IPVS模块选择一个负载后,会交给1-lqchwltv5w
Network Namespace,上面我们介绍了此NS提供了VXLAN基于三层网络的内网通信能力,所有内网流量都经过此Namespace的网卡,要么经过VXLAN进行跨机转发,要么通过bridge转发至本机的Container。
那VXLAN是如何通过overlay
网络IP找到对应的主机的呢?
VXLAN 使用了一种封装机制,结合了ARP 和 FDB(Forwarding Database)表的功能来实现跨主机的通信 。ARP 只解析内部 IP 到内部 MAC 地址,VXLAN 依赖 FDB 的额外信息来实现从内部 MAC 到远端主机(VTEP IP)的映射。
如下是1-lqchwltv5w Network Namespace的ARP
表和FDB表的数据:
1 2 3 4 5 6 7 8 9 10 11 12 $ ip netns exec 1-lqchwltv5w arp Address HWtype HWaddress Flags Mask Iface 192.166.0.65 ether 02:42:c0:a6:00:41 CM vxlan0 192.166.0.3 ether 02:42:c0:a6:00:03 CM vxlan0 192.166.0.67 ether 02:42:c0:a6:00:43 CM vxlan0 $ ip netns exec 1-lqchwltv5w bridge fdb show dev vxlan0 fe:40:0b:1d:01:3c master br0 permanent 02:42:c0:a6:00:41 dst 10.206.0.12 link-netnsid 0 self permanent 02:42:c0:a6:00:03 dst 10.206.0.12 link-netnsid 0 self permanent 02:42:c0:a6:00:43 dst 10.206.0.12 link-netnsid 0 self permanent
启动的Service的Container的Network Namespace的iptables信息如下:
如下是整个Docker Service网络的拓扑结构,包含了数据流入后,如何通过主机网络流入Service的ingress_sbox
的Namespace,然后通过IPVS模块进行路由选择,并最终通过OVERLAY网络进行流量转发的示意图:
总结 本文主要介绍了docker的bridge
和overlay
网络相关基础,其中了解了Linux内核如何进行相关网络设备的支持。主要有两个方面的收获:
同主机之间,容器是如何通过bridge
和iptables进行网络隔离,跨容器又是如何通过bridge
和veth
进行打通的。
跨主机之间,容器是如何借助现有的underlay的物理网络,通过VXLAN技术构建overlay的网络通信能力。
本文主要是研究Docker的network基础,希望通过这个学习,对后面深入浅出Kubernetes的网络有基础的铺垫。
参考 https://ammarsuhail155.medium.com/kubernetes-networking-798c06b2a3ca
https://network-insight.net/2016/03/19/kubernetes-network-namespace/
https://logingood.github.io/kubernetes/cni/2016/05/14/netns-and-cni.html
https://labs.iximiuz.com/challenges/connect-multiple-network-namespaces
https://github.com/nehalineogi/azure-cross-solution-network-architectures/blob/main/advanced-linux-networking/linux-vxlan.md
https://skminhaj.wordpress.com/2016/02/15/vxlan-encapsulation-and-packet-format/
https://ops.tips/blog/blocking-ingress-traffic-to-docker-swarm-worker-machines/
https://docker-tutorial.schoolofdevops.com/swarm-networking-deepdive/
https://www.suse.com/c/docker-swarm-container-networking/
https://stackoverflow.com/questions/42804541/docker-swarm-how-to-inspect-ingress-endpoint-container
https://docs.docker.com/engine/swarm/ingress/