APISIX Ingress 是如何支持上千个 Pod 副本的应用
作者:容鑫,Apache APISIX Committer
在 K8s 中为什么会遇到上千个 Pod 副本的应用场景?
在 Kubernetes 中,Pod 是最小的调度单元。应用程序实际是以 Pod 在运行的,通常情况下出于可扩展性和降低爆炸半径等方面的考虑,只会给 Pod 设置有限的资源。那么对于大流量的场景,一般都是通过水平扩容的方式进行应对。
例如电商行业在进行促销活动或秒杀抢购活动时,业务流量相对较大。为了应对这种场景,通常会设置弹性扩容。在活动进行时,服务会进行弹性伸缩直到能够承载流量,这时会基于弹性扩容的策略,为业务增加副本数,也就是 Pod 会变多。
每个 Pod 都有各自唯一的 IP ,但同时 Pod 的 IP 也不是固定的。为了及时追踪 Pod IP 的变化,从而进行负载均衡,Endpoints API 提供了在 Kubernetes 中跟踪网络端点的一种简单而直接的方法。 但随着 Kubernetes 集群和服务逐渐开始为更多的后端 Pod 进行处理和发送请求,比如上文提到大流量场景下,Pod 数量会被不断扩容,Endpoints API 也将变得越大。这种情况下,Endpoints API 局限性变得越来越明显,甚至成为性能瓶颈。
为了解决这个局限性问题,在 Kubernetes v1.21 的版本中引入了对 Endpointslice API 的支持,解决了 Endpoints API 处理大量网络端点带来的性能问题,同时提供了可扩展和可伸缩的能力。 通过下图我们可以明显看到它们之间的区别:
Endpoints 在流量高峰时的变化:
Endpointslices 在流量高峰时的变化:
在 Kubernetes 中,应用之间是如何进行相互访问的呢?Endpoints 和 Endpointslice 具体区别又是什么?和 Pod 有着什么样的关系?APISIX Ingress 中为什么要支持这些特性,以及如何进行安装和使用?本文将着重介绍这些问题。
Kubernetes 中如何访问应用
在 Kubernetes 中,每个 Pod 都有其自己唯一的 IP 地址。通常情况下,Service 通过 selector 和一组 Pod 建立关联,并提供了相同的 DNS 名,并可以在它之间进行负载均衡。Kubernetes 集群内不同应用之间可通过 DNS 进行相互访问。
在 Service 创建时,Kubernetes 会根据 Service 关联一个 Endpoints 资源,若 Service 没有定义 selector 字段,将不会自动创建 Endpoints。
什么是 Endpoints
Endpoints 是 Kubernetes 中的一个资源对象,存储在 etcd 中,用来记录一个 Service 对应一组 Pod 的访问地址,一个 Service 只有一个 Endpoints 资源。Endpoints 资源会去观测 Pod 集合,只要服务中的某个 Pod 发生变更,Endpoints 就会进行同步更新。 比如部署 3 个 httpbin 副本,查看 Pod 的情况,包括 IP 信息。
$ kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES httpbin-deployment-fdd7d8dfb-8sxxq 1/1 Running 0 49m 10.1.36.133 docker-desktop <none> <none> httpbin-deployment-fdd7d8dfb-bjw99 1/1 Running 4 (5h39m ago) 23d 10.1.36.125 docker-desktop <none> <none> httpbin-deployment-fdd7d8dfb-r5nf9 1/1 Running 0 49m 10.1.36.131 docker-desktop <none> <none>
创建 httpbin 服务,并查看应 Endpoints 端点情况。
$ kubectl get endpoints httpbin NAME ENDPOINTS AGE httpbin 10.1.36.125:80,10.1.36.131:80,10.1.36.133:80 23d
从上述示例可以看到,Endpoints 中 httpbin 资源对象的所有网络端点,分别对应了每个 Pod 的 IP 地址。
当然, Endpoints 也有它的一些不足之处,比如:
- Endpoints 具有容量限制,如果某个 Endpoints 资源中端口的个数超过 1000,那么 Endpoints 控制器会将其截断为 1000。
- 一个 Service 只有一个 Endpoints 资源,这意味着它需要为支持相应服务的每个 Pod 存储 IP 等网络信息。这导致 Endpoints 资源变的十分巨大,其中一个端点发生了变更,将会导致整个 Endpoints 资源更新。当业务需要进行频繁端点更新时,一个巨大的 API 资源被相互传递,而这会影响到 Kubernetes 组件的性能,并且会产生大量的网络流量和额外的处理。
什么是 Endpointslices
Endpointslices 为 Endpoints 提供了一种可扩缩和可拓展的替代方案,缓解处理大量网络端点带来的性能问题,还能为一些诸如拓扑路由的额外功能提供一个可扩展的平台。该特性在 Kubernetes v1.21+ 的版本中已提供支持。
EndpointSlice 旨在通过分片的方式来解决此问题,并没有使用单个 Endpoints 资源跟踪服务的所有网络端点,而是将它们拆分为多个较小的 EndpointSlice。
默认情况下,控制面创建和管理的 EndpointSlice 将包含不超过 100 个端点。 你可以使用 kube-controller-manager 的 --max-endpoints-per-slice
标志设置此值,其最大值为 1000。
为什么需要 Endpointslices
首先,我们考虑具有 2000 个 Pod 的服务它最终可能具有 1.0 MB 的 Endpoints 资源。在生产环境中,如果该服务发生滚动更新或节点迁移,那么 Endpoints 资源将会频繁变更。
想象一下,如果滚动更新会导致全部 Pod 都被替换,由于 etcd 具有最大请求大小限制,Kubernetes 对 Endpoints 最大容量限制为 1000,如果网络端点数量超出了 1000,那么多出来的网络端点,将不会被 Endpoints 资源记录。
当然也可能因为一些需求,需要多次进行滚动更新,那么这个巨大的 API 资源对象将会 Kubernetes 组件中来回传递,极大影响了 Kubernetes 组件的性能。
如果使用了 Endpointslices,假设一个服务后端有 2000 个 Pod。如果将配置修改为每个 Endpointslices 存储 100 个端点,最终将获得 20 个 Endpointslices。添加或删除 Pod 时,只需要更新其中 1 个 Endpointslice 资源即可,这样操作后,可扩展性和网络可伸缩有了很大的提升。
比起在流量高峰时,服务为了承载流量,扩容出大量的 Pod,Endpoints 资源会被频繁更新,两个使用场景的差异就变得非常明显。更重要的是,既然服务的所有 Pod IP 都不需要存储在单个资源中,那么我们就不必担心 etcd 中存储的对象的大小限制。
Endpoints VS Endpointslice
因为 Endpointslice 是在 Kubernetes v1.21+ 的版本得到支持,所以该结论是基于 Kubernetes v1.21+ 版本。
通过上文的描述我们总结一下两种资源的适用情况。
Endpoints 适用场景:
- 有弹性伸缩需求,Pod 数量较少,传递资源不会造成大量网络流量和额外处理。
- 无弹性伸缩需求,Pod 数量不会太多。哪怕 Pod 数量是固定,但是总是要滚动更新或者出现故障的。
Endpointslice 适用场景:
- 有弹性需求,且 Pod 数量较多(几百上千)。
- Pod 数量很多(几百上千),因为 Endpoints 网络端点最大数量限制为 1000,所以超过 1000 的 Pod 必须得用 Endpointslice。
在 APISIX Ingress 中的实践
APISIX Ingress Controller 是一个 Ingress 控制器的实现。可以将用户配置的规则转换为 Apache APISIX中的规则,从而使用 APISIX 完成具体的流量承载。
在具体实现过程中,APISIX Ingress 通过 watch Endpoints 或 Endpointslice 资源的变化,从而让 APISIX 能够对 Pod 进行负载均衡和健康检查等。为了能够支持 Kubernetes v1.16+ 的版本,APISIX Ingress 在安装时,默认使用 Endpoints 的特性。
如果你的集群版本为 Kubernetes v1.21+,在安装 APISIX Ingress 时,需要指定 watchEndpointSlice=true
标志来开启 Endpointslice 特性的支持。
注意:在集群的版本为 Kubernetes v1.21+ 时,我们推荐使用 Endpointslice 特性。否则当服务中 Pod 数量超过
--max-endpoints-per-slice
标志设定的值时,由于 APISIX Ingress watch 的是 Endpoints 资源对象,则会导致配置的丢失。开启此特性,就不额外关注--max-endpoints-per-slice
的值。
以下将为你简单介绍如何在 APISIX Ingress 中应用 Endpointslice 特性。
创建包含 20 个副本的 Service
使用 kubectl apply -f httpbin-deploy.yaml
命令,在 Kuernetes 中部署 httpbin 应用服务,并创建 20 个 Pod 副本。具体 htppbin-deploy.yaml
文件配置可参考下方代码。
# htppbin-deploy.yaml apiVersion: apps/v1 kind: Deployment metadata: name: httpbin-deployment spec: replicas: 20 selector: matchLabels: app: httpbin-deployment strategy: rollingUpdate: maxSurge: 50% maxUnavailable: 1 type: RollingUpdate template: metadata: labels: app: httpbin-deployment spec: terminationGracePeriodSeconds: 0 containers: - livenessProbe: failureThreshold: 3 initialDelaySeconds: 2 periodSeconds: 5 successThreshold: 1 tcpSocket: port: 80 timeoutSeconds: 2 readinessProbe: failureThreshold: 3 initialDelaySeconds: 2 periodSeconds: 5 successThreshold: 1 tcpSocket: port: 80 timeoutSeconds: 2 image: "kennethreitz/httpbin:latest" imagePullPolicy: IfNotPresent name: httpbin-deployment ports: - containerPort: 80 name: "http" protocol: "TCP" --- apiVersion: v1 kind: Service metadata: name: httpbin spec: selector: app: httpbin-deployment ports: - name: http port: 80 protocol: TCP targetPort: 80 type: ClusterIP
通过 APISIX Ingress 代理
第一步,使用 Helm 安装 APISIX Ingress。 通过 ·--set ingress-controller.config.kubernetes.watchEndpointSlice=true` 开启 Endpointslice 特性的支持。
helm repo add apisix https://charts.apiseven.com helm repo add bitnami https://charts.bitnami.com/bitnami helm repo update kubectl create ns ingress-apisix helm install apisix apisix/apisix \ --set gateway.type=NodePort \ --set ingress-controller.enabled=true \ --namespace ingress-apisix \ --set ingress-controller.config.apisix.serviceNamespace=ingress-apisix \ --set ingress-controller.config.kubernetes.watchEndpointSlice=true
第二步,使用 CRD 资源进行代理。注意,APISIX Ingress 中对 Endpoints 和 Endpointslice 特性的支持,使用者是感知不到的,它们的配置都是一致的。
apiVersion: apisix.apache.org/v2 kind: ApisixRoute metadata: name: httpbin-route spec: http: - name: rule match: hosts: - httpbin.org paths: - /get backends: - serviceName: httpbin servicePort: 80
第三步,进入 APISIX Pod 中查看。可以看到在 APISIX 中的 upstream 对象 nodes 字段包含了 20 个 Pod 的 IP 地址。
kubectl exec -it ${Pod for APISIX} -n ingress-apisix -- curl "http://127.0.0.1:9180/apisix/admin/upstreams" -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1'
{ "action": "get", "count": 1, "node": { "key": "\/apisix\/upstreams", "nodes": [ { "value": { "hash_on": "vars", "desc": "Created by apisix-ingress-controller, DO NOT modify it manually", "pass_host": "pass", "nodes": [ { "weight": 100, "host": "10.1.36.100", "priority": 0, "port": 80 }, { "weight": 100, "host": "10.1.36.101", "priority": 0, "port": 80 }, { "weight": 100, "host": "10.1.36.102", "priority": 0, "port": 80 }, { "weight": 100, "host": "10.1.36.103", "priority": 0, "port": 80 }, { "weight": 100, "host": "10.1.36.104", "priority": 0, "port": 80 }, { "weight": 100, "host": "10.1.36.109", "priority": 0, "port": 80 }, { "weight": 100, "host": "10.1.36.92", "priority": 0, "port": 80 } ... // 以下省略 13 个节点 // 10.1.36.118 // 10.1.36.115 // 10.1.36.116 // 10.1.36.106 // 10.1.36.113 // 10.1.36.111 // 10.1.36.108 // 10.1.36.114 // 10.1.36.107 // 10.1.36.110 // 10.1.36.105 // 10.1.36.112 // 10.1.36.117 ], "labels": { "managed-by": "apisix-ingress-controller" }, "type": "roundrobin", "name": "default_httpbin_80", "scheme": "http" }, "key": "\/apisix\/upstreams\/5ce57b8e" } ], "dir": true } }
上述信息与 Endpointslice 中的网络端点相对应。
addressType: IPv4 apiVersion: discovery.k8s.io/v1 endpoints: - addresses: - 10.1.36.92 ... - addresses: - 10.1.36.100 ... - addresses: - 10.1.36.104 ... - addresses: - 10.1.36.102 ... - addresses: - 10.1.36.101 ... - addresses: - 10.1.36.103 ... - addresses: - 10.1.36.109 ... - addresses: - 10.1.36.118 ... - addresses: - 10.1.36.115 ... - addresses: - 10.1.36.116 ... - addresses: - 10.1.36.106 ... - addresses: - 10.1.36.113 ... - addresses: - 10.1.36.111 ... - addresses: - 10.1.36.108 ... - addresses: - 10.1.36.114 ... - addresses: - 10.1.36.107 ... - addresses: - 10.1.36.110 ... - addresses: - 10.1.36.105 ... - addresses: - 10.1.36.112 ... - addresses: - 10.1.36.117 ... kind: EndpointSlice metadata: labels: endpointslice.kubernetes.io/managed-by: endpointslice-controller.k8s.io kubernetes.io/service-name: httpbin name: httpbin-dkvtr namespace: default ports: - name: http port: 80 protocol: TCP
总结
本文介绍了 Kubernetes 需要部署大量 Pod 的场景和遇到的问题,对比了 Endpoints 和 Endpointslice 之间区别,以及如何安装 APISIX Ingress 来使用 Endpointslice 的特性。
如果你的集群版本为 Kubernetes v1.21+,推荐在安装 APISIX Ingress 时开启 Endpointslice 特性支持,这样就不用关注 --max-endpoints-per-slice
标志设定的值,从而避免配置丢失。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
优先级反转那些事儿
从一个线上问题说起 最近在线上遇到了一些[HMDConfigManager remoteConfigWithAppID:]卡死 初步分析 观察了下主线程堆栈,用到的锁是读写锁随后又去翻了下持有着锁的子线程,有各种各样的情况,且基本都处于正常的执行状态,例如有的处于打开文件状态,有的处于read状态,有的正在执行NSUserDefaults的方法...通过观察发现,出问题的线程都有QOS:BACKGROUND标记。整体看起来持有锁的子线程仍然在执行,只是留给主线程的时间不够了。为什么这些子线程在持有锁的情况下,需要执行这么久,直到主线程的8s卡死?一种情况就是真的如此耗时,另一种则是出现了优先级反转。 解决办法 在这个案例里面,持有读写锁且优先级低的线程迟迟得不到调度(又或者得到调度的时候又被抢占了,或者得到调度的时候时间已然不够了),而具有高优先级的线程由于拿不到读写锁,一直被阻塞,所以互相死锁。iOS8之后引入了QualityOfService的概念,类似于线程的优先级,设置不同的QualityOfService的值后系统会分配不同的CPU时间、网络资源和硬盘资源等,因此我们可以通过...
- 下一篇
Linkis & DolphinScheduler强强联合,探索计算治理难题的终极密码!
2022 年 12 月 1 日,Apache Linkis(Incubating)&Apache DolphinScheduler线上 Meetup 活动即将来袭!来自两个社区的核心贡献者将带来项目整合的技术分享,聚焦解决计算治理难题的方法,干货满满! 在大数据平台的建设中,计算引擎能力的构建是极其重要的,DolphinScheduler 整合了越来越多的计算中间件,帮助中间件串行任务,提高大数据计算能力,提升效率。 Linkis 是由微众银行开源的计算中间件产品,其设计理念弥补了开源社区中这类软件的空白,在底层平台和上层应用之间建立起了标准化、可复用的转换模式。 更多内容请到linkis.apache.org了解 可视化易操作的任务调度系统和计算中间件相结合,可以大幅提高大数据计算能力和处理能力。强强联合之下,将会碰撞出怎样的火花呢?敬请期待! 活动时间 12月1日(周四)20:00-21:30 活动形式 线上多平台直播(WeDataSphere、DS、SegmentFault 思否、开源中国、开源基础软件社区视频号、开源中国网站、B站) 报名通道 加入直播交流群↓ 活动简...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- Windows10,CentOS7,CentOS8安装Nodejs环境
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- CentOS关闭SELinux安全模块
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS7,8上快速安装Gitea,搭建Git服务器
- SpringBoot2全家桶,快速入门学习开发网站教程
- 设置Eclipse缩进为4个空格,增强代码规范
- SpringBoot2配置默认Tomcat设置,开启更多高级功能