the site subtitle

Kubernetes 中的网络结构

2019.02.12

Kubernetes 采用的是基于扁平地址空间的网络模型,集群中的每个 Pod 都有自己的 IP 地址,Pod 之间不需要配置 NAT 就能直接通信。另外,同一个 Pod 中的容器共享 Pod 的 IP,能够通过 localhost 通信。

  • IP-per-Pod,每个 Pod 都拥有一个独立 IP 地址,Pod 内所有容器共享一个网络命名空间

  • 集群内所有 Pod 都在一个直接连通的扁平网络中,可通过 IP 直接访问

    • 所有容器之间无需 NAT 就可以直接互相访问
    • 所有 Node 和所有容器之间无需 NAT 就可以直接互相访问
    • 容器自己看到的 IP 跟其他容器看到的一样

接下来我们来先介绍一下几个概念,

  • Service cluster IP 尽可在集群内部访问,外部请求需要通过 NodePort、LoadBalance 或者 Ingress 来访问
  • 网络的命名空间:Linux在网络栈中引入网络命名空间,将独立的网络协议栈隔离到不同的命令空间中,彼此间无法通信;docker利用这一特性,实现不容器间的网络隔离。
  • Veth设备对:Veth设备对的引入是为了实现在不同网络命名空间的通信。
  • Iptables/Netfilter:Netfilter负责在内核中执行各种挂接的规则(过滤、修改、丢弃等),运行在内核 模式中;Iptables模式是在用户模式下运行的进程,负责协助维护内核中Netfilter的各种规则表;通过二者的配合来实现整个Linux网络协议栈中灵活的数据包处理机制。
  • 网桥:网桥是一个二层网络设备,通过网桥可以将linux支持的不同的端口连接起来,并实现类似交换机那样的多对多的通信。
  • 路由:Linux系统包含一个完整的路由功能,当IP层在处理数据发送或转发的时候,会使用路由表来决定发往哪里

如今,Kubernetes改变了软件开发的完成方式。作为用于管理容器化工作负载和服务的便携式,可扩展的开源平台,该平台可促进声明式配置和自动化,Kubernetes已证明自己是管理复杂微服务的主导者。它的受欢迎程度源于Kubernetes满足以下需求的事实:企业希望增长和减少支付,DevOps需要一个稳定的平台,可以大规模运行应用程序,开发人员想要可靠且可复制的流程来编写,测试和调试代码。是一篇很好的文章,可以了解有关Kubernetes演化和架构的更多信息。

管理Kubernetes网络的重要领域之一是在内部和外部转发容器端口,以确保容器和Pod可以正确相互通信。为了管理此类通信,Kubernetes提供以下四种联网模型:

  • 容器到容器通信
  • 点对点通讯
  • Pod到Service通讯
  • 对外沟通

在本文中,我们通过向您展示Kubernetes网络中的Pod可以相互通信的方式,深入探讨Pod到Pod的通信。

虽然Kubernetes在如何部署和操作容器方面持固执己见,但对于如何设计运行Pod的网络来说,这并不是非常明确的规定。Kubernetes对任何网络实施都施加以下基本要求(除非有任何故意的网络分段策略):

  • 所有Pod都可以与所有其他Pod通信,而无需NAT
  • 所有运行Pod的节点都可以与所有Pod通信(反之亦然),而无需NAT
  • Pod所看到的IP与其他Pod所看到的IP

为了说明这些要求,让我们使用具有两个群集节点的群集。节点位于子网192.168.1.0/24中,而Pod使用10.1.0.0/16子网,其中node1和node2分别将10.1.1.0/24和10.1.2.0/24用作Pod IP。

img

因此,从上面开始,网络必须建立遵循通信路径的Kubernetes要求。

  • 节点应该能够与所有Pod通信。例如,192.168.1.100应该能够直接到达10.1.1.2、10.1.1.3、10.1.2.2和10.1.2.3(无NAT)
  • Pod应该能够与所有节点通信。例如Pod 10.1.1.2应该能够在没有NAT的情况下达到192.168.1.100和192.168.1.101
  • Pod应该能够与所有Pod通信。例如,10.1.1.2应该能够直接与10.1.1.3、10.1.2.2和10.1.2.3通信(无NAT)

在探索这些需求时,我们将为如何发现和公开服务奠定基础。可以通过多种方式设计满足Kubernetes网络要求的网络,并具有不同程度的复杂性和灵活性。

点对点网络和连通性

Kubernetes不会协调网络的建立,而是将作业卸载到CNI插件上。是有关CNI插件安装的更多信息。以下是通过CNI插件实现的网络实现选项,这些选项允许Pod到Pod的通信满足Kubernetes的要求:

  1. 第2层(交换)解决方案
  2. 第三层(路由)解决方案
  3. 叠加解决方案

I-第2层解决方案

这是最简单的方法,适用于小型部署。Pod和节点应将Pod的IP子网视为一个单独的l2域。Pod到Pod的通信(在同一主机上或跨主机)通过ARP和L2交换进行。我们可以使用网桥 CNI插件在节点1(请注意/ 16子网)上进行以下配置,从而将L2网桥重用于pod容器。

