本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
运行高可用应用程序
您的客户希望您的应用程序始终可用,包括在您进行更改时,尤其是在流量激增期间。可扩展且具有弹性的架构可让您的应用程序和服务不受干扰地运行,从而让您的用户满意。可扩展的基础架构会根据业务需求进行增长和缩小。消除单点故障是提高应用程序可用性并使其具有弹性的关键步骤。
使用 Kubernetes,您可以操作应用程序,并以高可用性和弹性的方式运行它们。它的声明式管理可确保在你设置应用程序后,Kubernetes 将不断尝试将当前状态与所需状态相匹配。
建议
配置 Pod 中断预算
Pod 中断预算
避免运行单例 Pod
如果你的整个应用程序在单个 Pod 中运行,那么当该 Pod 被终止时,你的应用程序将不可用。与其使用单个 pod 部署应用程序,不如创建部署
运行多个副本
使用部署运行应用程序的多个副本 Pod 有助于它以高可用性的方式运行。如果一个副本出现故障,其余副本仍然可以运行,尽管容量会降低,直到 Kubernetes 创建另一个 Pod 来弥补损失。此外,您可以使用水平 Pod 自动扩缩器
跨节点调度副本
如果所有副本都在同一个节点上运行,并且该节点不可用,则运行多个副本不会很有用。考虑使用 pod 反关联性或 pod 拓扑分布约束将部署的副本分布到多个工作节点上。
您可以通过在多个可用区运行典型应用程序来进一步提高其可靠性。
使用 Pod 反关联性规则
下面的清单告诉Kubernetes调度器倾向于将容器放置在不同的节点和可用区上。它不需要不同的节点或可用区,因为如果有,那么 Kubernetes 将无法调度任何 Pod 在每个可用区中运行。如果你的应用程序只需要三个副本,你可以使用 requiredDuringSchedulingIgnoredDuringExecutiontopologyKey: topology.kubernetes.io/zone,而且 Kubernetes 调度器不会在同一个可用区中调度两个 pod。
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 拓扑分布约束
与 pod 反关联性规则类似,pod 拓扑分布约束允许您让应用程序在不同的故障(或拓扑)域(例如主机或可用区)上可用。当你试图通过在每个不同的拓扑域中拥有多个副本来确保容错能力和可用性时,这种方法非常有效。另一方面,Pod 反关联性规则很容易产生拓扑域中只有一个副本的结果,因为彼此之间具有反亲和力的 Pod 会产生排斥效果。在这种情况下,专用节点上的单个副本不太适合容错,也不能很好地利用资源。借助拓扑分布限制,您可以更好地控制调度程序应尝试在拓扑域中应用的分布或分布。以下是此方法中使用的一些重要属性:
-
maxSkew用于控制或确定拓扑域中可能出现不均匀情况的最大点。例如,如果应用程序有 10 个副本并部署在 3 个可用区中,则无法获得均匀的分布,但可以影响分布的均匀程度。在这种情况下,maxSkew可以是 1 到 10 之间的任何值。值为 1 表示您最终可能会得到与 3 个可用区相似的点差4,3,3,3,4,3或者3,3,4跨越 3 个可用区。相比之下,值为 10 意味着您最终可能会得到与 3 个可用区相似的点差10,0,0,0,10,0或者0,0,10跨越 3 个可用区。 -
topologyKey是其中一个节点标签的密钥,它定义了应用于 pod 分发的拓扑域的类型。例如,分区分布将具有以下键值对:topologyKey: "topology.kubernetes.io/zone" -
该
whenUnsatisfiable属性用于确定在无法满足所需的约束条件时您希望调度程序如何响应。 -
用于查找匹配
labelSelector的 pod,以便调度器在根据您指定的约束条件决定放置 pod 的位置时可以知道它们。
除了上述内容外,您还可以在 Kubernetes 文档中进一步阅读其他字段。
Pod 拓扑将限制分布在 3 个可用区
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 指标服务器
安装 Kubernetes 指标服务器
指标服务器不保留任何数据,也不是监控解决方案。其目的是向其他系统公开 CPU 和内存使用率指标。如果您想跟踪应用程序在一段时间内的状态,则需要像 Prometheus 或 Amazon 这样的监控工具。 CloudWatch
按照 EKS 文档在 EKS 集群中安装指标服务器。
水平吊舱自动缩放器 (HPA)
HPA 可以根据需求自动扩展您的应用程序,并帮助您避免在流量高峰期间影响客户。它在 Kubernetes 中是作为控制回路实现的,它定期从提供资源指标的 API 中查询指标。
HPA 可以从以下 API 检索指标:1.metrics.k8s.io也称为资源指标 API — 为 pod 2 提供 CPU 和内存使用情况。custom.metrics.k8s.io— 提供来自其他指标收集器(如 Prometheus)的指标;这些指标是您的 Kubernetes 集群内部的。3.external.metrics.k8s.io — 提供您的 Kubernetes 集群外部的指标(E.g.、SQS 队列深度、ELB 延迟)。
您必须使用这三个 API 之一来提供扩展应用程序的指标。
根据自定义或外部指标扩展应用程序
您可以使用自定义或外部指标,根据 CPU 或内存利用率以外的指标来扩展应用程序。自定义指标 custom-metrics.k8s.io API,HPA 可以使用它来自动扩展应用程序。
你可以使用适用于 Kubernetes 指标的 Prometheus Adapter API 从 Prometheus 收集指标
部署 Prometheus 适配器后,您可以使用 kubectl 查询自定义指标。kubectl get —raw /apis/custom.metrics.k8s.io/v1beta1/
顾名思义,外部指标为水平 Pod Autoscaler 提供了使用 Kubernetes 集群外部指标来扩展部署的能力。例如,在批处理工作负载中,通常根据 SQS 队列中正在运行的任务数量来扩展副本的数量。
要自动扩展 Kubernetes 工作负载,你可以使用 KEDA(Kubernetes Event-driven Autoscaling),这是一个开源项目,可以基于多个自定义事件推动容器扩展。这篇 AWS 博客
垂直吊舱自动缩放器 (VPA)
VPA 会自动调整 Pod 的 CPU 和内存预留,以帮助你 “调整应用程序的大小”。对于需要垂直扩展的应用程序(通过增加资源分配来实现),您可以使用 VPA
如果 VPA 需要对其进行扩展,您的应用程序可能会暂时不可用,因为 VPA 的当前实现不会对 Pod 进行就地调整;相反,它将重新创建需要扩展的 Pod。
EKS 文档包括设置 VPA 的演练。
Fairwinds Goldilocks
更新应用程序
现代应用需要快速创新,具有高度的稳定性和可用性。Kubernetes 为您提供了在不中断客户的情况下持续更新应用程序的工具。
让我们来看看一些最佳实践,这些实践使在不牺牲可用性的情况下快速部署变更成为可能。
有执行回滚的机制
拥有撤消按钮可以逃避灾难。最佳做法是在更新生产集群之前,在单独的较低环境(测试或开发环境)中测试部署。使用 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 将执行滚动更新RollingUpdateStrategy
对部署执行滚动更新时,您可以使用该Max UnavailableMax Surge属性允许你设置在所需数量的 Pod 上可以创建的最大 Pod 数量。
考虑max unavailable进行调整以确保推出不会干扰您的客户。例如,Kubernetes max unavailable 默认设置为 25%,这意味着如果您有 100 个 Pod,则在部署期间可能只有 75 个 Pod 处于活动状态。如果您的应用程序至少需要 80 个 Pod,则此次部署可能会造成中断。取而代之的是,你可以设置max unavailable为 20%,以确保在整个部署过程中至少有 80 个功能正常的 Pod。
使用 blue/green 部署
变更本质上是有风险的,但无法撤消的变更可能是灾难性的。更改程序使您能够通过回滚有效地将时间倒流,从而使增强和实验更加安全。 Blue/green 部署为你提供了一种在出现问题时快速撤回更改的方法。在此部署策略中,您将为新版本创建环境。此环境与正在更新的应用程序的当前版本相同。配置新环境后,流量将路由到新环境。如果新版本在不生成错误的情况下产生了所需的结果,则旧环境将终止。否则,流量将恢复到旧版本。
您可以通过创建与现有版本的 blue/green 部署相同的新部署在 Kubernetes 中执行部署。确认新部署中的 Pod 运行没有错误后,您可以通过更改服务中将流量路由到应用程序 Pod 的selector规范,开始向新部署发送流量。
Flux
使用 Canary 部署
Canary 部署是 blue/green 部署的一种变体,可以显著消除变更带来的风险。在此部署策略中,除了原有部署外,你还要使用更少的 Pod 来创建一个新的部署,并将一小部分流量转移到新的部署。如果指标表明新版本的性能与现有版本一样或更好,则可以逐步增加新部署的流量,同时扩大其规模,直到所有流量都转移到新部署为止。如果出现问题,您可以将所有流量路由到旧部署,并停止向新部署发送流量。
尽管 Kubernetes 没有提供执行金丝雀部署的原生方式,但你可以在 Istio 中使用 Fl ag
健康检查和自我修复
没有任何软件是没有错误的,但是 Kubernetes 可以帮助你最大限度地减少软件故障的影响。过去,如果应用程序崩溃,必须有人通过手动重启应用程序来修复这种情况。Kubernetes 让你能够检测 Pod 中的软件故障,并自动将其替换为新的副本。使用 Kubernetes,您可以监控应用程序的运行状况并自动替换运行状况不佳的实例。
Kubernetes 支持三种类型的运行状况检查:https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/
-
活体探测器
-
启动探测(在 Kubernetes 版本 1.16+ 中支持)
-
准备情况调查
Kubelet
如果您选择exec基于探测器,在容器内运行 shell 脚本,请确保 shell 命令在timeoutSeconds值到期之前退出。否则,您的节点将有<defunct>进程,从而导致节点故障。
建议
使用 Liveness Probe 移除不健康的 pod
Liveness 探测器可以检测到进程继续运行但应用程序无响应的死锁情况。例如,如果你正在运行一个监听端口 80 的 Web 服务,你可以配置 Liveness 探测器在 Pod 的端口 80 上发送 HTTP GET 请求。Kubelet 会定期向 Pod 发送 GET 请求并期望得到响应;如果 Pod 在 200-399 之间响应,则 kubelet 认为 Pod 运行正常;否则,该 Pod 将被标记为运行状况不佳。如果 Pod 持续未通过运行状况检查,kubelet 会将其终止。
您可以使用延initialDelaySeconds迟第一次探测。
使用 Liveness 探测器时,请确保您的应用程序不会遇到所有 Pod 同时失败的情况,因为 Kubernetes 会尝试替换所有 Pod,这将使您的应用程序处于离线状态。此外,Kubernetes 将继续创建新的 Pod,这些容器也会失效 Liveness 探测器,从而给控制平面带来不必要的压力。避免将 Liveness Probe 配置为依赖于 Pod 外部的因素,例如外部数据库。换句话说,Pod 外部没有响应的 Pod 数据库不应使你的 Pod 无法运行其 Liveness 探测器。
Sandor Szücs 在《活跃探测器很危险》一文中
对于需要更长时间启动的应用程序,使用启动探测器
当您的应用程序需要更多时间才能启动时,您可以使用启动探测来延迟存活和就绪度探测。例如,需要对数据库中的缓存进行补水处理的 Java 应用程序可能需要长达两分钟才能完全正常运行。任何存活或就绪状态探测器在完全运行之前都可能失败。配置启动探测器将允许 Java 应用程序在执行 Liveness 或 Readiness Probe 之前恢复正常。
在启动探测成功之前,所有其他探测器都将被禁用。你可以定义 Kubernetes 等待应用程序启动的最长时间。如果在最长配置时间之后,Pod 仍然无法通过启动探测,它将被终止,并创建一个新的 Pod。
启动探测器与 Liveness Probe 类似,如果失败,则会重新创建 Pod。正如Ricardo A在他的《神奇探测器及其配置方法》一文中所解释的那样initialDelaySeconds改用 Liveness/Readiness Probe 和。
使用就绪探测来检测部分不可用性
Liveness 探测器检测应用程序中的故障,这些故障可通过终止 Pod(因此重新启动应用程序)来解决,而 Readiness Probe 会检测应用程序可能暂时不可用的情况。在这些情况下,应用程序可能会暂时失去响应;但是,一旦此操作完成,它预计会恢复正常。
例如,在密集的磁盘 I/O 操作期间,应用程序可能暂时无法处理请求。在这里,终止应用程序的 Pod 并不是一种补救措施;同时,发送到 Pod 的其他请求可能会失败。
你可以使用就绪探测器来检测应用程序中的暂时不可用性,并停止向其 Pod 发送请求,直到它恢复正常运行。与Liveness Probe不同,在Liveness Probe中,故障将导致Pod重新创建,而Readiness Probe失败意味着Pod不会收到来自Kubernetes服务的任何流量。当就绪探测成功时,Pod 将恢复接收来自服务的流量。
就像 Liveness 探测器一样,避免配置依赖于 Pod 外部资源(例如数据库)的就绪探测器。以下场景是,配置不当的就绪状态会导致应用程序无法正常运行——如果 Pod 的就绪状态探测在无法访问应用程序的数据库时失败,则其他 Pod 副本也将同时失败,因为它们共享相同的运行状况检查标准。以这种方式设置探测器将确保每当数据库不可用时,Pod 的就绪探测器都会失败,并且 Kubernetes 将停止向所有 Pod 发送流量。
使用就绪探测器的副作用是它们会增加更新部署所需的时间。除非就绪探测成功,否则新副本不会接收流量;在此之前,旧副本将继续接收流量。
处理中断
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-based 样本容器为例:
$ 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应用程序来修复此问题。ENTRYPOINT
您还可以使用容器挂钩PreStop挂接操作在容器接收到SIGTERM信号之前运行,并且必须在发送该信号之前完成。该terminationGracePeriodSeconds值从PreStop挂接操作开始执行时开始生效,而不是从发送SIGTERM信号时开始生效。
建议
使用 Pod 中断预算保护关键工作负载
如果应用程序的副本数量低于申报的阈值,Pod 中断预算或 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 托管节点组升级期间,节点将在十五分钟的超时时间内耗尽。十五分钟后,如果未强制更新(该选项在 EKS 控制台中称为 “滚动更新”),则更新将失败。如果强制更新,则会删除 pod。
对于自管理节点,您还可以使用诸如 AWS 节点终止处理程序之类的工具
您可以使用 Pod 反关联性将部署的 Pod 调度到不同的节点上,避免节点升级期间与 PDB 相关的延迟。
练习混沌工程
Chaos Engineering是一门在分布式系统上进行实验的学科,目的是建立对系统承受生产中动荡条件的能力的信心。
Dominik Tornow在他的博客中解释说,Kubernetes是一个声明式系统,replica崩溃,部署控制器replica通过这种方式,Kubernetes 控制器可以自动修复故障。
像 Gremlin 这样的混沌工程工具
使用服务网格
您可以使用服务网格来提高应用程序的弹性。服务网格支持服务间通信,提高微服务网络的可观察性。大多数服务网格产品的工作原理是在每项服务旁边运行一个小型网络代理,以拦截和检查应用程序的网络流量。您可以将应用程序置于网格中,而无需修改应用程序。使用服务代理的内置功能,您可以让它生成网络统计信息,创建访问日志,并在分布式跟踪的出站请求中添加 HTTP 标头。
服务网格可以通过自动请求重试、超时、断路和速率限制等功能帮助您提高微服务的弹性。
如果您操作多个集群,则可以使用服务网格来启用跨集群的服务间通信。
服务网格
可观测性
可观测性是一个总括性术语,包括监控、记录和跟踪。基于微服务的应用程序本质上是分布式的。与单片应用程序不同,在单片应用程序中,监控单个系统就足够了,在分布式应用程序架构中,您需要监控每个组件的性能。您可以使用集群级监控、日志记录和分布式跟踪系统在集群中的问题干扰客户之前识别这些问题。
Kubernetes 内置的故障排除和监控工具是有限的。metrics-server 收集资源指标并将其存储在内存中,但不将其保存下来。你可以使用 kubectl 查看 Pod 的日志,但是 Kubernetes 不会自动保留日志。分布式跟踪的实现要么在应用程序代码级别完成,要么使用服务网格来完成。
Kubernetes 的可扩展性在这里大放异彩。Kubernetes 允许您自带首选的集中式监控、日志记录和跟踪解决方案。
建议
监控您的应用程序
您需要在现代应用程序中监控的指标数量在持续增长。如果你有一种自动化的方式来跟踪应用程序,这样你就可以专注于解决客户的挑战,这将有所帮助。 Cluster-wide 像 Prometheus
监控工具允许您创建运营团队可以订阅的警报。考虑制定规则,对加剧后可能导致中断或影响应用程序性能的事件激活警报。
如果你不清楚应该监控哪些指标,你可以从这些方法中汲取灵感:
Sysdig 发布的《Kubernetes 警报最佳实践》一文中
使用 Prometheus 客户端库公开应用程序指标
除了监控应用程序状态和聚合标准指标外,您还可以使用 Prometheus 客户端库公开应用程序特定的自定义指标,
使用集中式日志记录工具收集和保存日志
登录 EKS 分为两类:控制平面日志和应用程序日志。EKS 控制平面日志将审计和诊断日志直接从控制平面提供给您账户中的 CloudWatch 日志。应用程序日志是由集群内运行的 Pod 生成的日志。应用程序日志包括运行业务逻辑应用程序的 Pod 和 Kubernetes 系统组件(例如 CoreDNS、集群自动缩放器、Prometheus 等)生成的日志。
-
Kubernetes API 服务器组件日志
-
审核
-
身份验证器
-
控制器管理器
-
调度器
控制器管理器和调度器日志可以帮助诊断控制平面问题,例如瓶颈和错误。默认情况下,EKS 控制平面日志不会发送到 CloudWatch 日志。您可以启用控制平面日志记录并选择要为账户中的每个集群捕获的 EKS 控制平面日志的类型
收集应用程序日志需要在集群中安装 Flu ent Bit
Kubernetes 日志聚合器工具作为节点运行 DaemonSets 并抓取容器日志。然后,应用程序日志被发送到一个集中目标进行存储。例如, CloudWatch 容器洞察可以使用 Fluent Bit 或 Fluentd 来收集日志并将其发送到CloudWatch 日志进行存储。Fluent Bit和Fluentd支持许多流行的日志分析系统,例如Elasticsearch和InfluxDB,使您能够通过修改Fluent位或Fluentd的日志配置来更改日志的存储后端。
使用分布式跟踪系统识别瓶颈
典型的现代应用程序的组件分布在网络上,其可靠性取决于组成该应用程序的每个组件的正常运行。您可以使用分布式跟踪解决方案来了解请求的流向和系统的通信方式。跟踪可以向您显示应用程序网络中存在的瓶颈,并防止可能导致级联故障的问题。
您可以通过两种方式在应用程序中实现跟踪:您可以使用共享库在代码级别实现分布式跟踪,也可以使用服务网格。
在代码级别实现跟踪可能是不利的。在此方法中,你必须对代码进行更改。如果你有通晓多种语言的应用程序,这会更加复杂。您还负责在所有服务中维护另一个库。
LinkerD
AWS X-Ray
考虑使用像 AWS X-Ray