1. 云栖社区>
  2. PHP教程>
  3. 正文

Docker 网络构造:Docker如何使用Linux iptables 和 Interfaces

作者:用户 来源:互联网 时间:2017-12-01 09:34:41

linux网络iptablesdocker使用构造如何

Docker 网络构造:Docker如何使用Linux iptables 和 Interfaces - 摘要: 本文讲的是Docker 网络构造:Docker如何使用Linux iptables 和 Interfaces, #Docker 网络构造:Docker如何使用Linux iptables 和 Interfaces我使用docker至今已有一段时间了,与绝大部分的人一样,我被docker强大的功能和易用性深深的折服。简单方便是docker的核心之一,



#Docker 网络构造:Docker如何使用Linux iptables 和 Interfaces我使用docker至今已有一段时间了,与绝大部分的人一样,我被docker强大的功能和易用性深深的折服。简单方便是docker的核心之一,它强大的功能被抽象成了非常简单的命令。当我在使用和学习docker的时候,我很想知道docker在后台都做了一些什么事情,特别是在网络这一块(我最感兴趣的一块)我找到了很多关于创建和操作容器网络的文档,但是关于docker如何使网络工作的却没有那么多。 Docker广泛使用linux iptables和网桥接口,这篇文章是我如何用于创建容器网络的总结,大部分信息来自github上的讨论,演示文稿,以及我自己的测试。文章结尾我会给出我认为非常有用的资料链接。
我写这篇文章使用的是docker 1.12.3,但这不是作为对docker网络的全面描述,也不作为docker网络的介绍。我只希望这篇文章能给大家开拓视野,也非常感谢所有对文章错误,缺失的反馈和批评。
###Docker网络概览
Docker的网络建立在允许任何一方编写自己的网络驱动程序的容器网络模型(CNM)之上。这允许不同的网络类型可用于在docker引擎上运行的容器,并且容器可以同时连接到多个网络。除了各种第三方网络驱动程序可用,docker自带四个内置网络驱动程序:
* **Bridge**: 这是启动容器的默认网络。通过docker主机上的网桥接口实现连接。 使用相同网桥的容器有自己的子网,并且可以相互通信(默认情况下)。
* **Host**:这个驱动程序允许容器访问docker主机自己的网络空间(容器将看到和使用与docker主机相同的接口)。
* **Macvlan**:此驱动程序允许容器直接访问主机的接口或子接口(vlan)。 它还允许中继链接。
* **Overlay**:此驱动程序允许在运行docker的多个主机(通常是docker群集群)上构建网络。 容器还具有自己的子网和网络地址,并且可以直接相互通信,即使它们在不同的物理主机上运行。Bridge和Overlay可能是最常用的网络驱动程序,在本文和下一篇文章中我将主要关注这两个驱动程序。
####Docker Bridge 网络
在docker主机上运行的容器的默认网络是。 Docker在首次安装时创建一个名为“bridge”的默认网络。 我们可以列出所有docker网络来查看此网络 `docker network ls`:
```bash
$ docker network ls
NETWORK ID NAMEDRIVERSCOPE
3e8110efa04a bridgebridgelocal
bb3cd79b9236 docker_gwbridge bridgelocal
22849c4d1c3a hosthostlocal
3kuba8yq3c27 ingressoverlayswarm
ecbd1c6c193a nonenulllocal
```要检查其属性,运行`docker network inspect bridge`
```bash
$ docker network inspect bridge
[
{
"Name": "bridge",
"Id": "3e8110efa04a1eb0923d863af719abf5eac871dbac4ae74f133894b8df4b9f5f",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.18.0.0/16",
"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命令创建的每个后续网桥网络将具有与其相关联的新接口。
```bash
$ ifconfig docker0
docker0 Link encap:EthernetHWaddr 02:42:44:88:bd:75
inet addr:172.18.0.1Bcast:0.0.0.0Mask:255.255.0.0
UP BROADCAST MULTICASTMTU:1500Metric: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 `我们可以这样:
```bash
$ ifconfig | grep 192.168.100. -B 1
br-e6bc7d6b75f3 Link encap:EthernetHWaddr 02:42:bc:f1:91:09
inet addr:192.168.100.1Bcast:0.0.0.0Mask:255.255.255.0
```
linux桥接接口与交换机的功能类似,因为它们将不同的接口连接到同一子网,并根据MAC地址转发流量。 我们将在下面看到,连接到网桥网络的每个容器将在docker主机上创建自己的虚拟接口,并且docker引擎将同一网络中的所有容器连接到同一个网桥接口,这将允许它们与彼此进行通信。 您可以使用`brctl`获取有关网桥状态的更多详细信息。
```bash
$ brctl show docker0
bridge name bridge id STP enabled interfaces
docker08000.02424488bd75no
```
一旦我们有容器运行并连接到这个网络,我们将看到interfaces列下面列出的每个容器的接口。 并且在桥接器接口上运行流量捕获将允许我们看到同一子网上的容器之间的相互通信。
####Linux 虚拟网络接口(veth)
容器网络模型(CNM)允许每个容器具有其自己的网络空间。 从容器内部运行`ifconfig`将显示容器内部的网络接口:
```bash
$ docker run -ti ubuntu:14.04 /bin/bash
[email protected]:/#
[email protected]:/# ifconfig
eth0Link encap:EthernetHWaddr 02:42:ac:12:00:02
inet addr:172.18.0.2Bcast:0.0.0.0Mask:255.255.0.0
inet6 addr: fe80::42:acff:fe12:2/64 Scope:Link
UP BROADCAST RUNNING MULTICASTMTU:1500Metric: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.1Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNINGMTU:65536Metric: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 桥接接口没有连接的接口:
```bash
$ sudo brctl show docker0
bridge name bridge id STP enabled interfaces
docker08000.02424488bd75no
```
然后我从`ubuntu:14.04` 镜像启动2个容器
```bash
$ docker ps
CONTAINER ID IMAGE COMMANDCREATEDSTATUSPORTS NAMES
a754719db594 ubuntu:14.04 "/bin/bash"5 seconds agoUp 4 secondszen_kalam
976041ec420f ubuntu:14.04 "/bin/bash"7 seconds agoUp 5 secondsstupefied_easley
```
您能马上看到现在有两个接口连接到docker0网桥接口(每个容器一个)
```bash
$ sudo brctl show docker0
bridge name bridge id STP enabled interfaces
docker08000.02424488bd75noveth2177159
vethd8e05dd
```
从其中一个容器ping到google,然后从docker主机对容器的虚拟接口进行流量捕获,将显示容器流量
```bash
$ docker exec a754719db594 ping google.com
PING 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 ms
64 bytes from iad23s42-in-f110.1e100.net (216.58.217.110): icmp_seq=2 ttl=48 time=0.965 ms
[email protected]:~$ sudo tcpdump -i veth2177159 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on veth2177159, link-type EN10MB (Ethernet), capture size 262144 bytes
20:47:12.170815 IP 172.18.0.3 > iad23s42-in-f14.1e100.net: ICMP echo request, id 14, seq 55, length 64
20:47:12.171654 IP iad23s42-in-f14.1e100.net > 172.18.0.3: ICMP echo reply, id 14, seq 55, length 64
20:47:13.170821 IP 172.18.0.3 > iad23s42-in-f14.1e100.net: ICMP echo request, id 14, seq 56, length 64
20: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`命令检查容器来完成:
```bash
$ docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' a754719db594
172.18.0.3
```
然后我们从一个容器ping另一个容器
```bash
$ docker exec 976041ec420f ping 172.18.0.3
PING 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 ms
64 bytes from 172.18.0.3: icmp_seq=2 ttl=64 time=0.053 ms
```
要从docker主机看到这个流量,我们可以在对应于容器的任何一个虚拟接口上捕获,或者我们可以在桥接口(在这个实例中为docker0)上捕获,显示所有的容器间通信子网:
```bash
$ sudo tcpdump -ni docker0 host 172.18.0.2 and host 172.18.0.3
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes
20:55:37.990831 IP 172.18.0.2 > 172.18.0.3: ICMP echo request, id 14, seq 200, length 64
20:55:37.990865 IP 172.18.0.3 > 172.18.0.2: ICMP echo reply, id 14, seq 200, length 64
20:55:38.990828 IP 172.18.0.2 > 172.18.0.3: ICMP echo request, id 14, seq 201, length 64
20: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中讨论了几种方法。在我看来最简单的是以下(基于[这个](https://github.com/docker/docker/issues/20224)解决方案做了稍微的修改),这也取决于`ethtool`在容器中可访问
例如:我的系统上运行了3个容器
```bash
$ docker ps
CONTAINER ID IMAGE COMMANDCREATEDSTATUSPORTS NAMES
ccbf97c72bf5 ubuntu:14.04 "/bin/bash"3 seconds agoUp 3 secondsadmiring_torvalds
77d9f02d61f2 ubuntu:14.04 "/bin/bash"4 seconds agoUp 4 secondsgoofy_borg
19743c0ddf24 ubuntu:14.04 "/bin/sh"8 minutes agoUp 8 minuteshigh_engelbart
```
首先我运行如下命令来获得`peer_ifindex `号
```bash
$ docker exec 77d9f02d61f2 sudo ethtool -S eth0
NIC statistics:
peer_ifindex: 16
```
然后在docker主机上,通过`peer_ifindex `找到接口名称
```bash
$ sudo ip link | grep 16
16: veth7bd3604@if15: mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default
```
所以,在目前的情况下,接口名称是:`veth7bd3604`
##### iptables
Docker使用linux iptables来控制与它创建的接口和网络之间的通信。 Linux iptables由不同的表组成,但我们主要关注两个:`filter`和`nat`。过滤器是网络或接口的流量的安全规则表,用于允许或拒绝IP地址,而nat包含负责屏蔽IP地址或端口的规则。Docker使用nat允许桥接网络上的容器与docker主机之外的目的地进行通信(否则指向容器网络的路由必须在docker主机的网络中添加)
**iptables:filter**
iptables中的表由对应于处理docker主机上的数据包的不同条件或阶段的不同链组成。默认情况下,过滤器表具有3个链:用于处理到达主机并且去往同一主机的分组的输入链,用于发送到外部目的地的主机的分组的输出链,以及用于进入主机但具有目的地外部主机。每个链由一些规则组成,这些规则规定对分组采取一些措施(例如拒绝或接受分组)以及匹配规则的条件。 顺序处理规则,直到找到匹配项,否则应用链的默认策略。 也可以在表中定义自定义链。
要查看过滤器表中链的当前配置的规则和默认策略,可以运行`iptables -t filter -L`(或`iptables -L`,如果未指定表,则默认使用过滤器表)
```bash
$ sudo iptables -t filter -L
Chain INPUT (policy ACCEPT)
target prot opt source destination
ACCEPT tcp--anywhereanywheretcp dpt:domain
ACCEPT udp--anywhereanywhereudp dpt:domain
ACCEPT tcp--anywhereanywheretcp dpt:bootps
ACCEPT udp--anywhereanywhereudp dpt:bootps
Chain FORWARD (policy ACCEPT)
target prot opt source destination
DOCKER-ISOLATIONall--anywhereanywhere
DOCKER all--anywhereanywhere
ACCEPT all--anywhereanywherectstate RELATED,ESTABLISHED
ACCEPT all--anywhereanywhere
ACCEPT all--anywhereanywhere
DOCKER all--anywhereanywhere
ACCEPT all--anywhereanywherectstate RELATED,ESTABLISHED
ACCEPT all--anywhereanywhere
ACCEPT all--anywhereanywhere
DOCKER all--anywhereanywhere
ACCEPT all--anywhereanywherectstate RELATED,ESTABLISHED
ACCEPT all--anywhereanywhere
ACCEPT all--anywhereanywhere
ACCEPT all--anywhereanywhere
DROPall--anywhereanywhere
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
Chain DOCKER (3 references)
target prot opt source destination
Chain DOCKER-ISOLATION (1 references)
target prot opt source destination
DROPall--anywhereanywhere
DROPall--anywhereanywhere
DROPall--anywhereanywhere
DROPall--anywhereanywhere
DROPall--anywhereanywhere
DROPall--anywhereanywhere
RETURN all--anywhereanywhere
```
突出显示的是不同的链,以及每个链的默认策略(没有自定义链的默认策略)。 我们还可以看到Docker已经添加了两个自定义链:`Docker`和`Docker-Isolation`,并且在`Forward`链中插入了以这两个新链作为目标的规则。
***Docker-isolation chain***
`Docker-isolation`包含限制不同容器网络之间的访问的规则。 要查看更多详细信息,请在运行`iptables`时使用`-v`选项
```bash
$ sudo iptables -t filter -L -v
….
Chain DOCKER-ISOLATION (1 references)
pkts bytes target prot opt in out source destination
0 0 DROPall--br-e6bc7d6b75f3 docker0anywhereanywhere
0 0 DROPall--docker0 br-e6bc7d6b75f3anywhereanywhere
0 0 DROPall--docker_gwbridge docker0anywhereanywhere
0 0 DROPall--docker0 docker_gwbridgeanywhereanywhere
0 0 DROPall--docker_gwbridge br-e6bc7d6b75f3anywhereanywhere
0 0 DROPall--br-e6bc7d6b75f3 docker_gwbridgeanywhereanywhere
36991 3107K RETURN all--anyany anywhereanywhere
```
您可以在上面看到一些删除规则,阻止任何由docker创建的桥接接口之间的流量,从而确保容器网络不能通信。
***icc=false***
可以传递到`docker network create`命令的选项之一是`com.docker.network.bridge.enable_icc`,它代表容器间通信。 将此选项设置为`false`会阻止同一网络上的容器彼此通信。 这是通过在前向链中添加一个丢弃规则来实现的,该丢弃规则匹配来自与去往同一接口的网络相关联的桥接器接口的分组。
举个例子,我们用以下命令创建一个新的网络
```bash
docker network create --driver bridge --subnet 192.168.200.0/24 --ip-range 192.168.200.0/24 -o "com.docker.network.bridge.enable_icc"="false" no-icc-network
```
```bash
$ ifconfig | grep 192.168.200 -B 1
br-8e3f0d353353 Link encap:EthernetHWaddr 02:42:c4:6b:f1:40
inet addr:192.168.200.1Bcast:0.0.0.0Mask:255.255.255.0
$ sudo iptables -t filter -S FORWARD
-P FORWARD ACCEPT
-A FORWARD -j DOCKER-ISOLATION
-A FORWARD -o br-8e3f0d353353 -j DOCKER
-A FORWARD -o br-8e3f0d353353 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -i br-8e3f0d353353 ! -o br-8e3f0d353353 -j ACCEPT
-A FORWARD -o docker0 -j DOCKER
-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -i docker0 ! -o docker0 -j ACCEPT
-A FORWARD -i docker0 -o docker0 -j ACCEPT
-A FORWARD -o br-e6bc7d6b75f3 -j DOCKER
-A FORWARD -o br-e6bc7d6b75f3 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -i br-e6bc7d6b75f3 ! -o br-e6bc7d6b75f3 -j ACCEPT
-A FORWARD -i br-e6bc7d6b75f3 -o br-e6bc7d6b75f3 -j ACCEPT
-A FORWARD -o docker_gwbridge -j DOCKER
-A FORWARD -o docker_gwbridge -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -i docker_gwbridge ! -o docker_gwbridge -j ACCEPT
-A FORWARD -o lxcbr0 -j ACCEPT
-A FORWARD -i lxcbr0 -j ACCEPT
-A FORWARD -i docker_gwbridge -o docker_gwbridge -j DROP
-A FORWARD -i br-8e3f0d353353 -o br-8e3f0d353353 -j DROP
```
**iptables:nat**
`NAT`允许主机更改数据包的IP地址或端口。在这种情况下,它用于屏蔽源IP地址来自docker网络(例如172.18.0.0/24子网中的主机),目的地为容器外,位于docker主机的IP地址之后的数据包。此功能由`com.docker.network.bridge.enable_ip_masquerade`选项控制,可以在`docker network create`(如果未指定,则默认为true)命令中使用。
你可以在iptables的nat表中看到此命令的效果
```bash
$ sudo iptables -t nat -L
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
DOCKER all--anywhereanywhereADDRTYPE match dst-type LOCALChain INPUT (policy ACCEPT)
target prot opt source destinationChain OUTPUT (policy ACCEPT)
target prot opt source destination
DOCKER all--anywhere !127.0.0.0/8 ADDRTYPE match dst-type LOCALChain POSTROUTING (policy ACCEPT)
target prot opt source destination
MASQUERADEall--172.18.0.0/16 anywhere
MASQUERADEall--192.168.100.0/24 anywhere
MASQUERADEall--172.19.0.0/16 anywhere
MASQUERADEall--10.0.3.0/24!10.0.3.0/24
Chain DOCKER (2 references)
target prot opt source destination
RETURN all--anywhereanywhere
RETURN all--anywhereanywhere
RETURN all--anywhereanywhere
```
在`postrouting`链中,您可以看到在与自己网络外部的任何主机通信时,通过应用伪装操作创建的所有docker网络。
###总结
* 网桥网络在docker主机上具有对应的linux网桥接口,其作为layer2交换机,并且连接在同一子网上的不同容器。
* 容器中的每个网络接口在Docker主机上具有在容器运行时创建的对应虚拟接口。
* 桥接接口上来自Docker主机的流量捕获等效于在交换机上配置SPAN端口,可以在该网络上查看所有集群间通信。
* 在虚拟接口(veth- *)上来自docker主机的流量捕获将显示容器在特定子网上发送的所有流量
* Linux iptables规则用于阻止不同的网络(有时网络中的主机)使用过滤器表进行通信。 这些规则通常添加在`DOCKER-ISOLATION`链中。
* 容器通过桥接接口与外部通信,其IP被隐藏在docker主机的IP地址后面。 这是通过向`iptables`中的`nat`表添加规则来实现的。
###相关连接
[**Docker networking concepts**](https://github.com/docker/labs/tree/master/networking/concepts)
[**Deep dive into Docker 1.12 Networking**](https://www.youtube.com/watch?v=nXaF2d97JnE)
[**Docker container networking user guide**](https://docs.docker.com/engine/userguide/networking/)
[**Linux iptables overview**](https://wiki.archlinux.org/index.php/Iptables#Tables)原文:[docker-networking-internals-how-docker](http://securitynik.blogspot.ca/2016/12/docker-networking-internals-how-docker_16.html)
【云盟认证成员】: **超儿哥** 译

以上是云栖社区小编为您精心准备的的内容,在云栖社区的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索linux , 网络 , iptables , docker , 使用 , 构造 , 如何 ,以便于您获取更多的相关知识。