Kubernetes的网络模型
前言:kubernetes作为云原生领域绕不开的一个重要内容,在私有云和公有云领域都是构建集群的主要工具,所以本文主要分析一下kubernetes 集群中的整体网络模型。
注:为了简化,kubernetes 后文写为k8s
k8s网络标准
K8s的网络接口是开放的,只要是符合其规范开发者可以自定义自己的CNI(container network interface)这也造就了云原生网络层面的百花齐放,不得不说k8s社区考虑的是真的厉害,下面是k8s的网络实现标准
- 所有的pod 都可以在不使用nat的情况下和所有其它的pod通信
- 所有的节点都可以在不适应nat的情况下和所有的pod通信
- pod认为自己的IP地址与其他人认为它的IP地址一致
k8s中的网络类型
- 容器内部的网络通信
- pod to pod
- pod to service
- 外部网络到容器网络
1 容器内部通信
在云原生领域容器有很多种,k8s也有自己的容器运行时标准(container runtime interface )但是其实实现原理都大同小异(都是在linux 的网络空间做文章),为了方便理解这里我们使用docker 容器作为讲解的例子
在k8s中pod 是最小的工作单位,但是一个pod中可以有很多容器,可能一个pod 中有多个容器相互配合才能正常完成工作,虽然k8s官方推荐的是一个pod对应一个容器,但是实际的生产中,多容器在同一个pod中也是有出现的。图1 为pod 内部之间容器之间的关系:
图1pod内部各个容器之间的关系
在pod 中必须有一个由kubelet拉起的pause 其余所有的容器都共享pause的网络空间,可以理解为docker 容器的other container 模式,所以容器之间的通信相对简单,大家共享同一网络空间,相当于内部通信
2 pod to pod
pod 作为k8s中最小的工作单位,每个pod都有一个真实的IP地址,而且每个pod 使用该IP地址与外部通信,但是pod 在同一宿主节点还是在不同的宿主节点上还是有很大的区别的,所以我们将pod 和pod 的通信分为,pod to remote pod(非同一宿主节点) pod to local pod(同一宿主节点)
2.1 pod to local pod
当pod 在同一宿主机时,同节点的网络通信损耗相对较小,网络模型也相对简单,具体网络流向如图2 所示
图2 Pod 和同节点的pod通信
这里以网络建立连接时的arp请求为例。
pod1的数据包发送到自己的以太网设备eth0(其实也是一个veth不理解的veth pair 的可以去看下上篇文章《linux网络虚拟化》);随后数据包到达对端veth 所在的网桥之上;网桥上的所有设备接受到arp请求,pod2 收到arp包之后发送响应包,自此网络成功打通
2.2 pod to remote pod
当pod在不同的宿主机时,pod 之间通信需要跨过一层主机网络,不同的cni 有不同的实现方式,这里我们只用两个常见的overlay和underlay实现方式来讲解
图3 跨节点的pod 之间的通信
pod1 将数据包发送至自己的以太网设备eth0;
到达网桥之后发现目的地址,不在当前宿主机
underlay 方式,之间通过路由方式将,当前包的下一条路由至对端节点;到达对端节点之后同样路由至网桥上;然后到达pod 3
overlay 方式(以vxlan为例)将数据包封装一层vxlan,目的地址封装为对端宿主机地址通过eth0发出;到达对端之后经由vlxan网卡解封装,路由至网桥;到达pod3
3 pod to service
上面我们展示了pod 之间的通信,但是我们要知道的是,pod作为k8s集群中最小的工作单位,在设计之初就被认为是不稳定的,宿主机节点的内存、网络、等等各种小问题都容易导致pod重启,这是pod 的IP很容易变化。为了应对这些变化service出现了
service 对应着一组pod,当访问service时流量会被打散到这组pod(分配不一定均匀,这个要结合下kube-proxy来看)
iptables:一个用户空间程序,它提供了一个基于表的系统,用于定义使用 netfilter 框架操作和转换数据包的规则。在 Kubernetes 中,iptables 规则由 kube-proxy 控制器配置,该控制器监视 Kubernetes API 服务器的更改,当kube-proxy使用iptables模式时,kube-proxy 会将对应service 的iptables规则注入到各个节点的iptables表中,当流量匹配到时,就会根据iptables表规则转发
ipvsIPVS(IP 虚拟服务器)也构建在 netfilter 之上,并将传输层负载平衡作为 Linux 内核的一部分实现。IPVS 被合并到 LVS(Linux 虚拟服务器)中,它在主机上运行并充当真实服务器集群前面的负载平衡器。IPVS 可以将基于 TCP 和 UDP 的服务的请求定向到真实服务器,并使真实服务器的服务在单个 IP 地址上表现为虚拟服务。这使得 IPVS 非常适合 Kubernetes 服务
当集群规模较大时,iptables模式会有明显的iptables规则过多导致查询缓慢的问题,缓慢是网络所不能容忍的,所以k8s在新版本开始支持ipvs,但是当前市面上应用较多的还是iptables
当然kubernetes service 也有多种的网络模式,为了方便讲述,这里以cluster ip service 为例如图4所示
图4 pod to service
当数据包到达网桥之后,被iptables 捕获到,经过nat之后,将流量穿发至后端pod所在节点
当访问node port模式的service 的是其实也是差不多的,只不过流量要先到node 上,然后再被iptables规则捕获,只是这样的话,pod 到node 再到 pod ,多经过了一次封装,网络性能又会下降一点
其实还有个loadbalance的service ,因为我自己也没遇到过,所以先不写
4 外部网络到容器网络
其实上面的几种网络模型,已经把私有云网络基本覆盖了,但是又是我们希望我们的服务能够暴露给外部流量,所以就有了下面的两种问题
4.1 egress 将集群流量转发到Internet
这里每个不同的CNI实现的都略有差异,但是总体来说还是nat 。当数据包到达宿主机之后,被iptables 拦截,经过snat之后源地址被修改为egress ip地址,随后通过数据网卡将流量转发至外部
4.2 ingress
ingress 在k8s官方文档中有很详细的文档介绍,这里我只做一个简单的介绍,我们把他理解成外部网络访问ingress,ingress 根据访问接口不同通过ingress 将流量分配到对应的业务service 中