作者:容鑫,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 wideNAME                                 READY   STATUS    RESTARTS        AGE   IP            NODE             NOMINATED NODE   READINESS GATEShttpbin-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 httpbinNAME      ENDPOINTS                                      AGEhttpbin   10.1.36.125:80,10.1.36.131:80,10.1.36.133:80   23d

从上述示例能够看到,Endpoints 中 httpbin 资源对象的所有网络端点,别离对应了每个 Pod 的 IP 地址。

当然, Endpoints 也有它的一些不足之处,比方:

  1. Endpoints 具备容量限度,如果某个 Endpoints 资源中端口的个数超过 1000,那么 Endpoints 控制器会将其截断为 1000。
  2. 一个 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.yamlapiVersion: apps/v1kind: Deploymentmetadata:  name: httpbin-deploymentspec:  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: v1kind: Servicemetadata:  name: httpbinspec:  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.comhelm repo add bitnami https://charts.bitnami.com/bitnamihelm repo updatekubectl create ns ingress-apisixhelm 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/v2kind: ApisixRoutemetadata:  name: httpbin-routespec:  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: IPv4apiVersion: discovery.k8s.io/v1endpoints:- 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: EndpointSlicemetadata:  labels:    endpointslice.kubernetes.io/managed-by: endpointslice-controller.k8s.io    kubernetes.io/service-name: httpbin  name: httpbin-dkvtr  namespace: defaultports:- name: http  port: 80  protocol: TCP

总结

本文介绍了 Kubernetes 须要部署大量 Pod 的场景和遇到的问题,比照了 Endpoints 和 Endpointslice 之间区别,以及如何装置 APISIX Ingress 来应用 Endpointslice 的个性。

如果你的集群版本为 Kubernetes v1.21+,举荐在装置 APISIX Ingress 时开启 Endpointslice 个性反对,这样就不必关注 --max-endpoints-per-slice 标记设定的值,从而防止配置失落。