简单示例说明 kube-proxy 如何与 iptables 配合使用
我们知道 kube-proxy 是 Kubernetes 中一个运行在每个节点上的守护进程,它基本上反映了集群中定义的服务已经对后端 Pod 负载均衡的规则管理。
假设我们有几个 API 微服务的 Pods 运行在我们的集群中,这些 Pods 的副本通过一个 Service 服务暴露,当一个请求到达 Service 的虚拟 IP 时,如何将请求转发到其中一个底层 Pod?其实就是通过 kube-proxy 创建的规则,虽然表面上并没有那么简单,但是我们还是可以进行大致的了解。
kube-proxy 可以在三种不同的模式下运行。
iptables IPvs userspace(不再推荐)
虽然 iptables 模式对于许多集群和工作负载来说完全没有问题,但当服务数量很多时(超过1,000个),ipvs 就会很有优势了,由于 iptables 规则是按顺序读取的,如果集群中存在许多服务,那么它的使用会影响路由性能。
Tigera(Calico 的创建者和维护者)在这篇很棒的文章(https://www.tigera.io/blog/comparing-kube-proxy-modes-iptables-or-ipvs/)中详细介绍了 iptables 和 ipvs 模式的区别。
本文我们将专注于 iptables 模式(下一篇文章将专门介绍 ipvs 模式)来说明 kube-proxy 是如何定义 iptables 规则。
为此,我们将使用我刚刚用 kubeadm 创建的一个双节点集群。
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-1 Ready control-plane,master 57s v1.20.0
k8s-2 Ready 41s v1.20.0
接下来我们将部署一个简单的应用程序,并通过 NodePort 类型的服务将其暴露出来。
示例
首先,我们创建一个基于 ghost 镜像的 Deployment(ghost 是一个免费开源的博客平台)并指定两个副本。
$ kubectl create deploy ghost --image=ghost --replicas=2
然后使用 NodePort 类型的 Service 来暴露 Pods。
$ kubectl expose deploy/ghost \
--port 80 \
--target-port 2368 \
--type NodePort
部署完成后就可以获取这个新创建的 Service 的相关信息了。
$ kubectl describe svc ghost
Name: ghost
Namespace: default
Labels: app=ghost
Annotations:
Selector: app=ghost
Type: NodePort
IP: 10.98.141.188
Port: <unset> 80/TCP
TargetPort: 2368/TCP
NodePort: <unset> 30966/TCP
Endpoints: 10.44.0.3:2368,10.44.0.4:2368
Session Affinity: None
External Traffic Policy: Cluster
Events:
这里有几个需要注意的事项:
分配给 Service 的虚拟 IP 地址(VIP)是:10.98.141.188
已分配给该 Service 的 NodePort 端口是 30966。通过这个端口,我们可以从集群的任何节点(本例中使用的集群节点的IP地址为192.168.64.35 和 192.168.64.36)访问 ghost 网页界面
Endpoints 属性显示了 Service 所暴露的 Pod 的 IP 地址。换句话说,每个到达 Service 的虚拟 IP (10.98.141.188) 端口 80 的请求都会以随机的方式转发到 2368 端口的底层 Pod 的 IP (10.44.0.3 或 10.44.0.4)。
注意:我们也可以使用标准的 kubectl get 命令来查询 Endpoints 信息。
$ kubectl get endpoints
NAME ENDPOINTS AGE
ghost 10.44.0.3:2368,10.44.0.4:2368 4m
kubernetes 192.168.64.35:6443 6m
接下来,我们将仔细研究一下 kube-proxy 创建的 iptables 规则,以便将请求路由到后端 Pods。
iptables 规则
每次创建/删除 Service 或修改 Endpoints 时(例如,如果由于相关应用的 scale 而导致底层 Pod 数量发生变化),kube-proxy 都会负责更新集群每个节点上的 iptables 规则。
让我们看看我们之前定义的 Service是如何完成的。由于有相当多的 iptables 链生成,这里我们只考虑主要涉及到的请求的路由,这些请求在 NodePort 上得到并被转发到其中一个底层 Pods。
首先,KUBE-NODEPORTS
链就是来处理 NodePort 类型的 Service 上的数据包。
因此,每一个来自 30966 端口的数据包都会首先被 KUBE-MARK-MASQ
处理,它会给数据包打上了 0x4000
的标签。
注意:只有当负载均衡使用 IPVS 模式时,才会考虑到这个标记。
接下来,这个数据包由 KUBE-SVC-4XJR4EADNBDQKTKS
链(在上面的 KUBE-NODEPORTS
链中引用)进行处理。如果我们仔细看一下,可以看到多了两个 iptables 链。
KUBE-SEP-7I5NH52DVZSA3QHP
KUBE-SEP-PSCUKR75MU2ULAEX
我们可以看到这里随机概率为0.5 的 statistic mode,因此进入 KUBE-SVC-4XJR4EADNBDQKTKS
链的每个数据包都有50%的概率被 KUBE-SEP-7I5NH52DVZSA3QHP
或者 KUBE-SEP-PSCUKR75MU2ULAEX
(当它被第一个链忽略时)链进行处理。
如果我们检查这两条链,可以看到它们定义了 ghost 应用的底层 Pod 之一的路由。
通过几个 iptables 链,我们就能够了解一个请求从到达节点端口到底层 Pod的历程。
在本文希望我已经澄清了 kube-proxy 在使用 iptables 模式时的工作方式。接下来我们将看到当使用 ipvs 模式进行负载均衡时,路由是如何进行的工作。
“原文链接:https://medium.com/better-programming/k8s-a-closer-look-at-kube-proxy-372c4e8b090
”
进阶训练营第二期
本次训练营采用线上直播的形式,基于1.19.x
版本,根据第1期课程的打磨,我们总结出了 Docker 基础 + Kubernetes 基础 + 原理 + 基本使用 + 进阶技能 + 完整项目实践 的课程体系。加强系统知识吸收、夯实基础的同时,并在实际操作过程中去了解排查问题的方式方法,更为重要的是我们的老师非常负责任,随时帮你答疑解惑,我们认为不只是课堂上讲授知识,更重要的是售后支持,完全不用担心学习不到知识。