

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# Kubernetes 控制平面
<a name="scale-control-plane"></a>

**提示**  
 [通过亚马逊 EKS 研讨会探索](https://aws-experience.com/emea/smb/events/series/get-hands-on-with-amazon-eks?trk=4a9b4147-2490-4c63-bc9f-f8a84b122c8c&sc_channel=el)最佳实践。

Kubernetes 控制平面由 Kubernetes API 服务器、Kubernetes 控制器管理器、调度器和 Kubernetes 运行所需的其他组件组成。这些组件的可扩展性限制因您在集群中运行的内容而异，但对扩展影响最大的领域包括 Kubernetes 版本、利用率和单个节点扩展。

您可以采用以下两种模式之一运行集群的控制平面以满足不同的工作负载要求：
+  **标准模式 **-默认情况下，所有 EKS 集群都使用标准模式。控制平面会根据您的工作负载需求自动向上和向下扩展。标准模式可动态分配足够的控制平面容量，是大多数用例的推荐选项。
+  **预置模式 ** — 如果您的工作负载无法容忍控制平面扩展带来的性能变化，或者它们需要非常高的控制平面容量，则可以使用预置模式。在预置模式下，您可以预先分配控制平面容量，随时准备应对苛刻的需求。您将获得稳定且可预测的性能。

在 EKS 配置模式下，您可以从一组扩展层（XL、2XL、4XL 和 8XL）中进行选择。在每个层中，您都可以从集群的控制平面获得高且可预测的性能。预置模式对于以下用例特别有价值：
+ Performance-critical 工作负载
+ Large-scale 人工智能和机器学习操作
+ 预期的高需求活动
+ 需要在试运行和生产之间保持一致性的环境

在预置模式下，您可以提前预分配控制平面容量，并受益于以 1 分钟为间隔衡量的增强版 99.99% 服务水平协议 (SLA)。有关 EKS 预置模式的更多信息，包括等级规格和定价，请参阅 [ EKS 用户指南](https://docs.aws.amazon.com/eks/latest/userguide/eks-provisioned-control-plane.html)中的 EK * S 预置控制平面。*

## 限制工作负载和节点爆发
<a name="_limit_workload_and_node_bursting"></a>

**重要**  
为避免在控制平面上达到 API 限制，您应该限制一次将集群大小增加两位数百分比的扩展峰值（例如，1000 个节点增加到 1100 个节点或一次增加 4000 到 4500 个 Pod）。

EKS 控制平面将随着集群的增长自动扩展，但其扩展速度有限制。当你首次创建 EKS 集群时，控制平面将无法立即扩展到数百个节点或数千个 Pod。要详细了解 EKS 如何改进扩展，请参阅[这篇博客文章](https://aws.amazon.com/blogs/containers/amazon-eks-control-plane-auto-scaling-enhancements-improve-speed-by-4x/)。

扩展大型应用程序需要调整基础架构才能完全准备就绪（例如预热负载均衡器）。要控制扩展速度，请确保根据应用程序的正确指标进行扩展。CPU 和内存扩展可能无法准确预测您的应用程序限制，在 Kubernetes Horizontes Pod Autoscaler (HPA) 中使用自定义指标（例如每秒请求数）可能是更好的扩展选项。

要使用自定义指标，请参阅 [ Kubernetes 文档中的示例。](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/#autoscaling-on-multiple-metrics-and-custom-metrics)如果您有更高级的扩展需求或需要根据外部来源（例如 AWS SQS 队列）进行扩展，则使用 [ KEDA ](https://keda.sh) 进行基于事件的工作负载扩展。

## 安全地向下扩展节点和容器
<a name="_scale_nodes_and_pods_down_safely"></a>

### 替换长时间运行的实例
<a name="_replace_long_running_instances"></a>

定期更换节点可避免配置偏差和只有在延长正常运行时间后才会发生的问题（例如缓慢的内存泄漏），从而保持集群的健康。自动替换将为您提供节点升级和安全补丁的良好流程和实践。如果定期更换集群中的每个节点，则维护单独进程以进行持续维护所需的辛勤工作就会减少。

使用 Karpenter 的生存[时间 (TTL) ](https://docs.aws.amazon.com/eks/latest/best-practices/karpenter.html#_creating_nodepools) 设置在实例运行一段指定时间后替换实例。自管理节点组可以使用该`max-instance-lifetime`设置自动循环节点。托管节点组目前没有此功能，但您可以[在此处跟踪请求 GitHub](https://github.com/aws/containers-roadmap/issues/1190)。

### 移除未充分利用的节点
<a name="_remove_underutilized_nodes"></a>

当节点没有正在运行的工作负载时，您可以使用 Kubernetes 集群 Autoscaler 中的向下扩展阈值移除节点，[`--scale-down-utilization-threshold`](https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/FAQ.md#how-does-scale-down-work)也可以在 Karpenter 中使用配置器设置。`ttlSecondsAfterEmpty`

### 使用 pod 中断预算和安全关闭节点
<a name="_use_pod_disruption_budgets_and_safe_node_shutdown"></a>

从 Kubernetes 集群中移除 pod 和节点需要控制器更新多个资源（例如）。 EndpointSlices频繁或过快地执行此操作可能会导致 API 服务器节流和应用程序中断，因为更改会传播到控制器。[Pod 中断预算](https://kubernetes.io/docs/concepts/workloads/pods/disruptions/)是在集群中移除或重新安排节点时减缓流失速度以保护工作负载可用性的最佳实践。

## 运行 Kubectl 时使用 Client-Side 缓存
<a name="_use_client_side_cache_when_running_kubectl"></a>

低效地使用 kubectl 命令会给 Kubernetes API 服务器增加额外的负载。你应该避免运行重复使用 kubectl 的脚本或自动化（例如在 for 循环中），或者在没有本地缓存的情况下运行命令。

 `kubectl`具有客户端缓存，用于缓存来自集群的发现信息，以减少所需的 API 调用量。缓存默认处于启用状态，每 10 分钟刷新一次。

如果你从容器或没有客户端缓存的情况下运行 kubectl，你可能会遇到 API 限制问题。建议通过挂载来保留集群缓存，`--cache-dir`以避免进行不必要的 API 调用。

## 禁用 kubectl 压缩
<a name="_disable_kubectl_compression"></a>

在 kubeconfig 文件中禁用 kubectl 压缩可以降低 API 和客户端 CPU 的使用率。默认情况下，服务器将压缩发送到客户端的数据以优化网络带宽。这会增加每个请求的客户端和服务器上的 CPU 负载，如果您有足够的带宽，禁用压缩可以减少开销和延迟。要禁用压缩，你可以使用 kubeconfig `disable-compression: true` 文件中的`--disable-compression=true`标志或设置。

```
apiVersion: v1
clusters:
- cluster:
    server: serverURL
    disable-compression: true
  name: cluster
```

## 分片集群自动缩放器
<a name="_shard_cluster_autoscaler"></a>

[Kubernetes 集群 Autoscaler 已经过测试，](https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/proposals/scalability_tests.md)可以扩展到 1000 个节点。在拥有 1000 个以上节点的大型集群上，建议在分片模式下运行集群 Autoscaler 的多个实例。每个集群 Autoscaler 实例都配置为扩展一组节点组。以下示例显示了为每个扩展 4 个节点组配置的 2 个集群自动扩展配置。

ClusterAutoscaler-1

```
autoscalingGroups:
- name: eks-core-node-grp-20220823190924690000000011-80c1660e-030d-476d-cb0d-d04d585a8fcb
  maxSize: 50
  minSize: 2
- name: eks-data_m1-20220824130553925600000011-5ec167fa-ca93-8ca4-53a5-003e1ed8d306
  maxSize: 450
  minSize: 2
- name: eks-data_m2-20220824130733258600000015-aac167fb-8bf7-429d-d032-e195af4e25f5
  maxSize: 450
  minSize: 2
- name: eks-data_m3-20220824130553914900000003-18c167fa-ca7f-23c9-0fea-f9edefbda002
  maxSize: 450
  minSize: 2
```

ClusterAutoscaler-2

```
autoscalingGroups:
- name: eks-data_m4-2022082413055392550000000f-5ec167fa-ca86-6b83-ae9d-1e07ade3e7c4
  maxSize: 450
  minSize: 2
- name: eks-data_m5-20220824130744542100000017-02c167fb-a1f7-3d9e-a583-43b4975c050c
  maxSize: 450
  minSize: 2
- name: eks-data_m6-2022082413055392430000000d-9cc167fa-ca94-132a-04ad-e43166cef41f
  maxSize: 450
  minSize: 2
- name: eks-data_m7-20220824130553921000000009-96c167fa-ca91-d767-0427-91c879ddf5af
  maxSize: 450
  minSize: 2
```

## API 优先级和公平性
<a name="_api_priority_and_fairness"></a>

![APF](http://docs.aws.amazon.com/zh_cn/eks/latest/best-practices/images/scalability/APF.jpg)


### 概述
<a name="_overview"></a>

为了保护自己在请求增加期间不被超载，API Server 限制了在给定时间可以处理的机上请求的数量。一旦超过此限制，API 服务器将开始拒绝请求，并将 “请求过多” 的 429 HTTP 响应代码返回给客户端。服务器丢弃请求并让客户端稍后重试比没有服务器端对请求数量的限制和控制平面过载更可取，后者可能会导致性能下降或不可用。

Kubernetes 用来配置如何将这些机上请求划分为不同请求类型的机制称为 [ API 优先级和公平性。](https://kubernetes.io/docs/concepts/cluster-administration/flow-control/)API 服务器通过将和标志指定的值相加来配置其可以接受的机上请求总数。`--max-requests-inflight` `--max-mutating-requests-inflight`EKS 对这些标志使用 400 和 200 个请求的默认值，允许在给定时间共分发 600 个请求。但是，由于它将控制平面扩展到更大的尺寸以应对利用率的增加和工作负载的流失，它相应地将机上请求配额一直增加到2000年（可能会发生变化）。APF 规定了如何将这些机上请求配额进一步细分为不同的请求类型。请注意，EKS 控制平面高度可用，每个集群至少注册了 2 个 API 服务器。这意味着您的集群可以处理的机上请求总数是每个 kube-apiserver 设置的机上配额的两倍（如果进一步横向扩展，则更高）。在最大的EKS集群 requests/second 上，这相当于数千个。

两种名为 PriorityLevelConfigurations 和 FlowSchemas的 Kubernetes 对象配置如何在不同的请求类型之间划分请求总数。这些对象由 API 服务器自动维护，对于给定的 Kubernetes 次要版本，EKS 使用这些对象的默认配置。 PriorityLevelConfigurations 代表允许的请求总数的一小部分。例如，在总共 600 个请求中，工作负载最高的分配量 PriorityLevelConfiguration 为 98 个。分配给所有请求的总和 PriorityLevelConfigurations 将等于 600（或略高于 600，因为如果给定级别获得请求的一小部分授权，API 服务器将四舍五入）。要检查您的集群 PriorityLevelConfigurations 中的以及分配给每个集群的请求数，您可以运行以下命令。以下是 EKS 1.32 的默认值：

```
$ kubectl get --raw /metrics | grep apiserver_flowcontrol_nominal_limit_seats
apiserver_flowcontrol_nominal_limit_seats{priority_level="catch-all"} 13
apiserver_flowcontrol_nominal_limit_seats{priority_level="exempt"} 0
apiserver_flowcontrol_nominal_limit_seats{priority_level="global-default"} 49
apiserver_flowcontrol_nominal_limit_seats{priority_level="leader-election"} 25
apiserver_flowcontrol_nominal_limit_seats{priority_level="node-high"} 98
apiserver_flowcontrol_nominal_limit_seats{priority_level="system"} 74
apiserver_flowcontrol_nominal_limit_seats{priority_level="workload-high"} 98
apiserver_flowcontrol_nominal_limit_seats{priority_level="workload-low"} 245
```

第二种类型的对象是 FlowSchemas。具有一组给定属性的 API 服务器请求被归类为同一组属性 FlowSchema。这些属性包括经过身份验证的用户或请求的属性，例如 API 组、命名空间或资源。A FlowSchema 还指定了 PriorityLevelConfiguration 此类请求应映射到哪个。这两个对象一起说：“我希望这种类型的请求计入这部分机上请求。” 当请求到达 API 服务器时，它将检查每个请求， FlowSchemas 直到找到一个与所有必需的属性相匹配的请求。如果多个请求 FlowSchemas 匹配，则 API 服务器将选择匹配优先级最小的，该优先级指定为对象中的一个属性。 FlowSchema 

 PriorityLevelConfigurations 可以使用以下命令查看 FlowSchemas 到的映射：

```
$ kubectl get flowschemas
NAME                           PRIORITYLEVEL     MATCHINGPRECEDENCE   DISTINGUISHERMETHOD   AGE     MISSINGPL
exempt                         exempt            1                    <none>                7h19m   False
eks-exempt                     exempt            2                    <none>                7h19m   False
probes                         exempt            2                    <none>                7h19m   False
system-leader-election         leader-election   100                  ByUser                7h19m   False
endpoint-controller            workload-high     150                  ByUser                7h19m   False
workload-leader-election       leader-election   200                  ByUser                7h19m   False
system-node-high               node-high         400                  ByUser                7h19m   False
system-nodes                   system            500                  ByUser                7h19m   False
kube-controller-manager        workload-high     800                  ByNamespace           7h19m   False
kube-scheduler                 workload-high     800                  ByNamespace           7h19m   False
kube-system-service-accounts   workload-high     900                  ByNamespace           7h19m   False
eks-workload-high              workload-high     1000                 ByUser                7h14m   False
service-accounts               workload-low      9000                 ByUser                7h19m   False
global-default                 global-default    9900                 ByUser                7h19m   False
catch-all                      catch-all         10000                ByUser                7h19m   False
```

PriorityLevelConfigurations 可以有 “排队”、“拒绝” 或 “豁免” 的类型。对于 Queue 和 Reject 类型，将对该优先级的最大机上请求数施加限制，但是，当达到该限制时，行为会有所不同。例如，工作负载高 PriorityLevelConfiguration 使用队列类型，有 98 个请求可供控制器管理器、端点控制器、调度器、eks 相关控制器以及运行在 kube-system 命名空间中的 pod 使用。由于使用了 Queue 类型，API 服务器将尝试将请求保留在内存中，并希望在这些请求超时之前，机上请求的数量降至 98 以下。如果给定请求在队列中超时，或者已经排队的请求太多，则 API 服务器别无选择，只能丢弃该请求并向客户端返回 429。请注意，排队可能会阻止请求接收 429，但需要权衡请求的端到端延迟增加。

现在考虑一下映射到类型为 Rejec FlowSchema t 的包罗万象的包罗万象 PriorityLevelConfiguration 。如果客户达到 13 个机上请求的限制，API 服务器将不会进行排队，并将使用 429 响应代码立即丢弃请求。最后，映射到类型为 Ex PriorityLevelConfiguration empt 的请求永远不会收到 429，并且始终会立即分发。这用于高优先级请求，例如 healthz 请求或来自 system: masters 组的请求。

### 监控 APF 和已删除的请求
<a name="_monitoring_apf_and_dropped_requests"></a>

为了确认是否有任何请求因 APF 而被删除，`apiserver_flowcontrol_rejected_requests_total`可以监控的 API 服务器指标，以检查受影响的 FlowSchemas 和 PriorityLevelConfigurations. 例如，此指标显示，由于工作负载不足队列中的请求超时，来自服务账户的 100 个请求 FlowSchema 被丢弃：

```
% kubectl get --raw /metrics | grep apiserver_flowcontrol_rejected_requests_total
apiserver_flowcontrol_rejected_requests_total{flow_schema="service-accounts",priority_level="workload-low",reason="time-out"} 100
```

要检查给定 PriorityLevelConfiguration 距离接收 429 的距离有多接近，或者由于排队而出现延迟增加的情况，您可以比较并发限制和正在使用的并发量之间的差异。在此示例中，我们有 100 个请求的缓冲区。

```
% kubectl get --raw /metrics | grep 'apiserver_flowcontrol_nominal_limit_seats.*workload-low'
apiserver_flowcontrol_nominal_limit_seats{priority_level="workload-low"} 245

% kubectl get --raw /metrics | grep 'apiserver_flowcontrol_current_executing_seats.*workload-low'
apiserver_flowcontrol_current_executing_seats{flow_schema="service-accounts",priority_level="workload-low"} 145
```

要检查给定对象 PriorityLevelConfiguration 是否遇到排队但不一定是请求丢弃的情况，`apiserver_flowcontrol_current_inqueue_requests`可以参考的指标：

```
% kubectl get --raw /metrics | grep 'apiserver_flowcontrol_current_inqueue_requests.*workload-low'
apiserver_flowcontrol_current_inqueue_requests{flow_schema="service-accounts",priority_level="workload-low"} 10
```

其他有用的 Prometheus 指标包括：
+ apiserver\_flowcontrol\_dispatched\_requests\_t
+ apiserver\_flowcontrol\_请求\_执行\_秒
+ apiserver\_flowControl\_request\_wait\_duration\_seconds

有关 [ APF 指标](https://kubernetes.io/docs/concepts/cluster-administration/flow-control/#observability)的完整列表，请参阅上游文档。

### 防止请求被丢弃
<a name="_preventing_dropped_requests"></a>

#### 通过更改工作负载来防止 429
<a name="_prevent_429s_by_changing_your_workload"></a>

当 APF 因给定 PriorityLevelConfiguration 超出其允许的最大机上请求数而丢弃请求时，受影响的客户端 FlowSchemas 可以减少在给定时间执行的请求数量。这可以通过减少在429次请求发生期间提出的请求总数来实现。请注意，长时间运行的请求（例如昂贵的列表调用）尤其成问题，因为它们在执行的整个过程中都算作正在进行的请求。减少这些昂贵请求的数量或优化这些列表调用的延迟（例如，通过减少每个请求提取的对象数量或切换到使用监视请求）可以帮助减少给定工作负载所需的总并发量。

#### 通过更改 APF 设置来防止 429
<a name="_prevent_429s_by_changing_your_apf_settings"></a>

**警告**  
仅当你知道自己在做什么时才更改默认 APF 设置。错误配置的 APF 设置会导致 API 服务器请求丢失和严重的工作负载中断。

防止请求丢弃的另一种方法是更改默认设置 FlowSchemas 或 PriorityLevelConfigurations 安装在 EKS 集群上。EKS 为 FlowSchemas 给定的 Kubernetes 次要版本安装上游默认设置。 PriorityLevelConfigurations 除非对象上的以下注解设置为 false，否则修改后，API 服务器会自动将这些对象协调回其默认值：

```
  metadata:
    annotations:
      apf.kubernetes.io/autoupdate-spec: "false"
```

总体而言，APF 设置可以修改为：
+ 为你关心的请求分配更多机上容量。
+ 隔离可能耗尽其他请求类型容量的不必要或昂贵的请求。

这可以通过更改默认值 FlowSchemas PriorityLevelConfigurations 或创建这些类型的新对象来实现。操作员可以增加相关 PriorityLevelConfigurations 对象ConcurrencyShares 的保证值，从而增加分配给他们的机上请求的比例。此外，如果应用程序能够处理因请求在分发之前排队而导致的额外延迟，则在给定时间可以排队的请求数量也可以增加。

或者，可以创建特定于客户工作负载的新 FlowSchema 和 PriorityLevelConfigurations 对象。请注意，ConcurrencyShares 向现有 PriorityLevelConfigurations 或新存储分区分配更多保障 PriorityLevelConfigurations 将导致可由其他存储分区处理的请求数量减少，因为每台 API 服务器的总上限将保持在 600 个。

更改 APF 默认值时，应在非生产集群上监控这些指标，以确保更改设置不会导致意外的 429

1. `apiserver_flowcontrol_rejected_requests_total`应监控所有人的指标， FlowSchemas 以确保没有存储桶开始丢弃请求。

1. `apiserver_flowcontrol_current_executing_seats`应比较`apiserver_flowcontrol_nominal_limit_seats`和的值，以确保使用的并发性不会因突破该优先级的限制而面临风险。

定义新的 FlowSchema 和的一个常见用例 PriorityLevelConfiguration 是隔离。假设我们要将长时间运行的列表事件调用从 pod 隔离到它们自己的请求份额。这将防止来自使用现有服务账户的 pod 的重要请求收 FlowSchema 到 429，并防止请求容量不足。回想一下，机上请求的总数是有限的，但是，此示例显示可以修改 APF 设置，以便更好地分配给定工作负载的请求容量：

隔离列出事件请求的示例 FlowSchema 对象：

```
apiVersion: flowcontrol.apiserver.k8s.io/v1
kind: FlowSchema
metadata:
  name: list-events-default-service-accounts
spec:
  distinguisherMethod:
    type: ByUser
  matchingPrecedence: 8000
  priorityLevelConfiguration:
    name: catch-all
  rules:
  - resourceRules:
    - apiGroups:
      - '*'
      namespaces:
      - default
      resources:
      - events
      verbs:
      - list
    subjects:
    - kind: ServiceAccount
      serviceAccount:
        name: default
        namespace: default
```
+ 这会 FlowSchema 捕获服务帐号在默认命名空间中进行的所有列表事件调用。
+ 匹配优先级 8000 低于现有服务帐户使用的值 9000， FlowSchema 因此这些列表事件调用将匹配列表事件默认服务帐户，而不是服务帐户。
+ 我们正在使用包罗万象 PriorityLevelConfiguration 来隔离这些请求。此存储桶仅允许这些长时间运行的列表事件调用使用 13 个机上请求。一旦 Pod 尝试同时发出超过 13 个这样的请求，它们就会开始收到 429 个。

## 在 API 服务器中检索资源
<a name="_retrieving_resources_in_the_api_server"></a>

从 API 服务器获取信息是任何规模的集群的预期行为。当您扩展集群中的资源数量时，请求频率和数据量会迅速成为控制层面的瓶颈，并将导致 API 延迟和缓慢。根据延迟的严重程度，如果您不小心，它会导致意外停机。

了解您的要求以及避免此类问题的第一步是多久一次。以下是根据扩展最佳实践限制查询量的指南。本节中提供的建议是从已知扩展性最佳的选项开始。

### 使用共享告密者
<a name="_use_shared_informers"></a>

在构建与 Kubernetes API 集成的控制器和自动化时，您通常需要从 Kubernetes 资源中获取信息。如果您定期轮询这些资源，可能会导致 API 服务器上的大量负载。

使用 client-go 库中的[告密器](https://pkg.go.dev/k8s.io/client-go/informers)将使您能够根据事件监视资源的变化，而不是轮询更改。告密者通过对事件和更改使用共享缓存来进一步减少负载，这样监视相同资源的多个控制器就不会增加额外的负载。

控制器应避免在没有标签和字段选择器的情况下轮询集群范围内的资源，尤其是在大型集群中。每个未经过筛选的民意调查都需要通过 API 服务器从 etcd 发送大量不必要的数据，然后由客户端进行过滤。通过根据标签和命名空间进行筛选，您可以减少 API 服务器为完成发送给客户端的请求和数据而需要执行的工作量。

### 优化 Kubernetes API 的使用
<a name="_optimize_kubernetes_api_usage"></a>

使用自定义控制器或自动化调用 Kubernetes API 时，请务必将调用限制在所需的资源上。如果没有限制，你可能会在 API 服务器和 etcd 上造成不必要的负载。

建议您尽可能使用 watch 参数。如果没有参数，默认行为是列出对象。要使用监视而不是列表，您可以`?watch=true`将其附加到 API 请求的末尾。例如，要使用手表获取默认命名空间中的所有 pod，请使用：

```
/api/v1/namespaces/default/pods?watch=true
```

如果您要列出对象，则应限制所列内容的范围和返回的数据量。您可以通过向请求添加`limit=500`参数来限制返回的数据。`fieldSelector`参数和`/namespace/`路径可用于确保您的列表根据需要缩小范围。例如，要仅列出默认命名空间中正在运行的 pod，请使用以下 API 路径和参数。

```
/api/v1/namespaces/default/pods?fieldSelector=status.phase=Running&limit=500
```

或者列出所有使用以下命令运行的 pod：

```
/api/v1/pods?fieldSelector=status.phase=Running&limit=500
```

限制监视调用或列出对象的另一种选择是使用它 [`resourceVersions`，您可以在 Kubernetes 文档中阅读相关内容。](https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions)如果没有`resourceVersion`参数，您将收到最新的可用版本，该版本需要 etcd 法定读取，这是数据库中最昂贵和最慢的读取操作。资源版本取决于您要查询的资源，可以在字段中`metadata.resourseVersion`找到。如果使用监视通话，而不仅仅是列出通话，也建议使用此方法

有一种特殊的`resourceVersion=0`方法可以从 API 服务器缓存中返回结果。这可以减少 etcd 的负载，但它不支持分页。

```
/api/v1/namespaces/default/pods?resourceVersion=0
```

建议使用 watch，将 resourceVersion 设置为从其先前的列表或监视表中收到的最新已知值。这是在 client-go 中自动处理的。但是，如果您使用其他语言的k8s客户端，建议您仔细检查一下。

```
/api/v1/namespaces/default/pods?watch=true&resourceVersion=362812295
```

如果您在不带任何参数的情况下调用 API，它将是 API 服务器和 etcd 最耗费资源的时间。此调用将获取所有命名空间中的所有 pod，无需分页或限制范围，并且需要从 etcd 读取法定人数。

```
/api/v1/pods
```

### 防止 DaemonSet 雷鸣般的牛群
<a name="_prevent_daemonset_thundering_herds"></a>

A DaemonSet 确保所有（或部分）节点运行 Pod 的副本。当节点加入集群时，守护程序集控制器会为这些节点创建 pod。当节点离开集群时，这些 pod 会被垃圾回收。删除 DaemonSet 将清理它创建的 pod。

a 的一些典型用法 DaemonSet 是：
+ 在每个节点上运行集群存储守护程序
+ 在每个节点上运行日志收集守护程序
+ 在每个节点上运行节点监控守护程序

在具有数千个节点的集群上 DaemonSet，创建新节点 DaemonSet、更新节点或增加节点数量可能会导致控制平面上的高负载。如果 DaemonSet pod 在 pod 启动时发出昂贵的 API 服务器请求，它们可能会导致大量并发请求在控制层面上占用大量资源。

在正常操作中，您可以使用`RollingUpdate`来确保逐步推出新的 DaemonSet pod。使用`RollingUpdate`更新策略，在你更新 DaemonSet 模板后，控制器会杀死旧的 DaemonSet pod 并以受控的方式自动创建新 DaemonSet 的 pod。在整个更新过程中，最多有一个 pod DaemonSet 将在每个节点上运行。您可以通过设置为 1、`maxSurge` 0 和 60 `maxUnavailable` `minReadySeconds` 来执行逐步部署。如果你没有指定更新策略，Kubernetes 将默认为创建一个，`RollingUpdate`分别`maxUnavailable`为 1、`maxSurge` 0 和 `minReadySeconds` 0。

```
minReadySeconds: 60
strategy:
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 0
    maxUnavailable: 1
```

如果已经创建了新的 pod，并且所有节点上 DaemonSet 的 DaemonSet pod 数量都达到预期的数量，则`RollingUpdate`可以确保逐步推出新`Ready`的 pod。在策略未涵盖的某些条件下，可能会出现雷鸣般的牛群问题。`RollingUpdate`

#### 在创造时防止雷鸣般的牛群 DaemonSet
<a name="_prevent_thundering_herds_on_daemonset_creation"></a>

默认情况下，无论`RollingUpdate`配置如何，当你创建新节点时，kube-controller-manager 中的守护程序集控制器都会同时为所有匹配的节点创建 pod。 DaemonSet要在创建后强制逐步推出 pod DaemonSet，可以使用`NodeSelector`或`NodeAffinity`。 DaemonSet 这将创建一个匹配零个节点的，然后你可以逐步更新节点，使它们有资格以受控的速率从中 DaemonSet 运行 pod。你可以遵循这种方法：
+ 为的所有节点添加标签`run-daemonset=false`。

```
kubectl label nodes --all run-daemonset=false
```
+  DaemonSet 使用`NodeAffinity`设置创建您的，以匹配任何没有`run-daemonset=false`标签的节点。最初，这将导致你 DaemonSet 没有相应的 pod。

```
affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: run-daemonset
          operator: NotIn
          values:
          - "false"
```
+ 以受控速率从节点上移除`run-daemonset=false`标签。你可以使用这个 bash 脚本作为示例：

```
#!/bin/bash

nodes=$(kubectl get --raw "/api/v1/nodes" | jq -r '.items | .[].metadata.name')

for node in ${nodes[@]}; do
   echo "Removing run-daemonset label from node $node"
   kubectl label nodes $node run-daemonset-
   sleep 5
done
```
+ （可选）从您的 DaemonSet 对象中移除该`NodeAffinity`设置。请注意，这还将触发`RollingUpdate`并逐渐替换所有现有 DaemonSet 的 pod，因为 DaemonSet 模板已更改。

#### 在节点横向扩展时防止雷鸣般的牛群
<a name="_prevent_thundering_herds_on_node_scale_outs"></a>

与 DaemonSet 创建类似，快速创建新节点会导致大量 DaemonSet Pod 同时启动。你应该以受控的速率创建新节点，这样控制器就会以同样的速率创建 DaemonSet Pod。如果这不可能，您可以使用使新节点最初没有资格使用`NodeAffinity`现有 DaemonSet 节点。接下来，你可以逐步为新节点添加标签，这样 daemonset-controller 以受控的速率创建 Pod。你可以遵循这种方法：
+ 为所有现有节点添加标签 `run-daemonset=true` 

```
kubectl label nodes --all run-daemonset=true
```
+  DaemonSet 使用设置更新您的`NodeAffinity`设置，以匹配任何带有`run-daemonset=true`标签的节点。请注意，这还将触发`RollingUpdate`并逐渐替换所有现有 DaemonSet 的 pod，因为 DaemonSet 模板已更改。您应该等待完成`RollingUpdate`，然后再进行下一步。

```
affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: run-daemonset
          operator: In
          values:
          - "true"
```
+ 在集群中创建新节点。请注意，这些节点没有`run-daemonset=true`标签，因此它们与这些节点不匹配。 DaemonSet 
+ 以受控速率将`run-daemonset=true`标签添加到您的新节点（当前没有`run-daemonset`标签）。你可以使用这个 bash 脚本作为示例：

```
#!/bin/bash

nodes=$(kubectl get --raw "/api/v1/nodes?labelSelector=%21run-daemonset" | jq -r '.items | .[].metadata.name')

for node in ${nodes[@]}; do
   echo "Adding run-daemonset=true label to node $node"
   kubectl label nodes $node run-daemonset=true
   sleep 5
done
```
+ 或者，从 DaemonSet 对象中移除`NodeAffinity`设置并从所有节点移除`run-daemonset`标签。

#### 更新时防止雷鸣般的牛群 DaemonSet
<a name="_prevent_thundering_herds_on_daemonset_updates"></a>

`RollingUpdate`策略只会尊重符合条件的 DaemonSet pod 的`maxUnavailable`设置`Ready`。如果 a 只 DaemonSet 有 `NotReady` pod 或很大比例的 `NotReady` pod，而你更新了它的模板，则守护程序控制器将同时为任何 pod 创建新的 pod。`NotReady`如果有大量的 `NotReady` pod，这可能会导致雷鸣般的群集问题，例如，如果 pod 持续崩溃循环或无法提取镜像。

要在更新 pod DaemonSet 且有 pod 时强制逐步推出 `NotReady` pod，你可以暂时将更新策略 DaemonSet 从 from 更改`RollingUpdate`为`OnDelete`。在更新 DaemonSet 模板后`OnDelete`，控制器会在你手动删除旧的 pod 后创建新的 pod，这样你就可以控制新 pod 的推出。你可以遵循这种方法：
+ 检查您的舱内是否有`NotReady`吊舱 DaemonSet.
+ 如果否，您可以安全地更新 DaemonSet 模板，该`RollingUpdate`策略将确保逐步推出。
+ 如果是，则应首先更新您的策略 DaemonSet 以使用该`OnDelete`策略。

```
updateStrategy:
  type: OnDelete
```
+ 接下来，使用所需的更改更新您的 DaemonSet 模板。
+ 更新后，你可以通过按受控速率发出删除 DaemonSet 容器请求来删除旧的 pod。你可以使用这个 bash 脚本作为示例，其中 kube-system 命名空间中的 DaemonSet 名字是 fluentd-elasticsearch：

```
#!/bin/bash

daemonset_pods=$(kubectl get --raw "/api/v1/namespaces/kube-system/pods?labelSelector=name%3Dfluentd-elasticsearch" | jq -r '.items | .[].metadata.name')

for pod in ${daemonset_pods[@]}; do
   echo "Deleting pod $pod"
   kubectl delete pod $pod -n kube-system
   sleep 5
done
```
+ 最后，你可以更新 DaemonSet 到之前的`RollingUpdate`策略。