与许多组织类似,您会采用各种风险管理和风险缓解策略以使您的系统保持正运转,包括您的 Google Kubernetes 引擎 (GKE) 环境。这些策略可确保发生可预测和不可预测停机事件期间的业务连续性,对于现在正在努力降低疫情对其业务影响的企业而言,它们的重要性尤为凸显。
本文是两篇相关博文中的第一篇,我们将为您提供有关如何设置 GKE 集群以便在所谓的第零日以提高可用性的建议和最佳实践。在第二篇文章中,我们将介绍在您的集群正常运转后的第二日的高可用性最佳实践。
当考虑 GKE 集群的高可用性时,第零日往往被忽视,因为很多人都认为中断和维护是后续第二日运营的组成部分。而实际上,在部署工作负载之前,有必要仔细规划您的 GKE 集群的拓扑结构和配置。
为您的工作负载选择正确的拓扑、规模和健康检查
创建 GKE 环境和部署工作负载之前,您需要确定一些重要的设计点。
为您的集群选择正确的拓扑
GKE 提供两类集群:区域 (regional) 和可用区 (zonal)。在一个可用区集群拓扑中,集群控制平面和节点均在一个您创建集群时所指定的计算区域中运行。在区域集群中,控制平面和节点在单一 区域内跨多个可用区被复制。
区域集群由一个三节点的 Kubernetes 控制平面组成,与可用区集群相比,能够为您的集群的控制平面 API 提供更高的可用性。如果控制平面不可用,在节点运行的现有工作负载不受影响,尽管如此,但一些应用程序高度依赖集群 API 的可用性。对于此类工作负载,您最好使用区域集群拓扑。
当然,选择区域集群也并不足以保护 GKE 集群,因为伸缩、调度和替换 pod 是控制平面的“责任”,如果控制平面不可用,可能会影响您的集群的可靠性,这只能在控制平面重新变为可用时才可恢复。
您还应当切记,区域集群有冗余控制平面和节点。在区域拓扑中,节点跨不同可用区冗余,这可能导致成本不菲的跨可用区网络流量。
最后,尽管区域集群自动伸缩功能可尽最大努力在三个可用区之间分配资源,但除非发生扩容/收缩操作,否则不会自动重新平衡它们。
总之,为了实现 Kubernetes API 的高可用性以及在控制平面上进行维护时将对集群的干扰降至最低,我们建议您构建区域集群时,将节点部署到三个不同的可用区 —— 同时,建议您关注自动伸缩的功能。
水平和垂直伸缩
容量规划至关重要,但您不能预测一切。为了确保您的工作负载在峰值负载时可正常运转 —— 以及在正常或者低负载时控制成本 —— 我们建议您充分利用最适合您需求的 GKE 自动伸缩功能。
使用 Cluster Autoscaler 能够基于需求自动调整您的节点池的大小。
使用 Pod 水平自动伸缩功能基于利用率指标自动增加或者减少 pod 数量。
将 Pod 垂直自动伸缩 (VPA) 功能与节点自动配置(NAP,也称为节点池自动配置)功能结合使用,使 GKE 能够高效地在水平 (pod) 和垂直(节点)方向上对集群进行伸缩。VPA 可自动为您的容器设置 CPU、内存请求和限制的值。NAP 自动管理节点池,并删除仅从用户创建的节点池启用新节点的默认约束条件。
以上建议有助于优化成本。例如,NAP 通过在未充分利用期间移除节点来降低成本。但也许您不太关心成本,而更关心延迟和可用性 —— 在这种情况下,您可能希望从一开始就创建一个大集群并且使用 GCP 预留功能以保证您所需要的容量。不过,这可能是一种更高成本的方案。
检查默认监控设置
Kubernetes“擅长”观察工作负载的行为并确保负载始终均匀分布。然后,通过向 Kubernetes 公开您的工作负载的特定信号,可以进一步优化工作负载可用性。这些信号 —— 就绪 (Readiness) 和存活 (Liveness) 信号,为 Kubernetes 提供了有关您的工作负载的更多信息,能够帮助其确定工作负载是否正常工作并且为接收流量做好准备。让我们详细了解一下就绪探针和存活探针之间的差异。
每个应用程序行为各异:一些应用程序可能比其他应用程序需要更长时间来初始化;一些是运行时间较长并且可能会被误认为不可用的批处理程序。就绪探针和存活探针正是为了这个目的而设计 —— 旨在让 Kubernetes 了解工作负载的可接受行为。例如,一个应用程序可能需要长时间才能启动,在此期间,您不希望 kubernetes 开始向其发送客户流量,因为它还没准备好接受流量。利用就绪探针,您可以为 Kubernetes 提供有关何时应用程序已完成初始化并准备好为您的最终用户服务的准确信号。
务必设置就绪探针以确保 Kubernetes 知道工作负载何时真正准备好接受流量。同样地,设置存活探针可“告诉”Kubernetes 何时工作负载实际上无响应或者只是忙于执行 CPU 密集型工作。
最后,就绪探针和存活探针的好坏取决于它们如何被定义和编码。确保对您所创建的任何探针进行测试和验证。
正确设置您的 Deployment
每个应用程序具有不同的特点。有些是批处理工作负载,有些基于无状态微服务,有些基于有状态数据库。为了确保 Kubernetes 了解您的应用程序约束,您可以使用 Kubernetes 的 Deployment 控制器管理您的工作负载。一个 Deployment 控制器描述了期望的状态,并且与 Kubernetes 的调度器共同对实际状态进行变更以达到期望状态。
您的应用程序是否有状态?
如果您的应用程序需要在会话之间保存其状态,例如,数据库,则考虑使用 StatefulSet —— 一个 Kubernetes 控制器,能够以适当处理有状态应用程序的独特特性的方式管理和维护一个或多个 Pod。它与诸如 ReplicaSet 和 Deployment 的其他管理 pod 的 kubernetes 控制器类似。但是不同于 Deployment 的是,Statefulset 不假定 Pod 是可互换的。
为了维持状态,StatefulSet 还需要 Persistent Volume,这样,托管应用程序就可以在重启时保存和恢复数据。Kubernetes 提供 Storage Class、Persistent Volume 和 Persistent Volume Claim 作为云存储之上的一个抽象层。
了解 Pod 亲和性
您是否计划将所有副本置于同一节点?如果该节点发生故障会怎样?一次丢失所有副本可以吗?利用 Kubernetes Pod 亲和性和反亲和性规则,您可以控制您的 Pod 及其任何副本的放置。
为了避免单一故障点,利用 Pod 反亲和性指示 Kubernetes 不要将 Pod 并置于同一节点上。对于有状态应用程序,这可能是一个关键配置,尤其是,如果它需要最少数量的副本(例如,超过法定人数)以正常运行。
例如,Apache ZooKeeper 需要足够数量的服务器才能成功地提交对数据的修改。对于一个三服务器组合,两个服务器必须是“健康的”才能成功进行写操作。因此,弹性部署必须确保服务器跨故障域被部署。
因此,为了避免由于节点丢失导致的停机,我们建议您排除在同一机器中并置多个应用程序实例的做法。您可以利用 Pod 反亲和性做到这一点。
反过来说,有时,您可能希望将一组 Pod 置于同一个节点,以受益于其邻近性,并且因此在彼此通信时能够确保延迟更少、性能更好。利用 Pod 亲和性,您可以实现这一目的。
以 Redis —— 另一个有状态应用程序为例,它可能正在为您的 Web 应用提供内存缓存。在这一部署中,您可能希望尽可能将 Web 服务器与缓存并置以避免延迟以增强性能。
预测中断
一旦完成对您的 GKE 集群的配置并且应用程序已经基于其运行,则是时候思考一下如果负载增加或者发生中断您将如何应对。
完全数字化需要更好的容量规划
基于 GKE 运行您的 Kubernetes 集群使您不必考虑物理基础架构以及如何对其进行扩展。尽管如此,仍然强烈建议进行容量规划,尤其是,如果您认为可能会增加负载。
考虑使用预留实例以保证任何预期的资源需求激增。GKE 支持特定(机器类型和规格)和非特定预留。一旦设置预留,节点将自动在后台从专为您预留的资源池中使用预留资源。
确保制定支持计划
Google Cloud Support 是一个由全球工程师组成的团队,7*24 小时全天候工作以帮助您解决可能遇到的任何问题。在系统启动和运行并进入生产状态之前,务必利用这段好时机确保您已制定了正确的 Cloud Support 计划以帮助您应对可能发生的问题。
审查支持计划,确保有与您业务相适应的正确举措。
审查您的支持用户配置,确保团队成员能够启动支持案例。
确保在您的集群中启用 GKE Monitoring and Logging;您的技术支持工程师将需要这些日志和指标来对您的系统进行故障排除。
如果未启用 GKE Monitoring and Logging,请考虑启用新的仅限 beta 系统的日志功能,仅采集对故障排除至关重要的日志。
将一切整合在一起
容器化的应用程序可移植并且易于部署和扩展。凭借其广泛的集群管理功能,GKE 让运行您的工作负载变得更加简便、无忧。您最了解您的应用程序,但遵循这些建议,您能够显著提升您的集群的可用性和弹性。是否有更多的想法或者建议?让我们知道!请参阅本系列文章的第二部分,我们将详解如何应对生产集群中出现的问题。
评论