简单示例说明 kube-proxy 如何与 iptables 配合使用

k8s技术圈

共 3661字,需浏览 8分钟

 ·

2021-01-16 15:08

我们知道 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                     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 网页界面

    从集群的任一个节点访问 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 模式进行负载均衡时,路由是如何进行的工作。

原文链接:https://medium.com/better-programming/k8s-a-closer-look-at-kube-proxy-372c4e8b090


进阶训练营第二期

本次训练营采用线上直播的形式,基于1.19.x版本,根据第1期课程的打磨,我们总结出了 Docker 基础 + Kubernetes 基础 + 原理 + 基本使用 + 进阶技能 + 完整项目实践 的课程体系。加强系统知识吸收夯实基础的同时,并在实际操作过程中去了解排查问题的方式方法,更为重要的是我们的老师非常负责任,随时帮你答疑解惑,我们认为不只是课堂上讲授知识,更重要的是售后支持,完全不用担心学习不到知识。


 点击屏末  | 即刻学习

浏览 87
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报