{
  "name": "mynet",
  "type": "bridge",
  "bridge": "kube-bridge",
  "isDefaultGateway": true,
  "ipam": {
    "type": "host-local",
    "subnet": "10.1.0.0/16"
  }
}

需要预先创建kube-bridge,以便ARP数据包在物理接口上发出。为此,我们有另一个桥,该桥具有连接到其的物理接口和分配给它的节点IP,而kube-bridge通过如下所示的第ve对钩接到了该桥上。

img

我们可以传递一个预先创建的,在这种情况下, CNI插件将重用该桥。

II-第3层解决方案

一种更具可扩展性的方法是使用节点路由,而不是将流量切换到Pod。我们可以使用网桥CNI插件为配置了网关的Pod容器创建网桥。例如,可以在下面的node1上使用配置(请注意/ 24子网)。

{
  "name": "mynet",
  "type": "bridge",
  "bridge": "kube-bridge",
  "isDefaultGateway": true,
  "ipam": {
    "type": "host-local",
    "subnet": "10.1.0.0/24"
  }
}

那么,在节点1上运行IP 10.1.1.2的Pod1如何与在节点2上运行IP 10.1.2.2的Pod3通信?我们需要节点将流量路由到其他节点Pod子网的方法。

我们可以使用子网路由填充默认网关路由器,如下图所示。到10.1.1.0/24和10.1.2.0/24的路由分别配置为通过node1和node2。当节点添加或删除到集群中时,我们可以自动使路由表保持更新。我们还可以使用一些可以在公共云上完成工作的容器联网解决方案,例如Flannel的AWS和GCE后端,Weave的AWS-VPC模式等。

img

或者,可以如下图所示在每个节点上填充到其他子网的路由。同样,可以在小型/静态环境中自动更新路由,因为可以在群集中添加/删除节点,也可以使用容器网络解决方案(如calico)或Flannel主机网关后端。

img

III-叠加解决方案

除非有特殊原因使用覆盖解决方案,否则考虑到Kubernetes的网络模型通常是没有意义的,并且它缺乏对多个网络的支持。即使Pod位于覆盖网络中,Kubernetes要求节点应该能够到达每个Pod。同样,Pods也应该能够到达任何节点。我们将需要节点集中的主机路由,以便Pods和节点可以相互通信。由于主机间Pod到Pod的流量在底层结构中不可见,因此我们需要一个虚拟/逻辑网络,该网络覆盖在底层结构上。Pod到Pod的流量需要在源节点处封装。然后将封装的数据包转发到解封装的目标节点。可以围绕任何现有的Linux封装机制构建解决方案。我们需要有一个隧道接口(带有VXLAN,GRE等封装)和一个主机路由,以便通过隧道接口路由节点间Pod到Pod的流量。下面是关于如何构建可以满足Kubernetes网络要求的覆盖解决方案的非常概括的视图。与之前的两个解决方案不同,覆盖方法在建立隧道,填充FDB等方面花费了大量精力。现有的容器网络解决方案(如Weave和Flannel)可用于设置具有覆盖网络的Kubernetes部署。是一篇很好的文章,可以阅读更多有关类似Kubernetes主题的内容。

img

flannel

Flannel 是一个为 Kubernetes 提供 overlay network 的网络插件,它基于 Linux TUN/TAP,使用 UDP 封装 IP 包来创建 overlay 网络,并借助 etcd 维护网络的分配情况。

如何工作

Flannel 需要在集群中的每台主机上运行一个名为 flanneld 的代理程序,负责从预配置地址空间中为每台主机分配一个网段。Flannel 直接使用 Kubernetes API 或 ETCD 存储网络配置、分配的子网以及任何辅助数据(如主机的公网 IP)。数据包使用几种后端机制之一进行转发,包括 VXLAN 和各种云集成。

backend

Flannel 可以与几种不同的后端搭配。一旦后端设置完成,就不应在运行时更改。 VXLAN 是推荐的选择。对于有经验的用户,如果希望提高性能和基础架构支持(通常不能在云环境中使用),建议使用 host-gwUDP 建议仅用于调试,或者用于不支持 VXLAN 的非常旧的内核。 另外,还有实验性的 AWS、GCE 和 AliVPC,所有支持的后端可以 参考这里

VXLAN

使用内核的 VXLAN 封装数据包。 Type 和选项:

  • Type:字符串,vxlan
  • VNI:数字,要使用的 VXLAN Identifier (VNI) 。默认是 1。
  • Port:数字,用于发送封装的数据包 UDP 端口。默认值由内核决定,目前是 8472。
  • GBP:布尔值,启用 基于 VXLAN 组的策略。默认是 false。
  • DirectRouting:布尔值,当主机位于同一子网时,启用直接路由(类似 host-gw)。VXLAN 将仅用于将数据包封装到不同子网上的主机。默认为 false。
 ip r
default via 192.168.12.1 dev enp0s3
172.17.0.0/16 dev docker0  proto kernel  scope link  src 172.17.0.1 linkdown
192.168.12.0/24 dev enp0s3  proto kernel  scope link  src 192.168.12.102
host-gw
 ip r
