k8s 中的 service 如何找到绑定的 Pod 以及如何实现 Pod 负载均衡

前言

Service 资源主要用于为 Pod 对象提供一个固定、统一的访问接口及负载均衡的能力。

service 是一组具有相同 label pod 集合的抽象,集群内外的各个服务可以通过 service 进行互相通信。

当创建一个 service 对象时也会对应创建一个 endpoint 对象,endpoint 是用来做容器发现的,service 只是将多个 pod 进行关联,实际的路由转发都是由 kubernetes 中的 kube-proxy 组件来实现,因此,service 必须结合 kube-proxy 使用,kube-proxy 组件可以运行在 kubernetes 集群中的每一个节点上也可以只运行在单独的几个节点上,其会根据 service 和 endpoints 的变动来改变节点上 iptables 或者 ipvs 中保存的路由规则。

endpoint

endpoint 是 k8s 集群中的一个资源对象,存储在 etcd 中,用来记录一个 service 对应的所有 pod 的访问地址。

service 通过 selector 和 pod 建立关联。k8s 会根据 service 关联到 pod 的 podIP 信息组合成一个 endpoint。

如果 service 没有 selector 字段,当一个 service 被创建的时候,endpoint controller 不会自动创建 endpoint。

$ kubectl get svc -n study-k8s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
go-web-svc ClusterIP 10.233.55.112 <none> 8000/TCP 9d $ kubectl get endpoints -n study-k8s
NAME ENDPOINTS AGE
go-web-svc 10.233.111.171:8000,10.233.111.172:8000,10.233.72.153:8000 + 2 more... 9d

栗如

上面的 service go-web-svc,就有一个对应的 endpoint,ENDPOINTS 里面展示的就是 service 关联的 pod 的 ip 地址和端口。

其中 endpoint controller 负载维护 endpoint 对象,主要的功能有下面几种

1、负责生成和维护所有endpoint对象的控制器;

2、负责监听 service 和对应 pod 的变化;

3、监听到 service 被删除,则删除和该 service 同名的 endpoint 对象;

4、监听到新的 service 被创建,则根据新建 service 信息获取相关 pod 列表,然后创建对应 endpoint 对象;

5、监听到 service 被更新,则根据更新后的 service 信息获取相关 pod 列表,然后更新对应 endpoint 对象;

6、监听到 pod 事件,则更新对应的 service 的 endpoint 对象,将 podIp 记录到 endpoint中;

kube-proxy

kube-proxy 是 Kubernetes 的核心组件,部署在每个 Node 节点上,它是实现 Kubernetes Service 的通信与负载均衡机制的重要组件; kube-proxy 负责为 Pod 创建代理服务,从 apiserver 获取所有 server 信息,并根据 server 信息创建代理服务,实现server到Pod的请求路由和转发,从而实现K8s层级的虚拟转发网络。

在 k8s 中提供相同服务的一组 pod 可以抽象成一个 service,通过 service 提供统一的服务对外提供服务,kube-proxy 存在于各个 node 节点上,负责为 service 提供 cluster 内部的服务发现和负载均衡,负责 Pod 的网络代理,它会定时从 etcd 中获取 service 信息来做相应的策略,维护网络规则和四层负载均衡工作。k8s 中集群内部的负载均衡就是由 kube-proxy 实现的,它是 k8s 中内部的负载均衡器,也是一个分布式代理服务器,可以在每个节点中部署一个,部署的节点越多,提供负载均衡能力的 Kube-proxy 就越多,高可用节点就越多。

简单点讲就是 k8s 内部的 pod 要访问 service ,kube-proxy 会将请求转发到 service 所代表的一个具体 pod,也就是 service 关联的 Pod。

同理对于外部访问 service 的请求,不论是 Cluster IP+TargetPort 的方式;还是用 Node 节点 IP+NodePort 的方式,都被 Node 节点的 Iptables 规则重定向到 Kube-proxy 监听 Service 服务代理端口。kube-proxy 接收到 Service 的访问请求后,根据负载策略,转发到后端的 Pod。

kube-proxy 的路由转发规则是通过其后端的代理模块实现的,其中 kube-proxy 的代理模块目前有四种实现方案,userspace、iptables、ipvs、kernelspace 。

userspace 模式

userspace 模式在 k8s v1.2 后就已经被淘汰了,userspace 的作用就是在 proxy 的用户空间监听一个端口,所有的 svc 都转到这个端口,然后 proxy 内部应用层对其进行转发。proxy 会为每一个 svc 随机监听一个端口,并增加一个 iptables 规则。

