

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

# 运行高可用性应用程序
<a name="application"></a>

您的客户希望您的应用程序始终可用，包括在您进行更改时，尤其是在流量激增期间。可扩展且具有弹性的架构可让您的应用程序和服务不受干扰地运行，从而让您的用户满意。可扩展的基础架构会根据业务需求进行增长和缩小。消除单点故障是提高应用程序可用性并使其具有弹性的关键一步。

借助 Kubernetes，您可以操作您的应用程序，并以高度可用且具有弹性的方式运行它们。它的声明式管理可确保在设置应用程序后，Kubernetes 会不断尝试将[当前状态与所需状态相匹配](https://kubernetes.io/docs/concepts/architecture/controller/#desired-vs-current)。

## 建议
<a name="_recommendations"></a>

### 配置 Pod 中断预算
<a name="_configure_pod_disruption_budgets"></a>

 [Pod 中断预算](https://kubernetes.io/docs/tasks/run-application/configure-pdb/)用于限制应用程序将经历的并发中断量。如果必须始终有一部分工作负载可用，则应针对工作负载对其进行配置。EKS 自动模式、Karpenter 和 Cluster Autoscaler 在缩小规模时会知道并遵守配置的 Pod 中断预算。EKS 自动模式、Karpenter 和托管节点组在更新节点时也要遵守 Pod 中断预算

### 避免运行单例 Pod
<a name="_avoid_running_singleton_pods"></a>

如果你的整个应用程序在单个 Pod 中运行，那么如果该 Pod 被终止，你的应用程序将不可用。与其使用单个 Pod 部署应用程序，不如创建[部署](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/)。如果 Deployment 创建的 Pod 失败或被终止，部署[控制器](https://kubernetes.io/docs/concepts/architecture/controller/)将启动一个新的 Pod，以确保指定数量的副本 Pod 始终处于运行状态。

### 运行多个副本
<a name="_run_multiple_replicas"></a>

使用 Deployment 运行应用程序的多个副本 Pod 有助于其以高度可用的方式运行。如果一个副本失败，剩余的副本仍然可以运行，尽管容量会降低，直到 Kubernetes 创建另一个 Pod 来弥补损失。此外，您可以使用[水平容器自动扩缩器](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/)根据工作负载需求自动扩展副本。

### 跨节点调度副本
<a name="_schedule_replicas_across_nodes"></a>

如果所有副本都在同一个节点上运行，并且该节点变得不可用，那么运行多个副本就没有多大用处。考虑使用 pod 反亲和性或 pod 拓扑分布约束将部署的副本分布到多个工作节点上。

您可以通过在多个应用程序上运行典型应用程序来进一步提高其可靠性 AZs。

#### 使用 Pod 反关联性规则
<a name="_using_pod_anti_affinity_rules"></a>

下面的清单告诉 Kubernetes 调度器*更喜欢将 pod 放在单独*的节点上，然后. AZs 它不需要不同的节点或可用区，因为如果需要不同的节点或可用区，那么一旦每个可用区中都有一个容器在运行，Kubernetes 将无法调度任何 Pod。如果您的应用程序只需要三个副本，则可以使用 `requiredDuringSchedulingIgnoredDuringExecution` for，而 Kubernetes 调度器不会在同一个可用区中调度两个 Pod。`topologyKey: topology.kubernetes.io/zone`

```
apiVersion: apps/v1
kind: Deployment
metadata:
  name: spread-host-az
  labels:
    app: web-server
spec:
  replicas: 4
  selector:
    matchLabels:
      app: web-server
  template:
    metadata:
      labels:
        app: web-server
    spec:
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - web-server
              topologyKey: topology.kubernetes.io/zone
            weight: 100
          - podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - web-server
              topologyKey: kubernetes.io/hostname
            weight: 99
      containers:
      - name: web-app
        image: nginx:1.16-alpine
```

#### 使用 Pod 拓扑分布约束
<a name="_using_pod_topology_spread_constraints"></a>

与 Pod 反关联性规则类似，Pod 拓扑分布约束允许您在不同的故障（或拓扑）域（例如主机或）中提供应用程序。 AZs当您尝试通过在每个不同的拓扑域中拥有多个副本来确保容错能力和可用性时，这种方法非常有效。另一方面，Pod 反关联性规则可以很容易地生成拓扑域中只有一个副本的结果，因为彼此具有反亲和力的 Pod 会产生排斥效果。在这种情况下，专用节点上的单个副本不适合容错，也不能很好地利用资源。使用拓扑分布约束，您可以更好地控制调度器应尝试在拓扑域中应用的分布或分布。以下是这种方法中要使用的一些重要属性：

1. `maxSkew`用于控制或确定拓扑域中事物不均匀的最大点。例如，如果一个应用程序有 10 个副本并且部署在 3 个副本中 AZs，则无法实现均匀分布，但您可以影响分布的不均衡程度。在这种情况下，`maxSkew`可以是 1 到 10 之间的任何值。值为 1 意味着您最终可能会得到类似`4,3,3`于`3,4,3`或`3,3,4`跨越3的点差 AZs。相比之下，值为 10 意味着您最终可能会得到类似`10,0,0`于 3 `0,10,0` 或`0,0,10`跨越 3 的点差 AZs。

1. `topologyKey`是其中一个节点标签的密钥，它定义了应用于 Pod 分发的拓扑域的类型。例如，区域点差将具有以下键值对：

   ```
   topologyKey: "topology.kubernetes.io/zone"
   ```

1. 该`whenUnsatisfiable`属性用于确定在无法满足所需约束条件时您希望调度器如何响应。

1. 用于查找匹配`labelSelector`的 pod，以便调度器在根据您指定的约束条件决定将 Pod 放置在何处时可以意识到它们。

除上述内容外，您还可以在 [Kubernetes](https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/) 文档中进一步阅读其他字段。

**Pod 拓扑将约束分布在 3 AZs**  
 ![\[Pod topology spread constraints across 3 AZs\]](http://docs.aws.amazon.com/zh_cn/eks/latest/best-practices/images/reliability/pod-topology-spread-constraints.jpg) 

```
apiVersion: apps/v1
kind: Deployment
metadata:
  name: spread-host-az
  labels:
    app: web-server
spec:
  replicas: 10
  selector:
    matchLabels:
      app: web-server
  template:
    metadata:
      labels:
        app: web-server
    spec:
      topologySpreadConstraints:
      - maxSkew: 1
        topologyKey: "topology.kubernetes.io/zone"
        whenUnsatisfiable: ScheduleAnyway
        labelSelector:
          matchLabels:
            app: express-test
      containers:
      - name: web-app
        image: nginx:1.16-alpine
```

### 运行 Kubernetes 指标服务器
<a name="_run_kubernetes_metrics_server"></a>

安装 Kubernetes [指标服务器](https://github.com/kubernetes-sigs/metrics-server)以帮助扩展您的应用程序。像 [HPA 和 VPA](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) [这样的 Kubernetes 自动扩缩程序插件需要跟踪应用程序的指标才能对其进行扩](https://github.com/kubernetes/autoscaler/tree/master/vertical-pod-autoscaler)展。指标服务器收集可用于做出扩展决策的资源指标。这些指标是从 kubelets 收集的，并以[指标 API 格式](https://github.com/kubernetes/metrics)提供。

指标服务器不保留任何数据，也不是监控解决方案。其目的是向其他系统公开 CPU 和内存使用率指标。如果您想跟踪应用程序在一段时间内的状态，则需要像 Prometheus 或 Amazon 这样的监控工具。 CloudWatch

按照 [EKS 文档](https://docs.aws.amazon.com/eks/latest/userguide/metrics-server.html)在您的 EKS 集群中安装指标服务器。

## 水平吊舱自动扩缩器 (HPA)
<a name="_horizontal_pod_autoscaler_hpa"></a>

HPA 可以根据需求自动扩展您的应用程序，并帮助您避免在流量高峰期影响客户。它在 Kubernetes 中实现为控制循环，定期从中查询提供资源指标 APIs 的指标。

HPA 可以从以下内容检索指标 APIs：1. `metrics.k8s.io`也称为资源指标 API — 提供 pod 2 的 CPU 和内存使用情况。 `custom.metrics.k8s.io`— 提供来自其他指标收集器（例如 Prometheus）的指标；这些指标**是你的 Kubernetes 集群内部的**。3。 `external.metrics.k8s.io`— 提供 Kubernetes 集群**外部**的指标（例如 SQS 队列深度、弹性负载均衡延迟）。

您必须使用这三个指标中的一个 APIs 来提供扩展应用程序的指标。

### 根据自定义或外部指标扩展应用程序
<a name="_scaling_applications_based_on_custom_or_external_metrics"></a>

您可以使用自定义或外部指标，根据 CPU 或内存利用率以外的指标来扩展应用程序。[自定义指标](https://github.com/kubernetes-sigs/custom-metrics-apiserver) API 服务器提供了 HPA 可用于自动扩展`custom-metrics.k8s.io`应用程序的 API。

你可以使用[适用于 Kubernetes APIs 指标的 Prometheus 适配器从 Prometheus](https://github.com/directxman12/k8s-prometheus-adapter) 收集指标并与 HPA 一起使用。[在这种情况下，Prometheus 适配器将以 Metrics API 格式公开 Prometheus 指标。](https://github.com/kubernetes/metrics/blob/master/pkg/apis/metrics/types.go)

部署 Prometheus 适配器后，即可使用 kubectl 查询自定义指标。 `kubectl get —raw /apis/custom.metrics.k8s.io/v1beta1/`

顾名思义，外部指标使横向 Pod Autoscaler 能够使用 Kubernetes 集群外部的指标来扩展部署。例如，在批处理工作负载中，通常会根据 SQS 队列中正在运行的作业数量来扩展副本的数量。

要自动扩展 Kubernetes 工作负载，你可以使用 KEDA（Kubernetes 事件驱动的自动扩展），这是一个开源项目，可以根据许多自定义事件推动容器扩展。这篇 [AWS 博客](https://aws.amazon.com/blogs/mt/autoscaling-kubernetes-workloads-with-keda-using-amazon-managed-service-for-prometheus-metrics/)概述了如何使用适用于 Prometheus 的亚马逊托管服务来实现 Kubernetes 工作负载自动扩展。

## 垂直吊舱自动扩缩器 (VPA)
<a name="_vertical_pod_autoscaler_vpa"></a>

VPA 会自动调整 Pod 的 CPU 和内存预留，以帮助您 “调整应用程序的大小”。对于需要垂直扩展的应用程序（通过增加资源分配来完成），您可以使用 [VPA](https://github.com/kubernetes/autoscaler/tree/master/vertical-pod-autoscaler) 自动扩展 Pod 副本或提供扩展建议。

如果 VPA 需要对其进行扩展，您的应用程序可能会暂时不可用，因为 VPA 的当前实现不会对 Pod 进行就地调整；相反，它会重新创建需要扩展的 Pod。

 [EKS 文档](https://docs.aws.amazon.com/eks/latest/userguide/vertical-pod-autoscaler.html)包括设置 VPA 的演练。

 [Fairwinds Goldilocks](https://github.com/FairwindsOps/goldilocks/) 项目提供了一个仪表板，用于可视化针对CPU和内存请求和限制的VPA建议。它的 VPA 更新模式允许你根据 VPA 的建议自动缩放 Pod。

## 更新应用程序
<a name="_updating_applications"></a>

现代应用程序需要快速创新，并具有高度的稳定性和可用性。Kubernetes 为你提供了在不中断客户的情况下持续更新应用程序的工具。

让我们来看看一些能够在不牺牲可用性的情况下快速部署变更的最佳实践。

### 有执行回滚的机制
<a name="_have_a_mechanism_to_perform_rollbacks"></a>

拥有撤消按钮可以避免灾难。在更新生产集群之前，最佳做法是在单独的较低环境（测试或开发环境）中测试部署。使用 CI/CD 管道可以帮助您自动化和测试部署。通过持续部署管道，如果升级碰巧有缺陷，您可以快速恢复到旧版本。

您可以使用部署来更新正在运行的应用程序。这通常是通过更新容器镜像来完成的。你可以用它`kubectl`来更新部署，如下所示：

```
kubectl --record deployment.apps/nginx-deployment set image nginx-deployment nginx=nginx:1.16.1
```

该`--record`参数记录了对部署的更改，并在需要执行回滚时为您提供帮助。 `kubectl rollout history deployment`显示了集群中已记录的部署更改。您可以使用回滚更改。`kubectl rollout undo deployment <DEPLOYMENT_NAME>`

默认情况下，当你更新需要重新创建 pod 的部署时，Deployment 将执行[滚动更新](https://kubernetes.io/docs/tutorials/kubernetes-basics/update/update-intro/)。换句话说，Kubernetes 只会更新部署中正在运行的 Pod 的一部分，而不是一次更新所有 Pod。你可以通过属性控制 Kubernetes 如何执行滚动更新。`RollingUpdateStrategy`

在对 Deployment 执行*滚动更新*时，您可以使用[https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#max-unavailable](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#max-unavailable)属性来指定更新期间不可用的 Pod 的最大数量。Deployment 的`Max Surge`属性允许你设置在所需的 Pod 数量之上可以创建的最大 Pod 数量。

考虑`max unavailable`进行调整，以确保推出不会干扰您的客户。例如，Kubernetes `max unavailable` 默认设置为 25%，这意味着如果你有 100 个 Pod，那么在推出期间可能只有 75 个 Pod 处于活动状态。如果您的应用程序至少需要 80 个 Pod，则此部署可能会造成中断。相反，您可以`max unavailable`将设置为 20%，以确保在整个部署过程中至少有 80 个功能性的 Pod。

### 使用 blue/green 部署
<a name="_use_bluegreen_deployments"></a>

变更本质上是有风险的，但是无法撤消的更改可能会造成灾难性的后果。更改程序允许您通过*回滚*有效地将时间倒流，从而使增强功能和实验更加安全。 Blue/green 部署为您提供了一种在出现问题时快速撤回更改的方法。在此部署策略中，您将为新版本创建环境。此环境与正在更新的应用程序的当前版本相同。配置新环境后，流量就会路由到新环境。如果新版本在不产生错误的情况下产生预期的结果，则旧环境将被终止。否则，流量将恢复到旧版本。

您可以通过创建与现有版本 blue/green 部署相同的新部署来在 Kubernetes 中执行部署。确认新部署中的 Pod 运行没有错误后，您可以通过更改服务中将流量路由到应用程序的 Pod 的`selector`规范，开始向新部署发送流量。

许多持续集成工具，例如 [Flux](https://fluxcd.io)、[Jenkins 和 Spinn](https://www.jenkins.io) [aker](https://spinnaker.io)，都允许你实现部署的自动化。 blue/green AWS 容器博客包括使用 AWS Load Balancer 控制器的演练：[使用 AWS Load Balancer 控制器进行 blue/green 部署、金丝雀部署和测试 A/B ](https://aws.amazon.com/blogs/containers/using-aws-load-balancer-controller-for-blue-green-deployment-canary-deployment-and-a-b-testing/) 

### 使用 Canary 部署
<a name="_use_canary_deployments"></a>

Canary 部署是 blue/green 部署的一种变体，可以显著消除变更带来的风险。在此部署策略中，除了旧部署之外，您还创建了一个包含更少 Pod 的新部署，并将一小部分流量转移到新的部署。如果指标表明新版本的性能与现有版本一样好或更好，则可以逐步增加新部署的流量，同时扩大其规模，直到所有流量都转移到新部署。如果出现问题，您可以将所有流量路由到旧部署，并停止向新部署发送流量。

[尽管 Kubernetes 没有提供原生方式来执行金丝雀部署，但你可以在 Istio 中使用 Fl [ag](https://github.com/weaveworks/flagger) ger 等工具。](https://docs.flagger.app/tutorials/istio-progressive-delivery)

## Health 检查和自我修复
<a name="_health_checks_and_self_healing"></a>

没有哪个软件是没有错误的，但是 Kubernetes 可以帮助你最大限度地减少软件故障的影响。过去，如果应用程序崩溃，则必须有人通过手动重启应用程序来纠正这种情况。Kubernetes 使您能够检测 Pod 中的软件故障，并自动将其替换为新的副本。借助 Kubernetes，您可以监控应用程序的运行状况并自动替换不健康的实例。

[Kubernetes 支持三种类型的运行状况检查：](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/)

1. 活性探测

1. 启动探测（Kubernetes 版本 1.16\$1 中支持）

1. 准备情况调查

 [Kubernetes 代理 Kubel](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/) et 负责运行上述所有检查。Kubelet 可以通过三种方式检查 Pod 的生命值：kubelet 可以在容器内运行 shell 命令，向其容器发送 HTTP GET 请求，或者在指定端口上打开 TCP 套接字。

如果您选择`exec`基于探测器，在容器内运行 shell 脚本，请确保 shell 命令在`timeoutSeconds`值到期*之前*退出。否则，您的节点将有`<defunct>`进程，从而导致节点故障。

## 建议
<a name="_recommendations_2"></a>

### 使用 Liveness Probe 移除不健康的 pod
<a name="_use_liveness_probe_to_remove_unhealthy_pods"></a>

Liveness 探测器可以检测到进程继续运行但应用程序无响应的*死锁*情况。例如，如果您正在运行在端口 80 上侦听的 Web 服务，则可以将 Liveness 探测器配置为在 Pod 的端口 80 上发送 HTTP GET 请求。Kubelet 会定期向 Pod 发送 GET 请求并期望得到响应；如果 Pod 在 200-399 之间做出响应，那么 kubelet 会认为 Pod 运行状况良好；否则，Pod 将被标记为运行状况不佳。如果 Pod 持续未通过运行状况检查，kubelet 会将其终止。

您可以使用`initialDelaySeconds`来延迟第一次探测。

使用 Liveness Probe 时，请确保您的应用程序不会遇到所有 Pod 同时无法通过 Liveness 探测的情况，因为 Kubernetes 会尝试替换所有 Pod，这将使您的应用程序离线。此外，Kubernetes 将继续创建新的 Pod，这些容器也将无法通过 Liveness Probes，这给控制平面带来不必要的压力。避免将 Liveness Probe 配置为依赖于 Pod 外部的因素，例如外部数据库。换句话说，无响应的external-to-your-Pod 数据库不应该使你的 Pod 无法通过 Liveness Probes。

Sandor Szücs 的帖子 [LIVENESS PROBES ARE DANGERS 描述了探测器](https://srcco.de/posts/kubernetes-liveness-probes-are-dangerous.html)配置错误可能导致的问题。

### 对于需要更长时间才能启动的应用程序，请使用 Startup Probe
<a name="_use_startup_probe_for_applications_that_take_longer_to_start"></a>

当您的应用程序需要更多时间才能启动时，您可以使用启动探测器来延迟存活和就绪探测器。例如，需要整合数据库缓存的 Java 应用程序可能需要长达两分钟的时间才能完全运行。在完全运行之前，任何 Liveness 或 Readiness 探测都可能失败。配置启动探测将允许 Java 应用程序在执行 Liveness 或 Readiness Probe 之前恢复正*常*。

在启动探测成功之前，所有其他探测器都将被禁用。你可以定义 Kubernetes 等待应用程序启动的最长时间。如果在最大配置时间之后，Pod 仍然无法通过启动探测，则它将被终止，并创建一个新的 Pod。

启动探测器与 Liveness Probe 类似，如果它们失败，则会重新创建 Pod。正如 Ricardo A. 在他的《[神奇探测器及其配置方法](https://medium.com/swlh/fantastic-probes-and-how-to-configure-them-fef7e030bd2f)》一文中所解释的那样，当应用程序的启动时间不可预测时，应使用启动探测器。如果您知道应用程序需要十秒钟才能启动，则应改用 Liveness/Readiness/Readiness Probe。`initialDelaySeconds`

### 使用就绪探测器检测部分不可用
<a name="_use_readiness_probe_to_detect_partial_unavailability"></a>

*当 Liveness 探测器在应用程序中检测到通过终止 Pod（因此重新启动应用程序）来解决的故障时，Readiness Probe 会检测应用程序可能暂时不可用的情况。*在这些情况下，应用程序可能会暂时失去响应；但是，该操作完成后，预计它会恢复正常。

例如，在密集的磁盘 I/O 操作期间，应用程序可能暂时无法处理请求。在这里，终止应用程序的 Pod 不是一种补救措施；同时，发送到 Pod 的其他请求可能会失败。

你可以使用 Readiness Probe 来检测应用程序中的暂时不可用，并停止向其 Pod 发送请求，直到它恢复正常运行。*与 Liveness Probe 不同，失败会导致 Pod 的重新创建，而失败的就绪探测将意味着 Pod 不会收到来自 Kubernetes 服务的任何流量*。当就绪探测成功后，Pod 将恢复接收来自服务的流量。

就像 Liveness Probe 一样，避免配置依赖于 Pod 外部资源（例如数据库）的就绪探针。以下是配置不当的 Readiness 会导致应用程序无法运行的场景——如果 Pod 的就绪探测在应用程序的数据库无法访问时失败，那么其他 Pod 副本也会同时失败，因为它们具有相同的运行状况检查标准。*以这种方式设置探测器可以确保每当数据库不可用时，Pod 的就绪探测器就会失败，并且 Kubernetes 将停止向所有 Pod 发送流量。*

使用就绪探测器的副作用是，它们可能会增加更新部署所需的时间。除非就绪探测成功，否则新副本将不会接收流量；在此之前，旧副本将继续接收流量。



## 应对中断
<a name="_dealing_with_disruptions"></a>

Pod 的寿命是有限的，即使你有长时间运行的 Pod，也要谨慎地确保 Pod 在时机成熟时正确终止。根据您的升级策略，Kubernetes 集群升级可能需要您创建新的工作节点，这需要在较新的节点上重新创建所有 Pod。适当的终止处理和 Pod 中断预算可以帮助您避免服务中断，因为 Pod 会从较旧的节点中移除，并在较新的节点上重新创建。

升级工作节点的首选方法是创建新的工作节点并终止旧的工作节点。在终止工作节点之前，你应该`drain`这样做。当工作节点耗尽时，其所有 pod 都会被*安全驱逐出去*。安全是这里的关键词；当工作人员身上的吊舱被驱逐时，它们不会简单地收到`SIGKILL`信号。取而代之的是，向被驱逐的 Pod 中每个容器的主进程 (PID 1) 发送`SIGTERM`信号。`SIGTERM`信号发送后，Kubernetes 将在发送`SIGKILL`信号之前给进程一段时间（宽限期）。默认情况下，此宽限期为 30 秒；您可以通过在 kubectl 中使用`grace-period`标志或在 Podspec `terminationGracePeriodSeconds` 中声明来覆盖默认值。

 `kubectl delete pod <pod name> —grace-period=<seconds>` 

通常会有主进程没有 PID 1 的容器。考虑一下这个基于 Python 的示例容器：

```
$ kubectl exec python-app -it ps
 PID USER TIME COMMAND
 1   root 0:00 {script.sh} /bin/sh ./script.sh
 5   root 0:00 python app.py
```

在此示例中，shell 脚本接收`SIGTERM`，但主进程（在本示例中恰好是 Python 应用程序）没有收到`SIGTERM`信号。当 Pod 被终止时，Python 应用程序将被突然终止。这可以通过更改容器来启动 Python 应用程序来修复。[https://docs.docker.com/engine/reference/builder/#entrypoint](https://docs.docker.com/engine/reference/builder/#entrypoint)或者，你可以使用像 [dumb-init 这样的工具来](https://github.com/Yelp/dumb-init)确保你的应用程序可以处理信号。

您还可以使用[容器挂钩](https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks)在容器启动或停止时执行脚本或 HTTP 请求。`PreStop`挂钩操作在容器收到`SIGTERM`信号之前运行，并且必须在发送此信号之前完成。该`terminationGracePeriodSeconds`值从`PreStop`挂钩操作开始执行时起适用，而不是从发送`SIGTERM`信号时起生效。

## 建议
<a name="_recommendations_3"></a>

### 使用 Pod 中断预算保护关键工作负载
<a name="_protect_critical_workload_with_pod_disruption_budgets"></a>

如果应用程序的副本数量低于声明的阈值，Pod Disruption Budget 或 PDB 可以暂时停止驱逐过程。一旦可用副本的数量超过阈值，驱逐过程就会继续。您可以使用 PDB 来声明副本的`maxUnavailable`数量`minAvailable`和数量。例如，如果您希望应用程序至少有三个副本可用，则可以创建一个 PDB。

```
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: my-svc-pdb
spec:
  minAvailable: 3
  selector:
    matchLabels:
      app: my-svc
```

上述 PDB 策略告诉 Kubernetes 在有三个或更多副本可用之前停止驱逐过程。节点耗尽尊重`PodDisruptionBudgets`。在 EKS 托管节点组升级期间，[节点会被耗尽，超时时间为十五分钟](https://docs.aws.amazon.com/eks/latest/userguide/managed-node-update-behavior.html)。十五分钟后，如果未强制更新（该选项在 EKS 控制台中称为 “滚动更新”），则更新将失败。如果强制更新，则会删除 pod。

[对于自我管理的节点，您还可以使用诸如 [AWS 节点终止处理程序](https://github.com/aws/aws-node-termination-handler)之类的工具，该工具可确保 Kubernetes 控制平面对可能导致您的 EC2 实例不可用的事件（例如 EC2 [维护事件和 EC](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/monitoring-instances-status-check_sched.html) 2 Spot 中断）做出适当的响应。](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/spot-interruptions.html)它使用 Kubernetes API 封锁节点以确保没有新的 Pod 被调度，然后将其耗尽，终止所有正在运行的 Pod。

你可以使用 Pod 反亲和性在不同的节点上调度 Deployment 的 Pod，避免节点升级期间与 PDB 相关的延迟。

### 练习混沌工程
<a name="_practice_chaos_engineering"></a>

Chaos Engineering 是一门在分布式系统上进行实验的学科，目的是建立人们对系统承受生产中动荡条件的能力的信心。

多米尼克·托尔诺（Dominik Tor [now）在他的博客中解释说，Kubernetes是一个声明式系统](https://medium.com/@dominik.tornow/the-mechanics-of-kubernetes-ac8112eaa302)，其中 “*用户向系统提供所需系统状态的表示形式。然后，系统会考虑当前状态和所需状态，以确定从当前状态过渡到所需状态的命令顺序。* “ 这意味着 Kubernetes 始终存储*所需的状态*，如果系统出现偏差，Kubernetes 将采取措施恢复状态。例如，如果工作节点不可用，Kubernetes 会将这些 Pod 重新调度到另一个工作节点上。同样，如果`replica`崩溃，[部署控制器将](https://kubernetes.io/docs/concepts/architecture/controller/#design)创建一个新的。`replica`通过这种方式，Kubernetes 控制器可以自动修复故障。

像 [Gremlin](https://www.gremlin.com) 这样的混沌工程工具可以帮助你测试 Kubernetes 集群的弹性并识别单点故障。在集群（及其他集群）中引入人为混乱的工具可以发现系统性弱点，为识别瓶颈和错误配置提供机会，并在受控环境中纠正问题。Chaos Engineering 的理念主张故意破坏事物，并对基础设施进行压力测试，以最大限度地减少意想不到的停

### 使用服务网格
<a name="_use_a_service_mesh"></a>

您可以使用服务网格来提高应用程序的弹性。服务网格支持 service-to-service通信并提高微服务网络的可观察性。大多数服务网格产品的工作原理是在每个服务旁边运行一个小型网络代理，用于拦截和检查应用程序的网络流量。您可以将应用程序放置在网格中，而无需修改应用程序。使用服务代理的内置功能，您可以让它生成网络统计信息、创建访问日志，以及向出站请求添加 HTTP 标头以进行分布式跟踪。

服务网格可通过自动请求重试、超时、断路和速率限制等功能，帮助您提高微服务的弹性。

如果您运营多个集群，则可以使用服务网格来实现跨集群 service-to-service通信。

### 服务网格
<a name="_service_meshes"></a>
+  [Istio](https://istio.io) 
+  [LinkerD](http://linkerd.io) 
+  [领事](https://www.consul.io) 



## 可观测性
<a name="_observability"></a>

可观察性是一个总称，包括监控、记录和跟踪。基于微服务的应用程序本质上是分布式的。与仅仅监控单个系统的单片应用程序不同，在分布式应用程序架构中，您需要监控每个组件的性能。您可以使用集群级别的监控、日志记录和分布式跟踪系统来识别集群中的问题，以免它们对客户造成干扰。

Kubernetes 内置的故障排除和监控工具有限。指标服务器收集资源指标并将其存储在内存中，但不会将其保存。你可以使用 kubectl 查看 Pod 的日志，但是 Kubernetes 不会自动保留日志。分布式跟踪的实现要么在应用程序代码级别完成，要么使用服务网格完成。

Kubernetes 的可扩展性在这里大放异彩。Kubernetes 允许您使用首选的集中式监控、日志和跟踪解决方案。

## 建议
<a name="_recommendations_4"></a>

### 监控您的应用程序
<a name="_monitor_your_applications"></a>

在现代应用程序中，您需要监控的指标数量在不断增长。如果你有一种自动化的方式来跟踪应用程序，这样你就可以专注于解决客户的难题，这会有所帮助。Prometheus 或 Container [Insig](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/ContainerInsights.html) [hts 等集群范围的监控工具可以监控您的集群和工作负载，并在出现问题](https://prometheus.io)时或CloudWatch最好在出现问题之前向您提供信号。

监控工具允许您创建运营团队可以订阅的警报。考虑使用规则来激活警报，这些事件如果加剧可能会导致中断或影响应用程序性能。

如果您不清楚应该监控哪些指标，可以从以下方法中汲取灵感：
+  [红色方法](https://www.weave.works/blog/a-practical-guide-from-instrumenting-code-to-specifying-alerts-with-the-red-method)。代表请求、错误和持续时间。
+  [使用方法](http://www.brendangregg.com/usemethod.html)。代表利用率、饱和度和错误。

Sysdig 的文章《[在 Kubernetes 上发出警报的最佳实践](https://sysdig.com/blog/alerting-kubernetes/)》包括一份全面的列表，列出了可能影响应用程序可用性的组件。

### 使用 Prometheus 客户端库公开应用程序指标
<a name="_use_prometheus_client_library_to_expose_application_metrics"></a>

除了监控应用程序状态和汇总标准指标外，您还可以使用 [Prometheus 客户端](https://prometheus.io/docs/instrumenting/clientlibs/)库公开特定于应用程序的自定义指标，以提高应用程序的可观察性。

### 使用集中式日志工具收集和保留日志
<a name="_use_centralized_logging_tools_to_collect_and_persist_logs"></a>

登录 EKS 分为两类：控制平面日志和应用程序日志。EKS 控制平面日志记录可直接从控制平面向账户中的 CloudWatch 日志提供审计和诊断日志。应用程序日志是由集群内运行的 Pod 生成的日志。应用程序日志包括运行业务逻辑应用程序的 Pod 和 Kubernetes 系统组件（例如 CoreDNS、Cluster Autoscaler、Prometheus 等）生成的日志。

 [EKS 提供五种类型的控制平面日志](https://docs.aws.amazon.com/eks/latest/userguide/control-plane-logs.html)：

1. Kubernetes API 服务器组件日志

1. 审核

1. 身份验证器

1. 控制器管理器

1. 调度器

控制器管理器和调度程序日志可以帮助诊断控制平面问题，例如瓶颈和错误。默认情况下，EKS 控制平面日志不会发送到 CloudWatch 日志。您可以启用控制平面日志记录，并选择要为账户中的每个集群捕获的 EKS 控制平面日志类型

收集应用程序日志需要在集群中安装日志聚合器工具，例如 Fluen [t Bit](http://fluentbit.io)、[Fluentd](https://www.fluentd.org) 或 Contain [CloudWatcher Insigh](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/deploy-container-insights-EKS.html) ts。

Kubernetes 日志聚合器工具以节点身份运行 DaemonSets 并从节点中抓取容器日志。然后，应用程序日志被发送到一个集中的目标进行存储。例如，Conta CloudWatch iner Insights 可以使用 Fluent Bit 或 Fluentd 来收集日志并将其发送到CloudWatch 日志进行存储。Fluent Bit 和 Fluentd 支持许多流行的日志分析系统，例如 Elasticsearch 和 InfluxDB，使您能够通过修改 Fluent bit 或 Fluentd 的日志配置来更改日志的存储后端。

### 使用分布式跟踪系统来识别瓶颈
<a name="_use_a_distributed_tracing_system_to_identify_bottlenecks"></a>

典型的现代应用程序的组件分布在网络上，其可靠性取决于组成该应用程序的每个组件的正常运行。您可以使用分布式跟踪解决方案来了解请求的流向以及系统的通信方式。跟踪可以向您显示应用程序网络中存在瓶颈的位置，并防止可能导致级联故障的问题。

在应用程序中实现跟踪有两种选择：您可以使用共享库在代码级别实现分布式跟踪，也可以使用服务网格。

在代码级别实现跟踪可能不利。在这种方法中，你必须对你的代码进行更改。如果您有多语言应用程序，则情况会更加复杂。您还负责在服务中维护另一个图书馆。

[LinkerD](http://linkerd.io) 和 [Istio](http://istio.io) 等服务网格可用于在应用程序中实现分布式跟踪，只需对应用程序代码进行最少的更改。您可以使用服务网格来标准化指标生成、记录和跟踪。

像 [AWS X-Ray](https://aws.amazon.com/xray/)、[Jaeger](https://www.jaegertracing.io) 这样的追踪工具支持共享库和服务网格实现。

考虑使用像 [AWS X-Ray](https://aws.amazon.com/xray/) 或 [Jaeger](https://www.jaegertracing.io) 这样的追踪工具，它同时支持（共享库和服务网格）实现，这样以后采用服务网格时就不必切换工具了。