Kubernetes kube-proxy 如何与 iptables 完美配合使用

我们知道 kube-proxy 是 Kubernetes 中一个运行在每个节点上的守护进程,它基本上反映了集群中定义的服务已经对后端 Pod 负载均衡的规则管理。

服务在后端 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 模式的主要对比
本文我们将专注于 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    <none>                 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:              <none>
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:                   <none>

这里有几个需要注意的事项:

  • 分配给 Service 的虚拟 IP 地址(VIP)是:10.98.141.188

  • 已分配给该 Service 的 NodePort 端口是 30966。通过这个端口,我们可以从集群的任何节点(本例中使用的集群节点的IP地址为192.168.64.35 和 192.168.64.36)访问 ghost 网页界面
    从集群的任一个节点访问 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 上的数据包。
KUBE-NODEPORTS 链
因此,每一个来自 30966 端口的数据包都会首先被 KUBE-MARK-MASQ 处理,它会给数据包打上了 0x4000 的标签。

注意:只有当负载均衡使用 IPVS 模式时,才会考虑到这个标记。
KUBE-MARK-MASQ 链
接下来,这个数据包由 KUBE-SVC-4XJR4EADNBDQKTKS 链(在上面的 KUBE-NODEPORTS 链中引用)进行处理。如果我们仔细看一下,可以看到多了两个 iptables 链。

  • KUBE-SEP-7I5NH52DVZSA3QHP
  • KUBE-SEP-PSCUKR75MU2ULAEX
    Service iptables 链负载均衡请求我们可以看到这里随机概率为0.5 的 statistic mode,因此进入 KUBE-SVC-4XJR4EADNBDQKTKS 链的每个数据包都有50%的概率被 KUBE-SEP-7I5NH52DVZSA3QHP 或者 KUBE-SEP-PSCUKR75MU2ULAEX (当它被第一个链忽略时)链进行处理。

如果我们检查这两条链,可以看到它们定义了 ghost 应用的底层 Pod 之一的路由。
路由至 Pod 10.44.0.3
路由至 Pod 10.44.0.4
通过几个 iptables 链,我们就能够了解一个请求从到达节点端口到底层 Pod的历程。

在本文希望我已经澄清了 kube-proxy 在使用 iptables 模式时的工作方式。接下来我们将看到当使用 ipvs 模式进行负载均衡时,路由是如何进行的工作。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

运维那些事~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值