从客户端到 ClusterIP:Port 的报文都会通过 iptables 规则被重定向到 Proxy Port,Kube-Proxy 收到报文后,然后分发给对应的 Pod。

userspace 模式下,流量的转发主要是在用户空间下完成的,上面提到了客户端的请求需要借助于 iptables 规则找到对应的 Proxy Port,因为 iptables 是在内核空间,这里就会请求就会有一次从用户态到内核态再返回到用户态的传递过程, 一定程度降低了服务性能。所以就会认为这种方式会有一定的性能损耗。

默认情况下,用户空间模式下的 kube-proxy 通过轮转算法选择后端。

iptables

首先来简单了解下 iptables:

iptables 是 Linux 中最常用的一种防火墙工具,除了防火墙它还可以用作 IP 转发和简单的负载均衡功能。基于 Linux 中的 netfilter 内核模块实现。 Netfilter 在协议中添加了一些钩子,它允许内核模块通过这些钩子注册回调函数,这样经过钩子的所有数据都会被注册在响应钩子上的函数处理,包括修改数据包内容、给数据包打标记或者丢掉数据包等。iptables 是运行在用户态的一个程序,通过 netlink 和内核的 netfilter 框架打交道,具有足够的灵活性来处理各种常见的数据包操作和过滤需求。它允许将灵活的规则序列附加到内核的数据包处理管道中的各种钩子上。

Netfilter 是 Linux 2.4.x 引入的一个子系统,它作为一个通用的、抽象的框架,提供一整套的 hook 函数的管理机制,使得诸如数据包过滤、网络地址转换(NAT)和基于协议类型的连接跟踪成为了可能。

kubernetes v1.2 之后 iptables 成为默认代理模式,这种模式下,kube-proxy 会监视 Kubernetes master 对 Service 对象和 Endpoints 对象的添加和移除。 对每个 Service,它会安装 iptables 规则,从而捕获到达该 Service 的 clusterIP(虚拟 IP)和端口的请求,进而将请求重定向到 Service 的一组 backend 中的某个上面。因为流量转发都是在内核进行的,所以性能更高更加可靠。

可以看到该模式下 iptables 来做用户态的入口,kube-proxy 只是持续监听 Service 以及 Endpoints 对象的变化, iptables 通过设置的转发策略,直接将对 VIP 的请求转发给后端 Pod,iptables 使用 DNAT 来完成转发,其采用了随机数实现负载均衡。

如果 kube-proxy 在 iptables 模式下运行,并且所选的第一个 Pod 没有响应,则连接失败。 这与用户空间模式不同:在这种情况下,kube-proxy 将检测到与第一个 Pod 的连接已失败, 并会自动使用其他后端 Pod 重试。

该模式相比 userspace 模式,克服了请求在用户态-内核态反复传递的问题,性能上有所提升,但使用 iptables NAT 来完成转发,存在不可忽视的性能损耗,iptables 模式最主要的问题是在 service 数量大的时候会产生太多的 iptables 规则,使用非增量式更新会引入一定的时延,大规模情况下有明显的性能问题。

ipvs

当集群的规模比较大时,iptables 规则刷新就会很慢,难以支撑大规模的集群。因为 iptables 的底层实现是链表,对路由规则的增删查改都需要遍历一次链表。

kubernetes v1.2 之后 ipvs 成为kube-proxy的默认代理模式。ipvs 正是解决这一问题的,ipvs 是 LVS 的负载均衡模块,与 iptables 比较像的是,ipvs 的实现虽然也基于 netfilter 的钩子函数,但是它却使用哈希表作为底层的数据结构并且工作在内核态,也就是说 ipvs 在重定向流量和同步代理规则有着更好的性能,几乎允许无限的规模扩张。

ipvs 支持三种负载均衡模式:

1、DR模式(Direct Routing);

2、NAT 模式(Network Address Translation);

3、Tunneling(也称 ipip 模式)。

三种模式中只有 NAT 支持端口映射,所以 ipvs 使用 NAT 模式。linux 内核原生的 ipvs 只支持 DNAT,当在数据包过滤,SNAT 和支持 NodePort 类型的服务这几个场景中ipvs 还是会使用 iptables。

ipvs 也支持更多的负载均衡算法:

  • rr:round-robin/轮询;

  • lc:least connection/最少连接;

  • dh:destination hashing/目标哈希;

  • sh:source hashing/源哈希;

  • sed:shortest expected delay/预计延迟时间最短;

  • nq:never queue/从不排队

kernelspace

kernelspace 模式是 windows 上的代理模式,这里不展开讨论了

服务发现

