K8S 的网络架构弄清楚了吗?来源:思恒科技 K8S 网络设计与实现是在学习 K8S 网络过程中总结的内容。本文按照 K8S 网络设计原则、Pod 内部网络、Pod 之间网络等几个步骤讲解 K8S 复杂的网络架构。
图片出自:《你女儿也能看懂的插画版 Kubernetes 指南》 K8S 网络设计原则 K8S 网络设计原则如下: 每个 Pod 都拥有一个独立 IP 地址,Pod 内所有容器共享该 IP 地址。 集群内所有 Pod 都在一个直接连通的扁平网络中,可通过 IP 直接访问。 即:所有容器之间无需 NAT 就可以直接互相访问;所有 Node 和所有容器之间无需 NAT 就可以直接互相访问;容器自己看到的 IP 跟其他容器看到的一样。 K8S 网络规范 CNI 是由 CoreOS 提出的一个容器网络规范。已采纳规范的包括 Apache Mesos,Cloud Foundry,Kubernetes,Kurma 和 RKT。 另外 Contiv Networking,Project Calico 和 Weave 这些项目也为 CNI 提供插件。
CNI 规定了一个容器 Runtime 和网络插件之间的简单的契约。这个契约通过 JSON 的语法定义了 CNI 插件所需要提供的输入和输出。 CNI 插件提供两个功能: 一个用来将网络接口加入到指定网络。 另一个用来将其移除。这两个接口分别在容器被创建和销毁的时候被调用。容器 Runtime 首先需要获得一个网络命名空间以及一个容器 ID,然后连同一些 CNI 配置参数传给网络驱动。 接着网络驱动会将该容器连接到网络并将分配的 IP 地址以 JSON 的格式返回给容器 Runtime。 K8S 网络插件要求 K8S 对网络插件的要求总的来讲主要有两个最基本的,分别是: l 要能够为每一个 Node 上的 Pod 分配互相不冲突的 IP 地址。 l 要所有 Pod 之间能够互相访问。 K8S 网络实现方案 K8S网络实现方案有如下几种: 隧道方案 隧道方案在 IaaS 层的网络中应用也比较多,将 Pod 分布在一个大二层的网络规模下。网络拓扑简单,但随着节点规模的增长复杂度会提升。 代表方案: lWeave:UDP 广播,本机建立新的 BR,通过 PCAP 互通。 lOpen vSwitch:基于 VxLan 和 GRE 协议,但是性能方面损失比较严重。 lFlannel:UDP 广播,VxLan。 lRacher:IPsec。 路由方案 路由方案一般是从 3 层或者 2 层实现隔离和跨主机容器互通的,出了问题也很容易排查。 代表方案: lCalico:基于 BGP 协议的路由方案,支持很细致的 ACL 控制,对混合云亲和度比较高。 lMacvlan:从逻辑和 Kernel 层来看隔离性和性能优的方案,基于二层隔离,所以需要二层路由器支持,大多数云服务商不支持,所以混合云上比较难以实现。 K8S Pod 的网络创建流程 lK8S Pod 的网络创建流程如下: l每个 Pod 除了创建时指定的容器外,都有一个 Kubelet 启动时指定的基础容器。 lKubelet 创建基础容器,生成 Network Namespace。 lKubelet 调用网络 CNIdriver,根据配置调用具体的 CNI 插件。 lCNI 插件给基础容器配置网络。 lPod 中其他的容器共享基础容器的网络。 Pod 中的网络 Pod 是 K8S 的最小工作单元。每个 Pod 包含一个或多个容器。K8S 管理的也是 Pod 而不是直接管理容器。Pod 中的容器会作为一个整体被 Master 调度到一个 Node 上运行。 Pod 的设计理念是支持多个容器在一个 Pod 中共享网络地址和文件系统,可以通过进程间通信和文件共享这种简单高效的方式组合完成服务。
一个 Pod 中可以包含多个容器,而一个 Pod 只有一个 IP 地址。那么多个容器之间互相访问和访问外网是如何使用这一个 IP 地址呢? 答案是:多个容器共享同一个底层的网络命名空间 Net(网络设备、网络栈、端口等)。 下面以一个小例子说明,创建一个 Pod 包含两个容器,yaml 文件如下: apiVersion: apps/v1beta1 kind: Deployment metadata: name: Pod-two-container spec: replicas: 1 template: metadata: labels: app: nginx spec: containers: - name: busybox image: busybox command: - "/bin/sh" - "-c" - "while true;do echo hello;sleep 1;done" - name: nginx image: nginx
创建1个Pod 中包含2个Container,实际会创建3个Container。多出的一个是“Pause”容器。 该ContainePo的基础容器,为其他容器提供网络功能。查看 Pause 容器的基础信息:
使用命令docker inspect 容器 _ID 查看 Nginx 详细信息,其网络命令空间使用了 Pause 容器的命名空间,同样还有进程间通信的命名空间。 再查看 Busybox,可以发现其网络命令空间使用了 Pause 容器的命名空间,进程通信的命名空间也是 Pause 容器的命名空间。
实现方式:Nginx 和 Busybox 之所以能够和 Pause 的命名空间连通是因为 Docker 有一个特性:能够在创建时使用指定 Docker 的网络命名空间。 在 Docker 的官网上有一段描述: https://docs.docker.com/engine/reference/run/
所以如果要手动完成一个上面的 Pod,可以先创建 Pause,再创建 Nginx 和 Busybox,同时将网络指定为 Pause 的网络命名空间即可。 docker run --name pause mirrorgooglecontainers/pause-amd64:3.1 docker run --name=nginx --network=container:pause nginx docker run --name=busybox --network=container:pause busybox
上述步骤由 K8S 帮助我们完成,所以 Pod 命名空间应该是这样的:
讲完了 Pod 内部的网络实现,我们主要来看 Pod 如何获取 IP 地址,以及 Pod 与 Pod 之间的通信是怎么样的过程? Flannel 网络 Flannel 简介 Flannel 是 CoreOS 团队针对 Kubernetes 设计的一个网络规划服务,简单来说,它的功能是让集群中的不同节点主机创建的 Docker 容器都具有全集群唯一的虚拟 IP 地址。 在默认的 Docker 配置中,每个节点上的 Docker 服务会分别负责所在节点容器的 IP 分配。这样导致的一个问题是,不同节点上容器可能获得相同的 IP 地址。 Flannel 的设计目的就是为集群中的所有节点重新规划 IP 地址的使用规则,从而使得不同节点上的容器能够获得“同属一个内网”且“不重复的”IP 地址,并让属于不同节点上的容器能够直接通过内网 IP 通信。 Flannel 实质上是一种“覆盖网络(overlay network)”,也就是将 TCP 数据包装在另一种网络包里面进行路由转发和通信。 目前已经支持 UDP、Vxlan、Host-gw、Aws-vpc、Gce 和 Alloc 路由等数据转发方式,默认的节点间数据通信方式是 UDP 转发。 适用场景:不需要隔离 Pod,集群规模小。 设计思想:为每一个 Node 节点分配 IP 网段,使 Node 之间 IP 不重复,Pod 之间直接使用 IP 访问。 设计优势:网络模型简单,安装配置相对容易,成熟度高,适合大多数用例的环境。 Flannel 对网络要求提出的解决办法 互相不冲突的 IP: lFlannel 利用 Kubernetes API 或者 etcd 用于存储整个集群的网络配置,根据配置记录集群使用的网段。 lFlannel 在每个主机中运行 Flanneld 作为 Agent,它会为所在主机从集群的网络地址空间中,获取一个小的网段 Subnet,本主机内所有容器的 IP 地址都将从中分配。 如测试环境中 IP 分配: ①Master 节点
②Node1
③Node2
在 Flannel Network 中,每个 Pod 都会被分配唯一的 IP 地址,且每个 K8S Node 的 Subnet 各不重叠,没有交集。 Pod 之间互相访问:
Flannel 架构原理 Flannel 架构图: 各个组件的解释如下: Cni0:网桥设备,每创建一个 Pod 都会创建一对 Veth Pair。其中一端是 Pod 中的 eth0,另一端是 cni0 网桥中的端口(网卡) Pod 中从网卡 eth0 发出的流量都会发送到 cni0 网桥设备的端口(网卡)上。
注:cni0 设备获得的 IP 地址是该节点分配到的网段的第一个地址。 Flannel.1:Overlay 网络的设备,用来进行 Vxlan 报文的处理(封包和解包)。不同 Node 之间的 Pod 数据流量都从 Overlay 设备以隧道的形式发送到对端。
Flanneld:Flannel 在每个主机中运行 Flanneld 作为 Agent,它会为所在主机从集群的网络地址空间中,获取一个小的网段 Subnet,本主机内所有容器的 IP 地址都将从中分配。 同时 Flanneld 监听 K8S 集群数据库,为 Flannel.1 设备提供封装数据时必要的 Mac,IP 等网络数据信息。 不同 Node 上的 Pod 的通信流程: lPod 中产生数据,根据 Pod 的路由信息,将数据发送到 cni0 lcni0 根据节点的路由表,将数据发送到隧道设备 Flannel.1 lFlannel.1 查看数据包的目的 IP,从 Flanneld 获得对端隧道设备的必要信息,封装数据包。 lFlannel.1 将数据包发送到对端设备。节点的网卡接收到数据包,发现数据包为 Overlay 数据包,解开外层封装,并发送内层封装到 Flannel.1 设备。 lFlannel.1 设备查看数据包,根据路由表匹配,将数据发送给 cni0 设备。 lcni0 匹配路由表,发送数据给网桥上对应的端口。 通信流程 ①Pod1 中的容器到 cni0 Pod1 与 Pod3 能够互相 Ping 通:
Ping 包的 Dst IP 为 192.20.1.43,根据路由匹配到最后一条路由表项,去往 192.20.0.0/12 的包都转发给 192.20.0.1。
192.20.0.1 为 cni0 的 IP 地址。
②cni0 到 Flannel1.1 当 Icmp 包达到 cni0 之后,cni0 发现 Dst 为 192.20.1.43,CNI 根据主机路由表来查找匹配项。
创建一个 Daemonset 的应用,Pod1 落在 Master 节点上 IP 地址为 192.168.236.3,Pod2 落在 Node 节点上 IP 地址为 192.168.190.203:
Pod1 Ping Pod2:
②Ping 包旅程 Pod1 上的路由信息:
根据路由信息,Ping 192.168.190.203,会匹配到第一条。第一条路由的意思是:去往任何网段的数据包都发往网管 169.254.1.1,然后从 eth0 网卡发送出去。 路由表中 Flags 标志的含义: lU:UP 表示当前为启动状态。 lH:Host 表示该路由为一个主机,多为达到数据包的路由。 lG:Gateway 表示该路由是一个网关,如果没有说明目的地是直连的。 lD:Dynamicaly 表示该路由是重定向报文修改。 lM:表示该路由已被重定向报文修改。 Master 节点上的路由信息:
当 Ping 包来到 Master 节点上,会匹配到路由 tunl0。该路由的意思是:去往 192.169.190.192/26 的网段的数据包都发往网关 172.171.5.96。 因为 Pod1 在 5.95,Pod2 在 5.96。所以数据包就通过该路由发往到 Node 节点上。 Node 节点上路由信息:
当 Node 节点网卡收到数据包之后,发现发往的目的 IP 为 192.168.190.203,于是匹配到红线的路由(绿线是 Node 结点对应的发往 Master 的路由)。 该路由的意思是:192.168.190.203 是本机直连设备,去往设备的数据包发往 caliadce112d250。这个设备就是 Pod2 的 Veth Pair 的一端。 在创建 Pod2 时 Calico 会给 Pod2 创建一个 Veth Pair 设备。一端是 Pod2 的网卡,另一端就是我们看到的 caliadce112d250。 可以通过如下方式验证:在 Pod2 中安装 ethtool 工具,然后使用 ethtool-S eth0,查看 Veth Pair 另一端的设备号。
Pod2 网卡另一端的设备编号是 18,在 Node 上查看编号为 18 的网络设备,可以发现该网络设备就是 caliadce112d250。
所以,Node 上的路由,发送 caliadce112d250 的数据其实就是发送到 Pod2 的网卡中。Ping 包的旅行到这里就到了目的地。 查看一下 Pod2 中的路由信息,发现该路由信息和 Pod1 中是一样的。
顾名思义,IPIP 网络就是将 IP 网络封装在 IP 网络里。IPIP 网络的特点是所有 Pod 的数据流量都从隧道 tunl0 发送,并且在 tunl0 这增加了一层传输层的封包。 在 Master 网卡上抓包分析该过程:
打开 ICMP 285,Pod1 Ping Pod2 的数据包,能够看到该数据包一共 5 层,其中 IP 所在的网络层有两个,分别是 Pod 之间的网络和主机之间的网络封装。
根据数据包的封装顺序,应该是在 Pod1 Ping Pod2 的 ICMP 包外面多封装了一层主机之间的数据包。
之所以要这样做是因为 tunl0 是一个隧道端点设备,在数据到达时要加上一层封装,便于跨网段访问。 两层 IP 封装的具体内容:
IPIP 的连接方式:
BGP 工作模式 ①修改配置 在安装 Calico 网络时,默认安装是 IPIP 网络。calico.yaml 文件中,将 CALICO_IPV4POOL_IPIP 的值修改成"off",就能够替换成 BGP 网络。
②对比 BGP 网络相比较 IPIP 网络,最大的不同之处就是没有了隧道设备 tunl0,且不通过隧道设备发送流量。 前面介绍过 IPIP 网络 Pod 之间的流量发送 tunl0,然后 tunl0 发送对端设备。BGP 网络中,Pod 之间的流量直接从网卡发送目的地,减少了 tunl0 这个环境。 Master 节点上路由信息。从路由信息来看,没有 tunl0 设备。
同样创建一个 Daemonset,Pod1 在 Master 节点上,Pod2 在 Node 节点上。
③Ping 包旅程 Pod1 Ping Pod2:
根据 Pod1 中的路由信息,Ping 包通过 eth0 网卡发送到 Master 节点上。 Master 节点上路由信息。根据匹配到的 192.168.190.192 路由,该路由的意思是:去往网段 192.168.190.192/26 的数据包,发送网段 172.171.5.96。 而 5.96 就是 Node 节点。所以,该数据包直接发送了 5.96 机器:
Node 节点上的路由信息。根据匹配到的 192.168.190.192 的路由,数据将发送给 cali6fcd7d1702e 设备,该设备和上面分析的是一样,为 Pod2 的 Veth Pair 的一端。数据就直接发送给 Pod2 的网卡。
当 Pod2 对 Ping 包做出回应之后,数据到达 Node 节点上,匹配到 192.168.236.0 的路由。 该路由说的是:去往网段 192.168.236.0/26 的数据,发送给网关 172.171.5.95。数据包就直接通过网卡 ens160,发送到 Master 节点上。
通过在 Master 节点上抓包,查看经过的流量,筛选出 ICMP,找到 Pod1 Ping Pod2 的数据包。
可以看到 BGP 网络下,没有使用 IPIP 模式,数据包是正常的封装。
值得注意的是 Mac 地址的封装。192.168.236.0 是 Pod1 的 IP,192.168.190.198 是 Pod2 的 IP。 而源 Mac 地址是 Master 节点网卡的 Mac,目的 Mac 是 Node 节点的网卡的 Mac。 这说明,在 Master 节点的路由接收到数据,重新构建数据包时,使用 ARP 请求,将 Node 节点的 Mac 拿到,然后封装到数据链路层。
BGP 的连接方式如下图
网络对比 IPIP 网络: l流量:tunlo 设备封装数据,形成隧道,承载流量。 l适用网络类型:适用于互相访问的 Pod 不在同一个网段中,跨网段访问的场景。外层封装的 IP 能够解决跨网段的路由问题。 l效率:流量需要 tunl0 设备封装,效率略低。 BGP 网络: l流量:使用路由信息导向流量。 l适用网络类型:适用于互相访问的 Pod 在同一个网段,跨网段需要上行交换机或路由器支持。适用于大型网络。 l效率:原生 HostGW,效率高。存在问题 ①租户隔离问题 Calico 的三层方案是直接在 Host 上进行路由寻址,那么对于多租户如果使用同一个 CIDR 网络就面临着地址冲突的问题。 ②路由规模问题 通过路由规则可以看出,路由规模和 Pod 分布有关,如果 Pod 离散分布在 Host 集群中,势必会产生较多的路由项。 ③IPtables 规则规模问题 1 台 Host 上可能虚拟化十几或几十个容器实例,过多的 IPtables 规则造成复杂性和不可调试性,同时也存在性能损耗。 ④跨子网时的网关路由问题 当对端网络不为二层可达时,需要通过三层路由时,需要网关支持自定义路由配置,即 Pod 的目的地址为本网段的网关地址,再由网关进行跨三层转发。 K8S 网络方案对比 以下对比为摘录内容:
声明:本公众号或网站转载文章仅为分享、传达不同观点,除发布的文章无法追溯到作者并获得授权外,我们均会注明作者和文章来源。如涉及版权问题请及时联系我们,联系电话:010 5715 0600 ,我们会在第一时间删改。谢谢!
文章分类:
首页发布
|