Bridge: 这是启动容器的默认网络。通过docker主机上的网桥接口实现连接。 使用相同网桥的容器有自己的子网,并且可以相互通信(默认情况下)。 Host:这个驱动程序允许容器访问docker主机自己的网络空间(容器将看到和使用与docker主机相同的接口)。 Macvlan:此驱动程序允许容器直接访问主机的接口或子接口(vlan)。 它还允许中继链接。 Overlay:此驱动程序允许在运行docker的多个主机(通常是docker群集群)上构建网络。 容器还具有自己的子网和网络地址,并且可以直接相互通信,即使它们在不同的物理主机上运行。
Bridge和Overlay可能是最常用的网络驱动程序,在本文和下一篇文章中我将主要关注这两个驱动程序。 Docker Bridge 网络在docker主机上运行的容器的默认网络是。 Docker在首次安装时创建一个名为“bridge”的默认网络。 我们可以列出所有docker网络来查看此网络 docker network ls:$ docker network lsNETWORK ID NAME DRIVER SCOPE3e8110efa04a bridge bridge localbb3cd79b9236 docker_gwbridge bridge local22849c4d1c3a host host local3kuba8yq3c27 ingress overlay swarmecbd1c6c193a none null local要检查其属性,运行docker network inspect bridge$ docker network inspect bridge[ { <font color="red">"Name": "bridge",</font> "Id": "3e8110efa04a1eb0923d863af719abf5eac871dbac4ae74f133894b8df4b9f5f", "Scope": "local", <font color="red">"Driver": "bridge",</font> "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": null, "Config": [ { <font color="red">"Subnet": "172.18.0.0/16",</font> "Gateway": "172.18.0.1" } ] }, "Internal": 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": {} }]你还可以使用docker network create命令并指定选项--driver bridge创建自己的网络,例如
docker network create --driver bridge --subnet 192.168.100.0/24 --ip-range 192.168.100.0/ 24 my-bridge-network创建另一个网桥网络,名称为“my-bridge-network”,子网为192.168.100.0/24。 Linux 网桥接口docker创建的每个网桥网络由docker主机上的网桥接口呈现。、 默认桥网络“bridge”通常具有与其相关联的接口docker0,并且使用docker network create命令创建的每个后续网桥网络将具有与其相关联的新接口。$ ifconfig docker0docker0 Link encap:Ethernet HWaddr 02:42:44:88:bd:75 inet addr:172.18.0.1 Bcast:0.0.0.0 Mask:255.255.0.0 UP BROADCAST MULTICAST MTU:1500 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)要找到与你创建的docker网络关联的linux接口,可以使用ifconfig列出所有接口,然后找到你指定了子网的接口,例如,我们想查看我们之前创建的网桥接口my-bridge-network我们可以这样:$ ifconfig | grep 192.168.100. -B 1<font color="red">br-e6bc7d6b75f3</font> Link encap:Ethernet HWaddr 02:42:bc:f1:91:09 inet addr:192.168.100.1 Bcast:0.0.0.0 Mask:255.255.255.0linux桥接接口与交换机的功能类似,因为它们将不同的接口连接到同一子网,并根据MAC地址转发流量。 我们将在下面看到,连接到网桥网络的每个容器将在docker主机上创建自己的虚拟接口,并且docker引擎将同一网络中的所有容器连接到同一个网桥接口,这将允许它们与彼此进行通信。 您可以使用brctl获取有关网桥状态的更多详细信息。$ brctl show docker0bridge name bridge id STP enabled interfacesdocker0 8000.02424488bd75 no一旦我们有容器运行并连接到这个网络,我们将看到interfaces列下面列出的每个容器的接口。 并且在桥接器接口上运行流量捕获将允许我们看到同一子网上的容器之间的相互通信。 Linux 虚拟网络接口(veth)容器网络模型(CNM)允许每个容器具有其自己的网络空间。 从容器内部运行ifconfig将显示容器内部的网络接口:$ docker run -ti ubuntu:14.04 /bin/bashroot@6622112b507c:/#root@6622112b507c:/# ifconfigeth0 Link encap:Ethernet HWaddr 02:42:ac:12:00:02 inet addr:172.18.0.2 Bcast:0.0.0.0 Mask:255.255.0.0 inet6 addr: fe80::42:acff:fe12:2/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:9 errors:0 dropped:0 overruns:0 frame:0 TX packets:6 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:766 (766.0 B) TX bytes:508 (508.0 B)lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host 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)然而,上面看到的eth0只能从那个容器中可用,而在Docker主机的外部,docker会创建一个与其对应的双虚拟接口,并作为到容器外的链接。 这些虚拟接口连接到上面讨论的桥接器接口,以便于在同一子网上的不同容器之间的连接。
我们可以通过启动连接到默认网桥的两个容器来查看此过程,然后查看docker主机上的接口配置。
在运行启动任何容器之前,docker0 桥接接口没有连接的接口:$ sudo brctl show docker0bridge name bridge id STP enabled <font color="red">interfaces</font>docker0 8000.02424488bd75 no然后我从ubuntu:14.04 镜像启动2个容器$ docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESa754719db594 ubuntu:14.04 "/bin/bash" 5 seconds ago Up 4 seconds zen_kalam976041ec420f ubuntu:14.04 "/bin/bash" 7 seconds ago Up 5 seconds stupefied_easley您能马上看到现在有两个接口连接到docker0网桥接口(每个容器一个)$ sudo brctl show docker0bridge name bridge id STP enabled <font color="red">interfaces</font>docker0 8000.02424488bd75 no <font color="red">veth2177159</font> <font color="red">vethd8e05dd</font>从其中一个容器ping到google,然后从docker主机对容器的虚拟接口进行流量捕获,将显示容器流量$ docker exec a754719db594 ping google.comPING google.com (216.58.217.110) 56(84) bytes of data.64 bytes from iad23s42-in-f110.1e100.net (216.58.217.110): icmp_seq=1 ttl=48 time=0.849 ms64 bytes from iad23s42-in-f110.1e100.net (216.58.217.110): icmp_seq=2 ttl=48 time=0.965 msubuntu@swarm02:~$ sudo tcpdump -i <font color="red">veth2177159</font> icmptcpdump: verbose output suppressed, use -v or -vv for full protocol decodelistening on veth2177159, link-type EN10MB (Ethernet), capture size 262144 bytes20:47:12.170815 IP 172.18.0.3 > iad23s42-in-f14.1e100.net: ICMP echo request, id 14, seq 55, length 6420:47:12.171654 IP iad23s42-in-f14.1e100.net > 172.18.0.3: ICMP echo reply, id 14, seq 55, length 6420:47:13.170821 IP 172.18.0.3 > iad23s42-in-f14.1e100.net: ICMP echo request, id 14, seq 56, length 6420:47:13.171694 IP iad23s42-in-f14.1e100.net > 172.18.0.3: ICMP echo reply, id 14, seq 56, length 64同样,我们可以从一个容器平到另一个容器。
首先,我们需要获取容器的IP地址,这可以通过在容器中运行ifconfig或使用docker inspect命令检查容器来完成:$ docker inspect -f &#x27;{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}&#x27; a754719db594 <font color="red">172.18.0.3</font>然后我们从一个容器ping另一个容器$ docker exec 976041ec420f ping 172.18.0.3PING 172.18.0.3 (172.18.0.3) 56(84) bytes of data.64 bytes from 172.18.0.3: icmp_seq=1 ttl=64 time=0.070 ms64 bytes from 172.18.0.3: icmp_seq=2 ttl=64 time=0.053 ms要从docker主机看到这个流量,我们可以在对应于容器的任何一个虚拟接口上捕获,或者我们可以在桥接口(在这个实例中为docker0)上捕获,显示所有的容器间通信子网:$ sudo tcpdump -ni <font color="red">docker0</font> host 172.18.0.2 and host 172.18.0.3tcpdump: verbose output suppressed, use -v or -vv for full protocol decodelistening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes20:55:37.990831 IP 172.18.0.2 > 172.18.0.3: ICMP echo request, id 14, seq 200, length 6420:55:37.990865 IP 172.18.0.3 > 172.18.0.2: ICMP echo reply, id 14, seq 200, length 6420:55:38.990828 IP 172.18.0.2 > 172.18.0.3: ICMP echo request, id 14, seq 201, length 6420:55:38.990866 IP 172.18.0.3 > 172.18.0.2: ICMP echo reply, id 14, seq 201, length 64 定位一个容器的vet接口没有直接的方法来找到docker主机上的哪个veth接口链接到容器内的接口,但是在各种docker论坛和github中讨论了几种方法。在我看来最简单的是以下,这也取决于ethtool在容器中可访问
例如:我的系统上运行了3个容器$ docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<font color="red">ccbf97c72bf5</font> ubuntu:14.04 "/bin/bash" 3 seconds ago Up 3 seconds admiring_torvalds<font color="red">77d9f02d61f2</font> ubuntu:14.04 "/bin/bash" 4 seconds ago Up 4 seconds goofy_borg<font color="red">19743c0ddf24</font> ubuntu:14.04 "/bin/sh" 8 minutes ago Up 8 minutes high_engelbart首先我运行如下命令来获得peer_ifindex号$ docker exec 77d9f02d61f2 sudo ethtool -S eth0NIC statistics: peer_ifindex: <font color="red">16</font>然后在docker主机上,通过peer_ifindex找到接口名称$ sudo ip link | grep <font color="red">16</font>16: <font color="red">veth7bd3604</font>@if15: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default所以,在目前的情况下,接口名称是:veth7bd3604 iptablesDocker使用linux iptables来控制与它创建的接口和网络之间的通信。 Linux iptables由不同的表组成,但我们主要关注两个:filter和nat。过滤器是网络或接口的流量的安全规则表,用于允许或拒绝IP地址,而nat包含负责屏蔽IP地址或端口的规则。Docker使用nat允许桥接网络上的容器与docker主机之外的目的地进行通信(否则指向容器网络的路由必须在docker主机的网络中添加)
iptables:filter
iptables中的表由对应于处理docker主机上的数据包的不同条件或阶段的不同链组成。默认情况下,过滤器表具有3个链:用于处理到达主机并且去往同一主机的分组的输入链,用于发送到外部目的地的主机的分组的输出链,以及用于进入主机但具有目的地外部主机。每个链由一些规则组成,这些规则规定对分组采取一些措施(例如拒绝或接受分组)以及匹配规则的条件。 顺序处理规则,直到找到匹配项,否则应用链的默认策略。 也可以在表中定义自定义链。
要查看过滤器表中链的当前配置的规则和默认策略,可以运行iptables -t filter -L(或iptables -L,如果未指定表,则默认使用过滤器表)$ sudo iptables -t filter -L<font color="red">Chain INPUT (policy ACCEPT)</font>target prot opt source destinationACCEPT tcp -- anywhere anywhere tcp dpt:domainACCEPT udp -- anywhere anywhere udp dpt:domainACCEPT tcp -- anywhere anywhere tcp dpt:bootpsACCEPT udp -- anywhere anywhere udp dpt:bootps<font color="red">Chain FORWARD (policy ACCEPT)</font>target prot opt source destinationDOCKER-ISOLATION all -- anywhere anywhereDOCKER all -- anywhere anywhereACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHEDACCEPT all -- anywhere anywhereACCEPT all -- anywhere anywhereDOCKER all -- anywhere anywhereACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHEDACCEPT all -- anywhere anywhereACCEPT all -- anywhere anywhereDOCKER all -- anywhere anywhereACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHEDACCEPT all -- anywhere anywhereACCEPT all -- anywhere anywhereACCEPT all -- anywhere anywhereDROP all -- anywhere anywhere<font color="red">Chain OUTPUT</font> (policy ACCEPT)target prot opt source destination<font color="red">Chain DOCKER</font> (3 references)target prot opt source destination<font color="red">Chain DOCKER-ISOLATION</font> (1 references)target prot opt source destinationDROP all -- anywhere anywhereDROP all -- anywhere anywhereDROP all -- anywhere anywhereDROP all -- anywhere anywhereDROP all -- anywhere anywhereDROP all -- anywhere anywhereRETURN all -- anywhere anywhere突出显示的是不同的链,以及每个链的默认策略(没有自定义链的默认策略)。 我们还可以看到Docker已经添加了两个自定义链:Docker和Docker-Isolation,并且在Forward链中插入了以这两个新链作为目标的规则。
Docker-isolation chain
Docker-isolation包含限制不同容器网络之间的访问的规则。 要查看更多详细信息,请在运行iptables时使用-v选项$ sudo iptables -t filter -L -v….Chain <font color="red">DOCKER-ISOLATION</font> (1 references)pkts bytes target prot opt in out source destination 0 0 DROP all -- br-e6bc7d6b75f3 docker0 anywhere anywhere 0 0 DROP all -- docker0 br-e6bc7d6b75f3 anywhere anywhere 0 0 DROP all -- docker_gwbridge docker0 anywhere anywhere 0 0 DROP all -- docker0 docker_gwbridge anywhere anywhere 0 0 DROP all -- docker_gwbridge br-e6bc7d6b75f3 anywhere anywhere 0 0 DROP all -- br-e6bc7d6b75f3 docker_gwbridge anywhere anywhere36991 3107K RETURN all -- any any anywhere anywhere您可以在上面看到一些删除规则,阻止任何由docker创建的桥接接口之间的流量,从而确保容器网络不能通信。