乐趣区

关于docker:Docker容器网络基础篇

_链接:https://www.cnblogs.com/sally…
作者:Mr_Zack_

Docker 的技术依赖于 Linux 内核的虚拟化技术的倒退,Docker 应用到的网络技术有 Network Namespace、Veth 设施对、Iptables/Netfilter、网桥、路由等。接下来,我将以 Docker 容器网络实现的根底技术来别离论述,在到真正的容器篇章节之前,能造成一个巩固的基础知识网。

Network Namespace

为了反对网络协议栈的多个实例,Linux 在网络栈引入了 Network Namespace,这些独立的协定栈被隔离到不同的 Namespace 中,处于不同 Namespace 中的网络栈是齐全隔离的,彼此无奈通信。具体无关 Linux Namespace 的介绍,能够另行浏览之前写的《Linux Namespace》。

Linux 的网络协议栈十分复杂,为了反对独立的协定栈,相干的全局变量都必须批改为协定栈公有。Linux 实现 Network Namespace 的外围就是让这些全局变量称为 Network Namespace 变量的成员,而后为协定栈的函数调用退出一个 Namespace 参数。与此同时,为了保障已开发程序及内核代码的兼容性,内核代码隐式地应用了 Namespace 空间内的变量。应用程序如果没有对 Namespace 有非凡需要,那么不须要额定的代码,Network Namespace 对应用程序而言是通明的。

在建设了新的 Network Namespace,并将某个过程关联到这个网络命名空间后,就呈现了如下的命名空间下的内核数据结构,所有网络栈变量都放入了 Network Namespace 的数据结构中,这个 Network Namespace 是属于它过程组公有的,与其余过程组不抵触。

Docker 正是利用了 Network Namespace 个性,实现了不同容器之间的网络隔离。如果一个容器申明应用宿主机的网络栈(-net = host),即不开启 Network Namespace,例如:

docker run –d –net=host --name c_name i_name

这种状况下,这个容器启动之后监听的是宿主机的 80 端口。像这样间接应用宿主机网络栈的形式,尽管能够为容器提供良好的网络性能,但也不可避免的造成端口抵触等网络资源抵触的问题。

所以在个别状况下,咱们都心愿程序引入 Network Namespace 里的网络栈,即这个容器领有本人的 IP 和端口。然而,这个时候也会带来一个新的问题,被隔离的容器过程,是怎么与其它被隔离的过程进行网络通信的?

Net Bridge

上文说到,Linux 能够反对不同的网络,他们之间是怎么反对够相互通信的呢?如果是两台主机,那须要的可能只是一根网线,把它们连贯在一台交换机上。而在 Linux 当中,网桥(Bridge)就起到相应的作用。实质上来说,这是一个数据链路层(data link)的设施,依据 Mac 地址的信息转发到网桥的不同端口上。而 Docker 就是在宿主机上默认创立一个 docker0 的网桥,但凡连贯 docker0 的网桥,都能够用它来通信。

细述 Bridge

网桥是一个二层的虚构网络设备,把若干个网络接口“连贯”起来,使得网口之间的报文能够转发。网桥可能解析收发的报文,读取指标的 Mac 地址信息,和本人的 Mac 地址表联合,来决策报文转发的指标网口。为了实现这些性能,网桥会学习源 Mac 地址。在转发报文时,网桥只须要向特定的端口转发,从而防止不必要的网络交互。如果它遇到了一个本人从未学过的地址,就无奈晓得这个报文应该向哪个网口转发,就将报文播送给除了报文起源之外的所有网口。

在理论网络中,网络拓扑不可能永恒不变。如果设施挪动到另一个端口上,而它没有发送任何数据,那么网桥设施就无奈感知到这个变动,后果网桥还是向原来的端口发数据包,在这种状况下数据就会失落。所以网桥还要对学习到的 Mac 地址表加上超时工夫,默认 5min。如果网桥收到了对应端口 MAC 地址回发的包。则重置超时工夫,否则过了超时工夫后,就认为哪个设施不在那个端口上了,他就会播送重发。