service 的 endpoints 解决了容器发现问题,但是不提前知道 service 的 Cluster IP,就无法知道 service 服务了。Kubernetes 支持两种基本的服务发现模式 —— 环境变量和 DNS。

环境变量

当一个 pod 创建完成之后,kubelet 会在该 pod 中注册该集群已经创建的所有 service 相关的环境变量,但是需要注意的是,在 service 创建之前的所有 pod 是不会注册该环境变量的,所以在平时使用时,建议通过 DNS 的方式进行 service 之间的服务发现。

举个例子,一个名称为 redis-primary 的 Service 暴露了 TCP 端口 6379, 同时给它分配了 Cluster IP 地址 10.0.0.11,这个 Service 生成了如下环境变量:

REDIS_PRIMARY_SERVICE_HOST=10.0.0.11
REDIS_PRIMARY_SERVICE_PORT=6379
REDIS_PRIMARY_PORT=tcp://10.0.0.11:6379
REDIS_PRIMARY_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_PRIMARY_PORT_6379_TCP_PROTO=tcp
REDIS_PRIMARY_PORT_6379_TCP_PORT=6379
REDIS_PRIMARY_PORT_6379_TCP_ADDR=10.0.0.11

DNS

可以在集群中部署 CoreDNS 服务(旧版本的 kubernetes 群使用的是 kubeDNS), 来达到集群内部的 pod 通过DNS 的方式进行集群内部各个服务之间的通讯。

当前 kubernetes 集群默认使用 CoreDNS 作为默认的 DNS 服务,主要原因是 CoreDNS 是基于 Plugin 的方式进行扩展的,简单,灵活,并且不完全被Kubernetes所捆绑。

同时 k8s 中也建议使用 DNS 来做服务发现。

Kubernetes DNS 服务器是唯一的一种能够访问 ExternalName 类型的 Service 的方式。

总结

k8s 中一般使用 Service 为 Pod 对象提供一个固定、统一的访问接口及负载均衡的能力;

k8s 中的负载均衡主要借助于 endpoint 和 kube-proxy 来实现;

endpoint 是 k8s 集群中的一个资源对象,存储在 etcd 中,用来记录一个 service 对应的所有 pod 的访问地址,当一个 service 关联的 pod 被删除,更新,新增,对应的 endpoint 资源都会更新;

kube-proxy 是 Kubernetes 的核心组件,部署在每个 Node 节点上,它是实现 Kubernetes Service 的通信与负载均衡机制的重要组件; kube-proxy 负责为 Pod 创建代理服务,从 apiserver 获取所有 server 信息,并根据 server 信息创建代理服务,实现server到Pod的请求路由和转发,从而实现K8s层级的虚拟转发网络;

kube-proxy 的路由转发规则是通过其后端的代理模块实现的,其中 kube-proxy 的代理模块目前有四种实现方案,userspace、iptables、ipvs、kernelspace ;

service 的 endpoints 和 kube-proxy 解决了容器的发现和负载均衡的问题,但是 service 服务如何被内部的服务找到呢,Kubernetes 支持两种基本的服务发现模式 —— 环境变量和 DNS;

其中 k8s 中推荐使用 DNS 来做 service 的服务发现,当前 kubernetes 集群默认使用 CoreDNS 作为默认的 DNS 服务,主要原因是 CoreDNS 是基于 Plugin 的方式进行扩展的,简单,灵活,并且不完全被Kubernetes所捆绑。

参考

【kubernetes service 原理解析】https://zhuanlan.zhihu.com/p/111244353

【service selector】https://blog.csdn.net/luanpeng825485697/article/details/84296765

【一文看懂 Kube-proxy】https://zhuanlan.zhihu.com/p/337806843

【Kubernetes 【网络组件】kube-proxy使用详解】https://blog.csdn.net/xixihahalelehehe/article/details/115370095

【Service】https://jimmysong.io/kubernetes-handbook/concepts/service.html

【Service】https://kubernetes.io/zh-cn/docs/concepts/services-networking/service/

【k8s 中的 service 如何找到绑定的 Pod 以及如何实现 Pod 负载均衡】https://boilingfrog.github.io/2022/10/16/k8s中的service如何找到绑定的Pod以及如何实现Pod负载均衡/

