

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

# 通过断开网络连接 Kubernetes Pod 进行故障转移
<a name="hybrid-nodes-kubernetes-pod-failover"></a>

首先，我们将回顾在节点和 Kubernetes 控制平面之间断开网络连接期间影响 Kubernetes 行为的关键概念、组件和设置。EKS 符合上游 Kubernetes 标准，因此此处描述的所有 Kubernetes 概念、组件和设置都适用于 EKS 和 EKS 混合节点部署。

对 EKS 进行了一些改进，专门用于改善网络断开期间的 pod 故障转移行为，有关更多信息，请参阅上游 Kubernetes 存储库[中的 GitHub 问题 [ \#131294 ](https://github.com/kubernetes/kubernetes/pull/131294) 和 ](https://github.com/kubernetes/kubernetes/issues/131481) \#131481。

## 概念
<a name="_concepts"></a>

 污点和容忍：在 Kubernetes 中，污点和容忍度用于控制 Pod 在节点上的调度。污点由节点生命周期控制器设置，以表明节点不符合调度条件或应驱逐这些节点上的 Pod。当由于网络断开而无法访问节点时，节点生命周期控制器会应用 node.kubernetes。io/unreachable 使用 NoSchedule 效果进行污点，如果满足某些条件则 NoExecute 产生效果。node.kubernetes。io/unreachable 污点对应于 “ NodeCondition 准备就绪未知”。用户可以在中指定应用程序级别对污点的容忍度。 PodSpec
+ NoSchedule：除非新的 Pod 具有匹配的容忍度，否则不会在受污染的节点上调度。已经在节点上运行的 Pod 不会被驱逐。
+ NoExecute: 无法容忍该污点的 Pod 会立即被驱逐。容忍该污点（未指定容忍秒数）的 Pod 将永远处于绑定状态。在指定容忍秒数下容忍污点的 Pod 将在指定时间内保持绑定状态。在这段时间过后，节点生命周期控制器会将 Pod 逐出节点。

 节点租赁：Kubernetes 使用 Lease API 向 Kubernetes API 服务器传输 kubelet 节点心跳信号。对于每个节点，都有一个名称匹配的 Lease 对象。在内部，每个 kubelet 心跳都会更新 Lease 对象的 spec.RenewTime 字段。Kubernetes 控制平面使用该字段的时间戳来确定节点可用性。如果节点与 Kubernetes 控制平面断开连接，则它们无法更新租用的 sep.RenewTime，控制平面会将其解释为 “就绪未知”。 NodeCondition 

## 组件
<a name="_components"></a>

![Pod 故障转移行为中涉及的 Kubernetes 组件](http://docs.aws.amazon.com/zh_cn/eks/latest/best-practices/images/hybrid/k8s-components-pod-failover.png)



| 组件 | Sub-component | 说明 | 
| --- | --- | --- | 
| Kubernetes 控制飞机 | kube-api 服务器 | API 服务器是 Kubernetes 控制平面的核心组件，它暴露了 Kubernetes API。 | 
| Kubernetes 控制飞机 | 节点生命周期控制器 | kube-controller-manager 运行的控制器之一。它负责检测和响应节点问题。 | 
| Kubernetes 控制飞机 | kube-scheduler | 一个控制平面组件，用于监视未分配节点的新创建的 Pod，并选择一个节点供其运行。 | 
| Kubernetes 节点 | kubelet | 在群集中的每个节点上运行的代理。kubelet 会监视 PodSpecs 并确保这些容器中描述的容 PodSpecs 器运行状况良好。 | 

## 配置设置
<a name="_configuration_settings"></a>


| 组件 | 设置 | 说明 | K8s 默认 | EKS 默认 | 可在 EKS 中配置 | 
| --- | --- | --- | --- | --- | --- | 
| kube-api-server | 默认无法达到的容忍秒数 | 表示默认情况下，将该容忍`unreachable:NoExecute`度的容忍度添加到每个尚未具有此类容忍度的 Pod 中。`tolerationSeconds` | 300 | 300 | 否 | 
| 节点生命周期控制器 | 节点监视器宽限期 | 节点在被标记为不健康之前可能没有响应的时间长度。必须是 kubelet 的 N 倍`nodeStatusUpdateFrequency`，其中 N 是 kubelet 发布节点状态时允许的重试次数。 | 40 | 40 | 否 | 
| 节点生命周期控制器 | 大型集群大小阈值 | 节点生命周期控制器根据驱逐逻辑将集群视为大型集群的节点数量。`--secondary-node-eviction-rate`对于这个大小或更小的集群，会被覆盖为 0。 | 50 | 100000 | 否 | 
| 节点生命周期控制器 | 不健康区域阈值 | 区域中必须处于未就绪状态才能将该区域视为运行状况不佳的节点的百分比。 | 55% | 55% | 否 | 
| kubelet | 节点状态更新频率 | kubelet 将节点状态发布到控制平面的频率。必须与节点生命周期控制器`nodeMonitorGracePeriod`中兼容。 | 10 | 10 | 是 | 
| kubelet | 节点标签 | 在集群中注册节点时要添加的标签。`topology.kubernetes.io/zone`可以使用混合节点指定标签，将节点分组为区域。 | 无 | 无 | 是 | 

## 通过断开网络连接 Kubernetes Pod 进行故障转移
<a name="_kubernetes_pod_failover_through_network_disconnections"></a>

此处描述的行为假设 pod 以默认设置作为 Kubernetes 部署运行，并且 EKS 被用作 Kubernetes 提供商。实际行为可能会因您的环境、网络断开的类型、应用程序、依赖关系和集群配置而有所不同。本指南中的内容已使用特定的应用程序、集群配置和插件子集进行了验证。强烈建议在迁移到生产环境之前，在自己的环境和自己的应用程序中测试行为。

当节点和 Kubernetes 控制平面之间出现网络断开连接时，每个断开连接的节点上的 kubelet 无法与 Kubernetes 控制平面通信。因此，在恢复连接之前，kubelet 无法驱逐这些节点上的 pod。这意味着在网络断开之前在这些节点上运行的 Pod 将在断开连接期间继续运行，前提是没有其他故障导致它们关闭。总而言之，在节点与 Kubernetes 控制平面之间的网络断开期间，您可以实现静态稳定性，但是在恢复连接之前，您无法对节点或工作负载执行变更操作。

根据网络断开的性质，有五种主要场景会产生不同的 pod 故障转移行为。在所有场景中，一旦节点重新连接到 Kubernetes 控制平面，集群无需操作员干预即可恢复正常。以下场景根据我们的观察概述了预期结果，但这些结果可能不适用于所有可能的应用程序和集群配置。

### 场景 1：完整集群中断
<a name="_scenario_1_full_cluster_disruption"></a>

 **预期结果**：无法访问的节点上的 Pod 不会被驱逐并继续在这些节点上运行。

集群完全中断意味着集群中的所有节点都与 Kubernetes 控制平面断开连接。在这种情况下，控制平面上的节点生命周期控制器检测到集群中的所有节点都无法访问，并取消任何 Pod 驱逐。

在断开连接`Not Ready`期间，集群管理员将看到所有节点的状态。Pod 状态不会改变，在断开连接和后续重新连接期间，不会在任何节点上调度新的 Pod。

### 场景 2：整个区域中断
<a name="_scenario_2_full_zone_disruption"></a>

 **预期结果**：无法访问的节点上的 Pod 不会被驱逐并继续在这些节点上运行。

整个区域中断意味着该区域中的所有节点都与 Kubernetes 控制平面断开连接。在这种情况下，控制平面上的节点生命周期控制器检测到区域中的所有节点都无法访问，并取消任何 Pod 驱逐。

在断开连接`Not Ready`期间，集群管理员将看到所有节点的状态。Pod 状态不会改变，在断开连接和后续重新连接期间，不会在任何节点上调度新的 Pod。

### 场景 3：多数区域中断
<a name="_scenario_3_majority_zone_disruption"></a>

 **预期结果**：无法访问的节点上的 Pod 不会被驱逐并继续在这些节点上运行。

多数区域中断意味着给定区域中的大多数节点都与 Kubernetes 控制平面断开了连接。Kubernetes 中的区域由具有相同标签的节点定义。`topology.kubernetes.io/zone`如果集群中未定义任何区域，则大多数中断意味着整个集群中的大多数节点都已断开连接。默认情况下，大多数由节点生命周期控制器定义，在 Kubernetes 和 EK `unhealthy-zone-threshold` S 中均设置为 55%。由于在 EKS 中设置`large-cluster-size-threshold`为 100,000，因此如果一个区域中有 55% 或更多的节点无法访问，则 Pod 驱逐将被取消（假设大多数集群远小于 100,000 个节点）。

在断开连接`Not Ready`期间，集群管理员会看到区域中的大多数节点处于状态，但是 pod 的状态不会改变，也不会在其他节点上重新调度。

请注意，上述行为仅适用于大于三个节点的集群。在三个或更少节点的集群中，计划驱逐无法访问的节点上的 Pod，而新的 pod 则调度在运行正常的节点上。

在测试期间，我们偶尔会观察到，在网络断开期间，Pod 被逐出正好一个无法访问的节点，即使该区域的大多数节点都无法访问。我们仍在研究 Kubernetes 节点生命周期控制器中可能存在的竞争条件，这是造成这种行为的原因。

### 场景 4：少数族裔区域中断
<a name="_scenario_4_minority_zone_disruption"></a>

 **预期结果**：Pod 被逐出无法访问的节点，新的 Pod 被调度到符合条件的可用节点上。

少量中断意味着区域中与 Kubernetes 控制平面断开连接的节点比例较小。如果集群中未定义任何区域，则少数中断意味着整个集群中的少数节点断开连接。如前所述，少数派由节点生命周期控制器的`unhealthy-zone-threshold`设置定义，默认值为 55%。在这种情况下，如果网络断开的持续时间超过`default-unreachable-toleration-seconds`（5 分钟）和`node-monitor-grace-period`（40 秒），并且一个区域中只有不到 55% 的节点无法访问，则新的 Pod 会调度在运行正常的节点上，而无法访问的节点上的 pod 会被标记为驱逐。

集群管理员将看到在运行状况良好的节点上创建的新 pod，断开连接的节点上的 pod 将显示为`Terminating`。请记住，尽管已断开连接的节点上的 Pod 具有`Terminating`状态，但在节点重新连接到 Kubernetes 控制平面之前，它们不会被完全驱逐。

## 场景 5：网络中断期间重启节点
<a name="_scenario_5_node_restart_during_network_disruption"></a>

 **预期结果**：在节点重新连接到 Kubernetes 控制平面之前，无法访问的节点上的 Pod 才会启动。Pod 故障转移遵循场景 1—3 中描述的逻辑，具体取决于无法访问的节点的数量。

网络中断期间的节点重启意味着节点在断开网络的同时发生了另一个故障（例如电源重启、内存不足事件或其他问题）。如果 kubelet 也已重启，则网络断开连接开始时在该节点上运行的 pod 不会在断开连接期间自动重启。kubelet 在启动时会查询 Kubernetes API 服务器，以了解它应该运行哪些 pod。如果 kubelet 由于网络断开而无法访问 API 服务器，则它无法检索启动 pod 所需的信息。

在这种情况下，不能使用 `crictl` CLI 等本地故障排除工具手动启动 pod，以此作为 “破碎玻璃” 措施。Kubernetes 通常会移除失效的 pod 并创建新的 pod，而不是重启现有 pod（有关详细信息，请参阅容器 GitHub 存储库[中的 ](https://github.com/containerd/containerd/pull/10213) \#10213）。静态容器是唯一由 kubelet 控制的 Kubernetes 工作负载对象，可以在这些场景中重启。但是，通常不建议使用静态 pod 进行应用程序部署。取而代之的是，在不同的主机上部署多个副本，以确保在同时出现多个故障（例如节点故障以及节点与 Kubernetes 控制平面之间的网络断开）时应用程序的可用性。