ACK集群的性能和可用性与集群资源数量、资源访问频率、访问模式等紧密相关。不同变量组合下,API Server承载的压力和性能差异不同。在大规模的ACK集群Pro版(通常为超过500个节点或者10,000个Pod的集群)中,集群管理者需要根据业务实际状况合理规划和使用规模化集群,密切关注监控指标,确保集群的稳定性和可用性。
大规模集群使用须知
相较于使用多个集群,构建一个大规模集群可以有效减少集群管理运维负担,提高资源利用率。但在某些复杂的业务场景中,建议您根据业务逻辑或需求将服务拆分到多个集群中,例如非生产(测试)服务与生产(开发)服务拆分、数据库服务与前端应用拆分等。
如您有以下维度的考量,我们更建议您使用多个集群,而非单一的大规模集群。
分类 | 说明 |
隔离性 | 使用多个集群可以确保不同集群(例如生产集群和测试集群)的隔离性,避免某个集群的问题影响全部业务,降低故障爆炸半径。 |
位置 | 某些服务需要部署在离终端用户更近的特定地理位置,以满足可用性、低延时的需求。在此场景下,推荐您在多个地区部署多个集群。 |
单集群规模上限 | ACK托管控制面通过弹性伸缩和集群组件的性能优化来自适应集群规模。但Kubernetes架构存在性能瓶颈,超大的集群规模可能会影响集群的可用性和性能。在使用大规模集群前,您应该了解Kubernetes社区定义的容量限制与SLO,并前往配额平台查看并申请提高容器服务 Kubernetes 版的配额上限。如果超出社区和ACK的限制,请拆分业务,使用多个集群。 |
如您需要多集群管理,例如应用部署、流量管理、作业分发、全局监控等,建议您启用多集群舰队。
本文使用指引
本文主要面向ACK集群Pro版的集群开发和管理人员,提供规划和使用大规模集群的通用性建议,实际情况仍以您的集群环境和业务需求为准。
使用新版本集群
随着Kubernetes版本不断升级,容器服务ACK会定期发布支持的Kubernetes版本,并逐步停止对过期版本的技术支持。对于过期版本,ACK将逐步停止发布新功能特性,停止修复功能缺陷、安全缺陷,且仅提供有限的技术支持。
请您通过帮助文档、控制台信息、站内信等渠道关注版本发布情况,并在集群升级前了解相应版本的升级注意事项,及时完成版本升级,避免潜在的集群安全和稳定性问题。关于集群升级的更多信息,请参见手动升级集群、自动升级集群;关于Kubernetes版本支持信息,请参见Kubernetes版本概览及机制。
关注集群资源限制
为保证大规模集群的可用性、稳定性和各项性能,请关注下表列出的限制及对应的推荐解决方案。
限制项 | 说明 | 推荐解决方案 |
etcd数据库大小(DB Size) | etcd数据库大小上限为8 GB。当etcd数据库过大时,会影响其性能,包括数据读取和写入延迟、系统资源占用、选举延时等,也会导致服务和数据恢复过程更加困难和耗时。 | 控制etcd DB Size的总大小在8 GB以下。
|
etcd中每种类型资源的数据总大小 | 如果资源对象总量过大,客户端全量访问该资源时会导致大量的系统资源消耗,严重时可能会导致API Server或自定义控制器(Controller)无法初始化。 | 控制每种资源类型的对象总大小在800 MB以下。
|
API Server CLB的连接数和带宽 | 目前,ACK集群API Server使用的负载均衡类型为CLB,其连接数与带宽有最大容量限制。CLB最大连接数可参见CLB实例概述,最大带宽为5120Mbps。 超出CLB连接数或者带宽限制可能会造成节点Not Ready。 | 对于1,000节点以上的集群,建议选择按使用量计费的CLB实例。 说明 为提升网络连接速度和带宽,大规模集群访问Default命名空间中的Kubernetes服务时,应使用ENI直连模式。在2023年02月以后新建的,v1.20以上的集群中,新建集群已默认使用ENI直连;存量集群的切换请提交工单。更多信息,请参见Kube API Server。 |
每个命名空间的Service数量 | kubelet会把集群中定义的Service的相关信息以环境变量的形式注入到运行在该节点上的Pod中,让Pod能够通过环境变量来发现Service,并与之通信。 每个命名空间中的服务数量过多会导致为Pod注入的环境变量过多,继而可能导致Pod启动缓慢甚至失败 | 将每个命名空间的Service数量控制在5,000以下。 您可以选择不填充这些环境变量,将 |
集群的总Service数量 | Service数量过多会导致kube-proxy需要处理的网络规则增多,继而影响kube-proxy的性能。 对于LoadBalancer类型的Service,Service数量过多会导致Service同步到SLB的时延增加,延迟可能达到分钟级别。 | 将所有Service的总数量控制在10,000以下。 对LoadBalancer类型的Service,建议将Service总数控制在500以下。 |
单个Service的Endpoint最大数量 | 每个节点上运行着kube-proxy组件,用于监视(Watch)Service的相关更新,以便及时更新节点上的网络规则。当某个Service存在很多Endpoint时,Service相应的Endpoints资源也会很大,每次对Endpoints对象的更新都会导致控制面kube-apiserver和节点kube-proxy之间传递大量流量。集群规模越大,需要更新的相关数据越多,风暴效应越明显。 说明 为了解决此问题,kube-proxy在v1.19以上的集群中默认使用EndpointSlices来提高性能。 | 将单个Service的Endpoints的后端Pod数量控制在3,000以下。
|
所有Service Endpoint的总数量 | 集群中Endpoint数量过多可能会造成API Server负载压力过大,并导致网络性能降低。 | 将所有Service关联的Endpoint的总数量控制在64,000以下。 |
Pending Pod的数量 | Pending Pod数量过多时,新提交的Pod可能会长时间处于等待状态,无法被调度到合适的节点上。在此过程中,如果Pod无法被调度,调度器会周期性地产生事件(event),继而可能导致事件泛滥(event storm)。 | 将Pending Pod的总数量控制在10,000以下。 |
启用了使用阿里云KMS进行Secret的落盘加密的集群中的Secret数量 | 使用KMS v1加密数据时,每次加密都会生成一个新的数据加密密钥(DEK)。Kubernetes集群启动时,需要访问并解密存储在etcd中的Secret。如果集群存储的Secret过多,集群在启动或升级时需要解密大量的数据,影响集群性能。 | 将开启KMS V1加密的集群中存储的Secret数量控制在2,000以下。 |
合理设置管控组件参数
ACK集群Pro版提供自定义Pro版集群的控制面组件参数功能,支持修改kube-apiserver、kube-controller-manager、kube-scheduler等核心托管组件的参数。在大规模集群中,您需要合理调整管控组件限流的相关参数。
kube-apiserver
为了避免大量并发请求导致控制面超载,kube-apiserver限制了它在特定时间内可以处理的并发请求数。一旦超出此限制,API Server将开始限流请求,向客户端返回429 HTTP响应码,即请求过多,并让客户端稍后再试。如果服务端没有任何限流措施,可能导致控制面因处理超出其承载能力的请求而过载,严重影响整个服务或集群的稳定性以及可用性。因此,推荐您在服务端配置限流机制,避免因控制面崩溃而带来更广泛的问题。
限流分类
kube-apiserver的限流分为两种。
v1.18以下:kube-apiserver仅支持最大并发度限流,将请求区分为读类型和写类型,通过启动参数
--max-requests-inflight
和--max-mutating-requests-inflight
限制读写请求的最大并发度。该方式不区分请求优先级。某些低优先级的慢请求可能占用大量资源,造成API Server请求堆积,导致一些更高优先级或更紧急的请求无法及时处理。ACK集群Pro版支持自定义kube-apiserver的max-requests-inflight和max-mutating-requests-inflight的参数配置。更多信息,请参见自定义Pro版集群的控制面组件参数。
v1.18及以上:引入APF(API优先级和公平性)机制来进行更细粒度的流量管理,支持根据预设的规则和优先级来分类和隔离请求,从而确保更重要和紧急的请求优先处理,同时遵循一定的公平性策略,保证不同类型的请求都能够得到合理的处理机会。该特性于v1.20进入Beta阶段,默认开启。
限流监测与推荐解决方案
客户端可以通过返回状态码429或通过监控指标apiserver_flowcontrol_rejected_requests_total
来判断服务端是否有限流行为。当观测到有限流行为时,可以通过以下方式解决。
监控API Server资源用量:当资源用量较低时,可调整
max-requests-inflight
和max-mutating-requests-inflight
参数总和提高总的限流值。对于500个节点以上的集群,建议参数总和配置为2000~3000之间;对于3000个节点以上的集群,建议参数总和配置在3000~5000之间。
重新配置PriorityLevelConfiguration:
高优先级请求:为不期望限流的请求划分新的FlowSchema,匹配高优先级的PriorityLevelConfiguration,例如
workload-high
或exempt
。但exempt
的请求不会被APF限流,请谨慎配置。您也可以为高优先级的请求配置新的PriorityLevelConfiguration,给予更高的并发度。低优先级请求:当的确存在某些客户端慢请求导致API Server资源用量高或响应慢时,可以为该类请求新增一个FlowSchema,并为该FlowSchema匹配低并发度的PriorityLevelConfiguration。
ACK集群Pro版会为您托管kube-apiserver组件。kube-apiserver默认为多AZ高可用,会保证至少2个副本,并随着控制面资源使用率增大而逐渐调整至最大6个副本。
可并发的总实际请求值 = 副本数单个副本总请求量
。修改kube-apiserver的自定义参数会触发API Server的滚动更新,可能导致客户端Controller重新进行List-Watch操作。在大规模集群下,这可能会造成API Server负载过高,导致其服务暂时不可用。
kube-controller-manager和kube-scheduler
kube-controller-manager和kube-scheduler分别通过kubeAPIQPS/kubeAPIBurst、connectionQPS/connectionBurst参数控制与API Server通信的QPS。更多信息,请参见自定义Pro版集群的控制面组件参数、自定义调度器参数。
kube-controller-manager:对于1000个节点以上的集群,建议将kubeAPIQPS/kubeAPIBurst调整为300/500以上。
kube-scheduler:一般无需调整。Pod速率超过300/s时建议将connectionQPS/connectionBurst调整为800/1000。
kubelet
kubelet组件的kube-api-burst/qps
默认值为5/10,一般无需调整。当您的集群出现Pod状态更新缓慢、调度延迟、存储卷挂载缓慢等显著性能问题时,建议您调大参数。操作步骤及说明,请参见自定义节点池kubelet配置。
调大kubelet该参数会增大kubelet与API Server的通信QPS。如果kubelet发送的请求数量过多,可能会增大API Server的负载。建议您逐步增加取值,并关注API Server的性能和资源用量,确保控制面稳定性。
对节点kubelet执行变更操作时,需合理控制更新频率。为保证变更过程中控制面的稳定性,ACK限制单节点池每批次的最大并行数不超过10。
规划集群资源弹性速率
在大规模集群中,当集群处于稳定运行状态时,通常不会给控制面带来太大的压力。但当集群进行大规模变更操作时,例如快速创建或删除大量资源,或者大规模扩缩集群节点数时,可能会造成控制面压力过大,导致集群性能下降、响应延迟,甚至服务中断。
例如,在一个5,000个节点的集群中,如果存在大量固定数量的Pod且保持稳定运行状态,那么它对控制面的压力通常不会太大。但在一个1,000个节点的集群中,如果需要在一分钟内创建10,000个短暂运行的Job,或并发扩容2,000个节点,那么控制面的压力会激增。
因此,在大规模集群中进行资源变更操作时,需根据集群运行状态谨慎规划弹性操作的变更速率,以确保集群和控制面的稳定性。
推荐操作如下。
由于集群控制面影响因素较多,以下数字仅供参考。实际操作时,请遵循变更速率由小到大的操作顺序,观察控制面响应正常后再适当提高弹性速率。
节点扩缩容:对于2000个节点以上的集群,建议在通过节点池手动扩缩容节点时,单个节点池单次操作的节点数不超过100,多个节点池单次操作的总节点数不超过300。
应用Pod扩缩容:若您的应用关联了Service,扩缩容过程中Endpoint、EndpointSlice的更新会推送到所有节点。在节点数量多的场景下,需要更新的相关数据也很多,可能导致集群风暴效应。对5000节点以上的集群,建议非Endpoint关联的Pod更新QPS不超过300/s;Endpoints关联的Pod更新QPS不超过10/s。例如,在Deployment中声明Pod滚动更新(Rolling Update)策略时,推荐您先设置较小的
maxUnavailable
和maxSurge
取值,以降低Pod更新速率。
优化客户端访问集群模式
在Kubernetes集群中,客户端(客户业务应用程序或kubectl等)通过API Server来获取集群资源信息。随着集群中的资源数量的增加,如果客户端以相同的频率进行请求,这些请求可能会给集群控制面带来更大的负载,导致控制面响应延迟,甚至导致管控面雪崩。在访问集群资源时,需了解并规划访问资源的大小和频率。大规模集群使用建议如下。
优先使用informer访问本地缓存数据
优先使用client-go的informer获取资源,通过本地缓存(Cache)查询数据,避免List请求直接访问API Server,以减少API Server的负载压力。
优化通过API Server获取资源的方式
对于未访问过的本地缓存,仍需直接通过API Server获取资源。但可以遵循以下建议。
在List请求中设置
resourceVersion=0
。resourceVersion
表示资源状态的版本。设置为0
时,请求会获取API Server的缓存数据,而非直接访问etcd,减少API Server与etcd之间的内部交互次数,更快地响应客户端List请求。示例如下。k8sClient.CoreV1().Pods("").List(metav1.ListOptions{ResourceVersion: "0"})
避免全量List资源,防止数据检索量过大。
为了减少请求返回的数据量,应使用过滤条件(Filter)来限定List请求的范围,例如lable-selector(基于资源标签筛选)或field-selector(基于资源字段筛选)。
说明etcd是一个键值(KV)存储系统,本身不具备按label或field过滤数据的功能,请求带有的过滤条件实际由API Server处理。因此,当使用Filter功能时,建议同时将List请求的
resourceVersion
设置为0
。请求数据将从API Server的缓存中获取,而不会直接访问etcd,减少对etcd的压力。使用Protobuf(而非JSON)访问非CRD资源。
API Server可以以不同的数据格式向客户端返回资源对象,包括JSON和Protobuf。默认情况下,当客户端请求Kubernetes API时,Kubernetes返回序列化为JSON的对象,其内容类型(Content-Type)为
application/json
。 客户端可以指定请求使用Protobuf格式的数据,Protobuf在内存使用和网络传输流量方面相较JSON更具优势。但并非所有API资源类型都支持Protobuf。发送请求时,可以在
Accept
请求头中指定多种内容类型(例如application/json
、application/vnd.kubernetes.protobuf
),从而支持在无法使用Protobuf时回退到默认的JSON格式。更多信息,请参见Alternate representations of resources 。示例如下。Accept: application/vnd.kubernetes.protobuf, application/json
使用中心化控制器
避免在每个节点上都创建独立的控制器用于Watch集群的全量数据。在这种情况下,控制器启动时,将几乎同时向API Server发送大量的List请求以同步当前的集群状态,对控制面造成巨大压力,继而导致服务不稳定或崩溃。
为了避免此问题,建议采用中心化的控制器设计,为整个集群创建一个或一组集中管理的控制器实例,运行在单个节点或少数几个节点上。中心化的控制器会负责监听和处理所需的集群数据,仅启动一次(或少数几次)List请求,并仅维护必要数量的Watch连接,大大减少了对API Server的压力。
合理规划大规模工作负载
停用自动挂载默认的Service Account
为了确保Pod中的Secret的同步更新,kubelet会为Pod配置的每个Secret建立一个Watch长连接。Watch机制可以让kubelet实时接收Secret的更新通知。但当所有节点创建的Watch数量总和过多时,大量Watch连接可能会影响集群控制面的性能。
Kubernetes 1.22版本以前:创建Pod时,如果未指定ServiceAccount,Kubernetes会自动挂载默认的ServiceAccount作为Pod的Secret,使得Pod内部的应用能够与API Server安全通信。
对于批处理系统和无需访问API Server的业务Pod,建议您显式声明禁止自动挂载ServiceAccount Token,以避免创建相关的Secret和Watch(更多信息,请参见automountServiceAccountToken)。在大规模集群中,该操作可以避免创建不必要的Secret和与API Server的Watch连接,减轻集群控制面的负担。
Kubernetes 1.22及之后:使用TokenRequest API来获取一个短期的、自动轮换的令牌,并以投射卷的形式挂载此令牌。在提高Secret安全性的同时,该操作还能减少kubelet为每个ServiceAccount的Secret建立的Watch连接,降低集群的性能开销。
关于如何启用ServiceAccount Token投射卷功能,请参见使用ServiceAccount Token卷投影。
控制Kubernetes Object数量和大小
请及时清理无需使用的Kubernetes资源,例如ConfigMap、Secret、PVC等,减少系统资源的占用,保持集群健康、高效运转。有以下使用建议。
限制Deployment历史记录数:revisionHistoryLimit声明了要为Deployment保留多少个旧的ReplicaSet。如果取值太高,Kubernetes会保留很多历史版本的ReplicaSet,增加kube-controller-manager的管理负担。在大规模集群中,如果Deployment较多且更新频繁,您可以调低Deployment的revisionHistoryLimit的取值,清理旧的ReplicaSet。Deployment的revisionHistoryLimit默认取值为10。
清理无需使用的Job和相关的Pod:如果集群中通过CronJob或其他机制创建了大量的作业(Job)对象,请使用ttlSecondsAfterFinished来自动清理集群中较旧的Pod,指定在某个时间周期后Job和相关的Pod将完成自动清理(删除)。
合理配置Informer类型组件的资源配置
Informer类型的组件主要用于监控和同步Kubernetes集群的资源状态。Informer类组件会建立对集群API Server资源状态的Watch连接,并在本地维护一个资源对象的缓存,以便快速响应资源状态的变化。
对于Informer类型的组件,例如Controller组件、kube-scheduler等,组件内存占用与其Watch的资源大小相关。大规模集群下,请关注该类组件的内存资源消耗,避免组件OOM。组件频繁OOM会导致组件持续资源监听出现问题。组件频繁重启时,每次执行的List-Watch操作也会对集群控制面(尤其是API Server)造成额外的压力。
关注集群控制面指标
您可以查看控制面组件监控大盘,获取控制面核心组件的指标清单、异常指标问题解析等。在大规模集群中,请重点关注以下指标。关于指标的使用说明、详细说明,请参见控制面组件监控。
管控资源用量
目前管控组件所有资源用量均可以查看,相关指标和介绍如下:
指标名称 | PromQL | 说明 |
内存使用量 | memory_utilization_byte{container="kube-apiserver"} | API Server的内存使用量。单位:字节。 |
CPU使用量 | cpu_utilization_core{container="kube-apiserver"}*1000 | API Server的CPU使用量。单位:毫核。 |
kube-apiserver
关于如何查看指标及指标的完整说明,请参见kube-apiserver组件监控指标说明。
资源对象数量
名称
PromQL
说明
资源对象数量
max by(resource)(apiserver_storage_objects)
max by(resource)(etcd_object_counts)
当ACK为1.22及以上版本时, 指标名字为apiserver_storage_objects
当ACK为1.22及以下版本时,指标名字为etcd_object_counts。
说明由于兼容性问题,1.22版本中apiserver_storage_objects名称和etcd_object_counts名称均存在。
请求时延
名称
PromQL
说明
GET读请求时延
histogram_quantile($quantile, sum(irate(apiserver_request_duration_seconds_bucket{verb="GET",resource!="",subresource!~"log|proxy"}[$interval])) by (pod, verb, resource, subresource, scope, le))
展示GET请求的响应时间,维度包括API Server Pod、Verb(GET)、Resources、Scope。
LIST读请求时延
histogram_quantile($quantile, sum(irate(apiserver_request_duration_seconds_bucket{verb="LIST"}[$interval])) by (pod_name, verb, resource, scope, le))
展示LIST请求的响应时间,维度包括API Server Pod、Verb(LIST)、Resources、Scope。
写请求时延
histogram_quantile($quantile, sum(irate(apiserver_request_duration_seconds_bucket{verb!~"GET|WATCH|LIST|CONNECT"}[$interval])) by (cluster, pod_name, verb, resource, scope, le))
展示Mutating请求的响应时间,维度包括API Server Pod、Verb(GET、WATCH、LIST、CONNECT)、Resources、Scope。
请求限流
名称
PromQL
说明
请求限流速率
sum(irate(apiserver_dropped_requests_total{request_kind="readOnly"}[$interval])) by (name)
sum(irate(apiserver_dropped_requests_total{request_kind="mutating"}[$interval])) by (name)
API Server的限流速率 ,
No data
或者0
表示没有限流。
kube-scheduler
关于如何查看指标及指标的完整说明,请参见kube-scheduler组件监控指标说明。
Pending Pod数
名称
PromQL
说明
Scheduler Pending Pods
scheduler_pending_pods{job="ack-scheduler"}
Pending Pod的数量。队列种类如下:
unschedulable:不可调度的Pod数量。
backoff:backoffQ的Pod数量,即因为某种原因暂时不能被调度的Pod数量。
active:activeQ的Pod数量,即准备就绪并等待被调度的Pod数量。
请求时延
名称
PromQL
说明
Kube API 请求时延
histogram_quantile($quantile, sum(rate(rest_client_request_duration_seconds_bucket{job="ack-scheduler"}[$interval])) by (verb,url,le))
kube-scheduler对kube-apiserver组件发起的HTTP请求时延,从方法(Verb)和请求URL维度分析。
kube-controller-manager
关于如何查看指标及指标的完整说明,请参见kube-controller-manager组件监控指标说明。
Workqueue
名称 | PromQL | 说明 |
Workqueue深度 | sum(rate(workqueue_depth{job="ack-kube-controller-manager"}[$interval])) by (name) | Workqueue深度在单位时间内的变化。 |
Workqueue处理时延 | histogram_quantile($quantile, sum(rate(workqueue_queue_duration_seconds_bucket{job="ack-kube-controller-manager"}[5m])) by (name, le)) | 事件在Workqueue中存在的时长。 |
etcd
关于如何查看指标及指标的完整说明,请参见etcd组件监控指标说明。
KV总数
名称
PromQL
说明
kv总数
etcd_debugging_mvcc_keys_total
etcd集群KV对(键对)总数。
数据库大小(DB Size)
名称
PromQL
说明
磁盘大小
etcd_mvcc_db_total_size_in_bytes
etcd Backend DB总大小,即etcd后端数据库总大小。
etcd_mvcc_db_total_size_in_use_in_bytes
etcd Backend DB实际使用大小,即etcd后端数据库实际使用大小。
相关文档
关于ACK集群的配额与限制,请参见配额与限制。
关于如何合理规划集群VPC网络及容器网络,请参见Kubernetes集群网络规划。
关于如何实现集群创建、工作负载的高可靠配置,请参见工作负载推荐配置。