k8s 中的 service 如何找到绑定的 Pod 以及如何实现 Pod 负载均衡的更多相关文章

  1. 【Kubernetes】在K8s中创建StatefulSet

    在K8s中创建StatefulSet 遇到的问题: 使用Deployment创建的Pod是无状态的,当挂在Volume之后,如果该Pod挂了,Replication Controller会再run一个 ...

  2. 在K8s中创建StatefulSet

    在K8s中创建StatefulSet 遇到的问题: 使用Deployment创建的Pod是无状态的,当挂在Volume之后,如果该Pod挂了,Replication Controller会再run一个 ...

  3. k8s 中的 ingress 使用细节

    k8s中的ingress 什么是ingress Ingress 如何使用 ingress 使用细节 参考 k8s中的ingress 什么是ingress k8s 中使用 Service 为相同业务的 ...

  4. Spring Cloud Ribbon 中的 7 种负载均衡策略

    负载均衡通器常有两种实现手段,一种是服务端负载均衡器,另一种是客户端负载均衡器,而我们今天的主角 Ribbon 就属于后者--客户端负载均衡器. 服务端负载均衡器的问题是,它提供了更强的流量控制权,但 ...

  5. K8S中Service

    Service 的概念Kubernetes  Service  定义了这样一种抽象:一个  Pod  的逻辑分组,一种可以访问它们的策略 —— 通常称为微服务. 这一组  Pod  能够被  Serv ...

  6. k8s中几个基本概念的理解,pod,service,deployment,ingress的使用场景

    k8s 总体概览 前言 Pod 副本控制器(Replication Controller,RC) 副本集(Replica Set,RS) 部署(Deployment) 服务(Service) ingr ...

  7. k8s中ingress,service,depoyment,pod如何关联

    k8s中pod通过label标签名称来识别关联,它们的label  name一定是一样的.ingress,service,depoyment通过selector 中app:name来关联 1.查询发布 ...

  8. [转帖]k8s 中的服务如何沟通

    k8s 中的服务如何沟通 https://www.jianshu.com/p/9fae09876eb7 本文将介绍 k8s 中的服务如何相互访问,例如后端服务访问数据库,不同类型的服务间的相互访问.并 ...

  9. 深入理解k8s中的访问控制(认证、鉴权、审计)流程

    Kubernetes自身并没有用户管理能力,无法像操作Pod一样,通过API的方式创建/删除一个用户实例,也无法在etcd中找到用户对应的存储对象. 在Kubernetes的访问控制流程中,用户模型是 ...

随机推荐

  1. YII学习总结3(session)

    session操作 <?php namespace app\controllers; use yii\web\Controller; class HelloController extends ...

  2. 使用.NET简单实现一个Redis的高性能克隆版(二)

    译者注 该原文是Ayende Rahien大佬业余自己在使用C# 和 .NET构建一个简单.高性能兼容Redis协议的数据库的经历. 首先这个"Redis"是非常简单的实现,但是他 ...

  3. Postman如何通过xmysql工具的Restful API 接口访问MySQL

    GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. 导语 有时候用 Postman 接口测试需要获取MySQL的查询结果做接口输出的校验,这里介绍下 Postman 通过 R ...

  4. JavaScript 里三个点 ...,可不是省略号啊···

    摘要:Three dots ( - ) in JavaScript. 本文分享自华为云社区<JavaScript 里三个点 ... 的用法>,作者: Jerry Wang . Rest P ...

  5. 高效简单的.Net数据库“访问+操作”技术

    本文技术源自外企,并已在多个世界500强大型项目开发中运用. 本文适合有初步C#.Linq.Sql知识的同学阅读. 相关技术在IDataAccess接口中提供. IDataAccess所在的命名空间是 ...

  6. 一例智能网卡(mellanox)的网卡故障分析

    一例智能网卡(mellanox)的网卡故障分析 背景:这个是在centos 7.6.1810的环境上复现的,智能网卡是目前很多 云服务器上的网卡标配,在oppo主要用于vpc等场景,智能网卡的代码随着 ...

  7. [CF1526F] Median Queries(交互 / 构造)

    题面 这是一道交互题. 有一个未知的长度为 N \tt N N 的排列 P \tt P P,已知 P 1 < P 2 \tt P_1 < P_2 P1​<P2​ . 每次询问格式为 ...

  8. Java-往数据库插入日期

    Java-往数据库中插入日期 将字符串类型的时间转换成mysql的日期格式 String str = "2022-6-11"; SimpleDateFormat sdf = new ...

  9. Rust 从入门到精通06-语句和表达式

    1.语句和表达式 语句和表达式是 Rust 语言实现逻辑控制的基本单元. 在 Rust 程序里面,语句(Statement)是执行一些操作但不返回的指令,表达式(Expressions)计算并产生一个 ...

  10. DispatcherServlet 分发流程

    0 太长不看版 HTTPServlet 的 Service 方法将请求按类进行分解 主要是根据HTTP方法的类型调用 doXXX 方法 GET 和 HEAD 方法需要对 if-modified-sin ...