Linux 为了反对越来越多的网卡以及虚构设施,所以应用网桥去提供这些设施之间转发数据的二层设施。Linux 内核反对网口的桥接(以太网接口),这与单纯的交换机还是不太一样,交换机仅仅是一个二层设施,对于承受到的报文,要么转发,要么抛弃。运行着 Linux 内核的机器自身就是一台主机,有可能是网络报文的目的地,其收到的报文要么转发,要么抛弃,还可能被送到网络协议的网络层,从而被本人主机自身的协定栈消化,所以咱们能够把网桥看作一个二层设施,也能够看做是一个三层设施。

Linux 中 Bridge 实现

Linux 内核是通过一个虚构的网桥设施(Net Device)来实现桥接的。这个虚构设施能够绑定若干个以太网接口,从而将它们连接起来。Net Device 网桥和一般的设施不同,最显著的是它还能够有一个 ip 地址。

如上图所示,网桥设施 br0 绑定的 eth0 和 eth1。对于网络协议栈的下层来说,只看到 br0。因为桥接是在数据链路层实现的,下层不须要关怀桥接的细节,于是协定栈下层须要发送的报文被送到 br0,网桥设施的解决代码判断报文被转发到 eth0 还是 eth1,或者两者皆转发。反之,从 eth0 或者从 eth1 接管到的报文被提交给网桥的解决代码,在这里判断报文应该被转发、抛弃或者提交到协定栈下层。

而有时 eth0、eth1 也可能会作为报文的源地址或目标地址,直接参与报文的发送和接管,从而绕过网桥。

Bridge 罕用操作

Docker 主动实现了对网桥的创立和保护。如果想要进一步了解网桥,能够看下如下举的一些罕用操作命令。

新增一个网桥:

brctl addbr xxxxx

在新增网桥的根底上减少网口,在 linux 中,一个网口其实就是一个物理网卡。将物理网卡和网桥连接起来:

brctl addif xxxx ethx

网桥的物理网卡作为一个网口,因为在链路层工作,就不再须要 IP 地址了,这样下面的 IP 地址天然生效:

ipconfig ethx 0.0.0.0

给网桥配置一个 IP 地址:

ipconfig brxxx xxx.xxx.xxx.xxx

这样网桥就是一个有了 IP 地址,而连贯在这之上的网卡就是一个纯链路层设施了。

Veth Pair

上文说到,docker 在宿主机上创立 docker0 网桥后,但凡连贯到 docker0 上的网桥,就能够用它来通信。那么这里又有个问题,就是这些容器是如何连贯到 docker0 网桥上的?所以这就是 Veth Pair 虚构设施的作用了,Veth Pair 就是为了在不同的 Network Namespace 之间进行通信,利用它,能够将两个 Network Namespace 连接起来。

Veth Pair 设施的特点是:它被创立进去后,总是以两张虚构网卡(Veth Peer)的模式呈现。并且,其中一个网卡收回的数据包,能够间接呈现在另一张“网卡”上,哪怕这两张网卡在不同的 Network Namespace 中。

正是因为这样的特点,Veth Pair 成对呈现,很像是一对以太网卡,经常被看做是不同 Network Namespace 直连的“网线”。在 Veth 一端发送数据时,他会将数据发送到另一端并触发另一端的接管操作。咱们能够把 Veth Pair 其中一端看做另一端的一个 Peer。

Veth Pair 操作命令

创立 Veth Pair:

ip link add veth0 type veth peer name veth1

创立后查看 Veth Pair 的信息:

ip link show

将其中一个 Veth Peer 设置到另一个 Namespace:

ip link set veth1 netns netns1

在 netns1 中查看 veth1 设施:

ip netns exec netns1 ip link show

当然,在 docker 外面,除了将 Veth 放入容器,还改名为 eth0。想要通信必须先调配 IP 地址:

ip netns exec netns1 ip addr add 10.1.1.1/24 dev veth1
ip addr add 10.1.1.2/24 dev veth0

启动它们:

ip netns exec netns1 ip link set dev veth1 up
ip link set dev veth0 up

测试通信:

ip netns exec netns1 ping 10.1.1.2

Veth Pair 查看端对端

在实际操作 Veth Pair 时,能够应用 ethtool 便于操作。

在一个 Namespace 中查看 Veth Pair 接口在设施列表中的序列号:

ip netns exec netns1 ethtool -S veth1

如果得悉另一端的接口设施序列号,如果序列号为 6,则能够持续查看 6 代表了什么设施:

ip netns exec netns2 ip link | grep 6

Iptables/Netfilter

Linux 协定栈十分高效且简单。如果咱们想要在数据处理过程中对关怀的数据进行一些操作,则须要 Linux 提供一套相应的机制帮忙用户实现自定义的数据包处理过程。

在 Linux 网络协议栈有一组网络回调函数挂接点,通过这些挂接点函数挂接的钩子函数能够在 Linux 网络栈解决数据包的过程中对数据包一些操作,例如过滤、批改、抛弃等。整个挂接点技术叫做 Iptables 和 Netfilter。

Netfilter 负责在内核中执行各种各样的挂接规定,运行在内核模式中。而 Iptables 是在用户模式下运行的过程,负责帮助保护内核中 Netfilter 的各种规定表。通过二者的配合来实现整个 Linux 网络协议栈中灵便的数据包解决机制。

规定表 Table

这些挂载点能挂接的规定也分不同的类型,目前次要反对的 Table 类型如下:

•RAW•MANGLE•NAT•FILTER

上述 4 个规定链的优先级是 RAW 最高,FILTER 最低。

在理论利用中,不同挂接点须要的规定类型通常不同。例如,在 Input 的挂接点上显著不须要 FILTER 的过滤规定,因为依据指标地址,曾经在本机的下层协定栈了,所以无需再挂载 FILTER 过滤规定。

Route

Linux 零碎蕴含了一个残缺的路由性能。当 IP 层在解决数据发送或者转发时,会应用路由表来决定发往哪里。通常状况下,如果主机与目标主机间接相连,那么主机能够间接发送 IP 报文到目标主机。

路由性能是由 IP 层保护的一张路由表来实现。当主机收到数据报文时,它用此表来决策接下来应该做什么操作。当从网络侧接管到数据报文时,IP 层首先会查看报文的 IP 地址是否与主机本身的地址雷同。如果数据报文中的 IP 地址是本身主机的地址,那么报文将被发送到传输层相应的协定栈中去。如果报文中的 IP 地址不是主机本身的地址,并且配置了路由性能,那么报文将被转发,否则报文将被抛弃。

路由表的数据个别以条目模式存在,一个典型的路由表条目通常蕴含以下次要的条目项:

•目标 IP 地址•下一个路由器的 IP 地址•标记•网络接口标准

通过路由表转发时,如果任何条目标第一个字段齐全匹配指标条目标 IP 地址(主机)或局部匹配(网络),那么它将批示下一个路由器的 IP 地址。这些信息将通知主机数据包该转发到哪一个“下一个路由器”。而条目中所有其它字段将提供更多的辅助信息来为路由转发做决定。

如果没有一个齐全匹配的 IP,则持续搜寻网络 ID。找到则转发数据到指定路由器上。由此可知,网络上所有主机都是通过这个路由表中的单个条目进行治理。

如果上述两个条件都不匹配,则将数据报文转发到一个默认路由器上。

如果上述步骤失败,默认路由器也不存在,那么这个数据报文无奈转发。任何无奈投递的数据都会产生一个 ICMP 主机不可达或者 ICMP 网络不可达的谬误,并将该谬误返回给生成此数据的应用程序。

Route Table

Linux 路由表至多 2 个,一个是 LOCAL,一个是 MAIN。

Local 表用于供 Linux 协定栈辨认本地地址,以及进行本地各个不同网络之间的数据转发。MAIN 表用于各类网络 IP 的转发。它的建设既能够应用动态配置生成,也能够应用动静路由发现协定生成。动静路由发现协定个别应用组播性能来通过发送路由发现数据,动静获取和替换网络的路由信息,并更新到路由表中。

通过下列命令查看 LOCAL 表的内容:

ip route show table local type local

路由表的查看:

ip route list

总结

到此为止,介绍了实现 docker 容器网络最外围的根底局部,包含 Network Namespace、Bridge、Veth Pair、Iptables/Netfilter、Route。接下来,将会持续在这根底之上论述 Docker 容器网络的具体实现。

退出移动版