default via 192.168.12.1 dev enp0s3
10.244.0.0/24 dev cni0  proto kernel  scope link  src 10.244.0.1
172.17.0.0/16 dev docker0  proto kernel  scope link  src 172.17.0.1 linkdown
192.168.12.0/24 dev enp0s3  proto kernel  scope link  src 192.168.12.102
default via 192.168.12.1 dev enp0s3
10.244.0.0/24 dev cni0  proto kernel  scope link  src 10.244.0.1
10.244.2.0/24 via 192.168.12.107 dev enp0s3
172.17.0.0/16 dev docker0  proto kernel  scope link  src 172.17.0.1 linkdown
192.168.12.0/24 dev enp0s3  proto kernel  scope link  src 192.168.12.102
ip r
default via 192.168.12.1 dev enp0s3
10.244.0.0/24 via 192.168.12.102 dev enp0s3
10.244.2.0/24 dev cni0  proto kernel  scope link  src 10.244.2.1
172.17.0.0/16 dev docker0  proto kernel  scope link  src 172.17.0.1 linkdown
192.168.12.0/24 dev enp0s3  proto kernel  scope link  src 192.168.12.107
cat /etc/cni/net.d/10-flannel.conflist
{
  "name": "cbr0",
  "plugins": [
    {
      "type": "flannel",
      "delegate": {
        "hairpinMode": true,
        "isDefaultGateway": true
      }
    },
    {
      "type": "portmap",
      "capabilities": {
        "portMappings": true
      }
    }
  ]
}

10: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default
    link/ether 3e:99:d6:c1:e9:bb brd ff:ff:ff:ff:ff:ff
    inet 10.244.0.0/32 scope global flannel.1
       valid_lft forever preferred_lft forever
ip r
default via 192.168.12.1 dev enp0s3
10.244.0.0/24 dev cni0  proto kernel  scope link  src 10.244.0.1
10.244.1.0/24 via 10.244.1.0 dev flannel.1 onlink
172.17.0.0/16 dev docker0  proto kernel  scope link  src 172.17.0.1 linkdown
192.168.12.0/24 dev enp0s3  proto kernel  scope link  src 192.168.12.102

host-gw

使用 host-gw 通过远程计算机 IP 创建到子网的 IP 路由。运行 flannel 的主机之间需要直接连接第 2 层(数据链路层)。(Use host-gw to create IP routes to subnets via remote machine IPs. Requires direct layer2 connectivity between hosts running flannel.) host-gw 性能好,依赖少,并且易于设置。 Type:

  • Type:字符串,host-gw 下面对 host-gw 和 vxlan 这两种 backend 做个简单比较。
  1. host-gw 把每个主机都配置成网关,主机知道其他主机的 subnet 和转发地址。vxlan 则在主机间建立隧道,不同主机的容器都在一个大的网段内(比如 10.2.0.0/16)。
  2. 虽然 vxlan 与 host-gw 使用不同的机制建立主机之间连接,但对于容器则无需任何改变,bbox1 仍然可以与 bbox2 通信。
  3. 由于 vxlan 需要对数据进行额外打包和拆包,性能会稍逊于 host-gw。 host-gw只支持二层互通的网络

UDP

不可用于生产环境。仅在内核或网络无法使用 VXLAN 或 host-gw 时,用 UDP 进行 debug。 Type 和选项:

  • Type:字符串,udp
  • Port:数字,用于发送封装数据包的 UDP 端口号,默认是 8285

Logging

  • 从容器运行 flannel 时,将安装 Strongswan 工具。swanctl 可用于与 charon 交互,并提供日志命令..
  • Charon 的日志也会写入 flannel 进程的标准输出。

Troubleshooting

  • ip xfrm state 可以用来与内核的安全关联数据库进行交互。这可以用来显示当前安全关联(SA)以及主机是否成功建立到其他主机的 IPsec 连接。
  • ip xfrm policy 可以用来显示已安装的策略。Flannel 为每个连接的主机安装三个策略。 Flannel 不会恢复手动删除的策略(除非重新启动 Flannel)。它也不会在启动时删除陈旧的策略。可以通过重新启动主机或通过 ip xfrm state flush && ip xfrm policy 策略刷新和重新启动 flannel 来删除所有 ipsec 状态来删除它们。 Flannel 可以被添加到任何已经存在的 Kubernetes 集群中。但是在 Pod 使用网络之前添加 Flannel 是最简单的。 对于 Kubernetes v1.7+:

Ref

Kubernetes: Flannel networking

https://blog.laputa.io/kubernetes-flannel-networking-6a1cb1f8ec7c

kubernetes flannel代码解析

https://ieevee.com/tech/2017/08/12/k8s-flannel-src.html

GCE 理解k8s的网络 https://medium.com/google-cloud/understanding-kubernetes-networking-pods-7117dd28727

https://github.com/eranyanay/cni-from-scratch.git

https://superuser.openstack.org/articles/review-of-pod-to-pod-communications-in-kubernetes/