本文共 9261 字,大约阅读时间需要 30 分钟。
与 Docker 默认的网络模型不同,Kubernetes 形成了一套自己的网络模型,该网络模型更加适应传统的网络模式,应用能够平滑的从非容器环境迁移到 Kubernetes 环境中。
自从 Docker 容器出现,容器的网络通信一直是众人关注的焦点,而容器的网络方案又可以分为两大部分:
利用 Net Namespace 可以为 Docker 容器创建隔离的网络环境,容器具有完全独立的网络栈,与宿主机隔离。也可以使 Docker 容器共享主机或者其他容器的网络命名空间。
我们在使用docker run
创建 Docker 容器时,可以使用--network=
选项指定容器的网络模式,Docker 有以下 4 种网络模式:
--network=host
指定,不支持多主机;--network=bridge
指定,默认设置,不支持多主机;--network=container:NAME_or_ID
指定,即joiner 容器
,不支持多主机;--network=none
指定,不支持多主机。连接到 host 网络的容器共享 Docker host 的网络栈,容器的网络配置与 host 完全一样。
我们先查看一下主机的网络。
[root@datanode03 ~]# ifconfigdocker0: flags=4099mtu 1500 inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255 ether 02:42:44:8d:48:70 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 0enp1s0: flags=4163 mtu 1500 inet 192.168.1.203 netmask 255.255.255.0 broadcast 192.168.1.255 inet6 fe80::2e0:70ff:fe92:4779 prefixlen 64 scopeid 0x20 ether 00:e0:70:92:47:79 txqueuelen 1000 (Ethernet) RX packets 46093 bytes 66816291 (63.7 MiB) RX errors 0 dropped 1 overruns 0 frame 0 TX packets 24071 bytes 1814769 (1.7 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0lo: flags=73 mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 inet6 ::1 prefixlen 128 scopeid 0x10 loop txqueuelen 0 (Local Loopback) RX packets 170 bytes 107720 (105.1 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 170 bytes 107720 (105.1 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
然后创建 host 网络的容器,再查看容器的网络信息。
[root@datanode03 ~]# docker run -it --network=host busyboxUnable to find image 'busybox:latest' locallylatest: Pulling from library/busybox90e01955edcd: Pull complete Digest: sha256:2a03a6059f21e150ae84b0973863609494aad70f0a80eaeb64bddd8d92465812Status: Downloaded newer image for busybox:latest/ # ifconfigdocker0 Link encap:Ethernet HWaddr 02:42:44:8D:48:70 inet addr:172.17.0.1 Bcast:172.17.255.255 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)enp1s0 Link encap:Ethernet HWaddr 00:E0:70:92:47:79 inet addr:192.168.1.203 Bcast:192.168.1.255 Mask:255.255.255.0 inet6 addr: fe80::2e0:70ff:fe92:4779/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:45850 errors:0 dropped:1 overruns:0 frame:0 TX packets:23921 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:66794758 (63.7 MiB) TX bytes:1783655 (1.7 MiB)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:170 errors:0 dropped:0 overruns:0 frame:0 TX packets:170 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:107720 (105.1 KiB) TX bytes:107720 (105.1 KiB)
在容器中可以看到 host 的所有网卡,并且连 hostname 也是 host 的,可以直接使用宿主机 IP 地址与外界通信,无需额外进行 NAT 转换。由于容器通信时,不再需要通过 Linux Bridge 等方式转发或者数据包的封装,性能上有很大的优势。
当然,Host 模式有利也有弊,主要包括以下缺点:
容器没有隔离、独立的网络栈:容器因与宿主机共用网络栈而争抢网络资源,并且容器崩溃也可能导致主机崩溃,这再生产环境中是不允许发生的。
端口资源:Docker host 上已经使用的端口就不能再用了。Bridge 模式是 Docker 默认的网络模式,也是开发者最常用的网络模式。在这种模式下,Docker 为容器创建独立的网络栈,保证容器内的进行使用独立的网络环境,实现容器之间,容器与宿主机之间的网络栈隔离。同时,通过宿主机上的 Docker0 网桥,容器可以与宿主机乃至外界进行网络通信。
从上图可以看出,容器是可以与宿主机以及外界的其他机器通信的,同一宿主机上,容器之间都是桥接在 Docker0 这个网桥上,Docker0 作为虚拟交换机使容器间互相通信。但是,由于宿主机的 IP 地址与容器 veth pair 的 IP 地址均不在同一个网段,故仅仅依靠 veth pair 和 NameSpace 的技术并不足以使宿主机以外的网络主动发现容器的存在。Docker 采用了端口绑定的方式(通过 iptables 的 NAT),将宿主机上的端口流量转发到容器内的端口上,这样一来,外界就可以与容器中的进程进行通信。
Container 模式是一种特殊的网络模式。该模式下的容器使用其他容器的网络命名空间,网络隔离性会处于 Bridge 模式与 Host 模式之间。也就是说,当容器与其他容器共享网络命名空间时,这两个容器间不存在网络隔离,但他们与宿主机机器其他容器又存在网络隔离。
Container 模式的容器可以通过 localhost 来与同一网络命名空间下的其他容器通信,传输效率高。这种模式节约了一定数量的网络资源,但并没有改变容器与外界的通信方式。在 Kubernetes 体系架构下引入 Pod 概念,Kubernetes 为 Pod 创建一个基础设施容器,同一 Pod 下的其他容器都以 Container 模式共享这个基础设施容器的网络命名空间,相互之间以 localhost 访问,构成一个统一的整体。
与前几种不同,None 模式的 Docker 容器拥有自己的 Network Namespace,但并不为 Docker 容器进行网络配置。也就是说,该 Docker 容器没有网卡、IP、路由等信息。需要用户为 Docker容器添加网卡、配置 IP 等。
常见的跨主机通信方案主要有以下几种:
主机 IP + 端口
的方式访问容器中的服务。显然,这种方式仅能支持网络栈的 4 层及以上的应用,并且容器与宿主机紧耦合,很难灵活地处理问题,可扩展性不佳;容器网络发展到现在,形成了两大阵营:
CNM 和 CNI 是网络规范或者网络体系,并不是网络实现,因此不关心容器的网络实现方式,CNM 和 CNI 关心的只是网络管理。
但从网络实现角度,又可分为:
隧道方案:隧道方案在 IaaS 层的网络中应用也比较多,它的主要缺点是随着节点规模的增长复杂度会提升,而且出了网络问题后跟踪起来比较麻烦,大规模集群情况下这是需要考虑的一个问题。路由方案:一般是基于3层或者2层实现网络隔离和跨主机容器互通的,出了问题也很容易排查。
Calico:基于 BGP 协议的路由方案,支持很细致的 ACL 控制(Nerwork Policy),对混合云亲和度比较高。Macvlan:从逻辑和 Kernel 层来看,是隔离性和性能最优的方案。基于二层隔离,所以需要二层路由器支持,大多数云服务商不支持,所以混合云上比较难以实现。flannel 是 CoreOS 开发的容器网络解决方案。flannel 为每个 host 分配一个 subnet,容器从此 subnet 中分配 IP,这些 IP 可以在 host 间路由,容器间无需 NAT 和 port mapping 就可以跨主机通信。
每个 subnet 都是从一个更大的 IP 池中划分的,flannel 会在每个主机上运行一个叫 flanneld 的 agent,其职责就是从池子中分配 subnet。为了在各个主机间共享信息,flannel 用 etcd(与 consul 类似的 key-value 分布式数据库)存放网络配置、已分配的 subnet、host 的 IP 等信息。
数据包如何在主机间转发是由 backend 实现的。flannel 提供了多种 ,有 UDP、vxlan、host-gw、aws-vpc、gce 和 alloc 路由,最常用的有 vxlan 和 host-gw。
Flannel 实质上是一种叠加网络(Overlay Network),也就是将 TCP 数据包装在另一种网络包里面进行路由转发和通信。
我们使用 kubectl apply 安装的 flannel 默认的 backend 为 vxlan,host-gw 是 flannel 的另一个 backend,我们将前面的 vxlan backend 切换成 host-gw。
与 vxlan 不同,host-gw 不会封装数据包,而是在主机的路由表中创建到其他主机 subnet 的路由条目,从而实现容器跨主机通信。要使用 host-gw 首先修改 flannel 的配置 flannel-config.json
:
kubectl edit cm kube-flannel-cfg -o yaml -n kube-system
找到如下字段进行修改。
net-conf.json: | { "Network": "10.244.0.0/16", "Backend": { "Type": "host-gw" } }
host-gw 把每个主机都配置成网关,主机知道其他主机的 subnet 和转发地址,由于 vxlan 需要对数据包进行额外的打包和拆包,性能会比 vxlan 强一些。
Calico 把每个操作系统的协议栈当做一个路由器,然后认为所有的容器是连在这个路由器上的网络终端,在路由器之间运行标准的路由协议——BGP,然后让他们自己去学习这个网络拓扑该如何转发,所以Calico 是一个纯三层的虚拟网络方案,Calico 为每个容器分配一个 IP,每个 host 都是 router,把不同 host 的容器连接起来。与 VxLAN 不同的是,Calico 不对数据包做额外封装,不需要 NAT 和端口映射,扩展性和性能都很好。
与其他容器网络方案相比,Calico 还有一大优势:network policy。用户可以动态定义 ACL 规则,控制进出容器的数据包,实现业务需求。
请查看我的博文 。
macvlan 本身是 linxu kernel 模块,其功能是允许在同一个物理网卡上配置多个 MAC 地址,即多个 interface,每个 interface 可以配置自己的 IP。macvlan 本质上是一种网卡虚拟化技术,Docker 用 macvlan 实现容器网络就不奇怪了。
macvlan 的最大优点是性能极好,相比其他实现,macvlan 不需要创建 Linux bridge,而是直接通过以太 interface 连接到物理网络。
macvlan 会独占主机的网卡,也就是说一个网卡只能创建一个 macvlan 网络:
但主机的网卡数量是有限的,如何支持更多的 macvlan 网络呢?
好在 macvlan 不仅可以连接到 interface(如 enp0s9),也可以连接到 sub-interface(如 enp0s9.xxx)。
VLAN 是现代网络常用的网络虚拟化技术,它可以将物理的二层网络划分成多达 4094 个逻辑网络,这些逻辑网络在二层上是隔离的,每个逻辑网络(即 VLAN)由 VLAN ID 区分,VLAN ID 的取值为 1-4094。
Linux 的网卡也能支持 VLAN(apt-get install vlan),同一个 interface 可以收发多个 VLAN 的数据包,不过前提是要创建 VLAN 的 sub-interface。
比如希望 enp0s9 同时支持 VLAN10 和 VLAN20,则需创建 sub-interface enp0s9.10 和 enp0s9.20。
在交换机上,如果某个 port 只能收发单个 VLAN 的数据,该 port 为 Access 模式,如果支持多 VLAN,则为 Trunk 模式,所以接下来实验的前提是:
enp0s9 要接在交换机的 trunk 口上。不过我们用的是 VirtualBox 虚拟机,则不需要额外配置了。
转载地址:http://jpsra.baihongyu.com/