中小企业和初创公司如何在 Kubernetes 上扩展的最佳实践(三):可靠性的挑战与提升

本文是 Kubernetes 最佳实践系列文章的一篇,该系列文章共 6 篇,针对中小型企业和初创公司在 Kubernetes 上的最佳实践的总结。在本系列文章的第 1 篇中,你将了解到在 Kubernetes 上采用和扩展所面临的挑战,以及“开发人员生产力”的最佳实践。在第 2 篇中,我们介绍了“可观察性”的最佳实践,涵盖了在 Kubernetes 环境中进行监控、日志记录和跟踪的重要性。

在本篇(第 3 篇)中,我们重点讨论可靠性。对于中小型企业 (SMB) 来说,可靠性的意义和范围可能与大型企业不同。中小企业通常资源有限,部署规模较小,这在确保其 Kubernetes 集群的可靠性方面带来了独特的挑战。我们首先概述了这些挑战,然后提供了一组清单和最佳实践,帮助中小企业确保其 Kubernetes 环境的可靠性。我们主要总结的是中小企业规模的集群的经验,它们的集群通常由少于 500 个节点组成。

可靠性挑战

可靠性是指应用程序在各种情况下按预期工作的能力。当事情未按预期工作时,问题可能以多种形式表现出来,例如:

  • 功能问题:应用程序无法正常运行。常见问题包括:
    • Pod 崩溃循环:由于应用程序崩溃或配置问题,Pod 不断重启。
    • 负载平衡器/入口配置不正确:配置错误的负载平衡器或入口控制器会阻止流量到达应用程序。
    • 证书未续订:过期的 SSL/TLS 证书会导致连接失败。
    • 资源限制或请求不正确:资源分配不当可能导致 Pod 被驱逐或资源匮乏。
    • 依赖项缺失或配置错误:应用程序的依赖项(如数据库、消息队列等)配置不完整或不正确,可能阻止应用程序正常运行。

  • 延迟问题:虽然应用程序在大多数情况下运行良好,但某些请求可能会遇到响应缓慢的情况。延迟问题可能由以下因素引起:
    • 网络拥塞:网络带宽不足或存在网络瓶颈会导致延迟增加。
    • 计算资源限制:应用程序的 CPU 或内存分配不足可能会导致处理时间变慢。
    • 数据库性能:数据库查询速度慢或数据库资源不足会导致数据检索延迟。
    • 外部依赖关系:应用程序依赖的外部服务或 API 发生延迟,可能会影响整体响应时间。

为了应对这些挑战,遵循 Kubernetes 和云原生计算中的成熟实践,并结合应用程序的特定需求和特点至关重要。

可靠性最佳实践

在 Kubernetes 环境中实现高效可靠性需要采用结构化方法并遵循最佳实践。以下是一些关键建议。

一、正确调整节点大小

  • 了解你的应用程序需求

了解应用程序的具体需求至关重要。这包括确定你的应用程序是计算密集型、内存密集型,还是 I/O 密集型。确定应用程序类型后,准确评估所需的 CPU、RAM 和存储,以确保节点既不会过度配置,也不会配置不足。

常见的挑战包括资源利用不足,比如计算密集型应用未充分利用 CPU 功能但最大化内存,或者在利用率为 60% 的节点上无法为新 pod 分配足够资源。此外,节点间的资源利用率不均衡也会影响效率,比如某些节点 CPU 占用高而内存利用低,或反之亦然。

选择正确的节点大小和类型选择与应用程序资源需求匹配的节点大小非常重要。过大的节点会导致资源浪费和额外成本,而过小的节点可能影响性能。较小的节点有助于更精细地进行扩展和资源分配,从而灵活适应不断变化的需求。然而,在某些情况下(如运行大型单片应用或高内存要求的工作负载),较大的节点可能更有优势,提供更高性能并减少碎片化和节点间通信开销。

  • 节点类型的选择应根据应用程序需求量身定制

共享节点适用于开发/暂存环境;专用节点适合资源密集型或低延迟需求的应用程序;计算优化节点适合 CPU 密集型任务;内存优化节点则适用于消耗大量内存的应用。

  • 使用节点池并利用调度功能

节点池允许将服务于类似类型应用或工作负载的节点进行分组,从而优化资源管理和分配效率。通过将具有相似资源需求的应用程序分组到同一个节点池中,你可以提高性能和资源利用率,便于更轻松地管理和扩展应用。

Kubernetes 的调度功能(如 pod 亲和性和反亲和性)有助于提高资源利用效率。Pod 亲和性可以将相关 pod 彼此靠近放置,以减少延迟,而反亲和性则通过避免在同一节点上放置过多相似资源需求的 pod,防止资源争用。

二、正确调整 Pod 的大小

在 Kubernetes 集群中部署资源时,指明要为这些资源分配多少 CPU 和内存 (RAM) 是非常重要的。通过描述这些资源约束,你可以帮助 kube-scheduler 控制平面组件在将 Pod 分配给工作节点时做出更好的调度决策。一旦你的 Pod 被分配到工作节点,kubelet 组件将确保执行 Pod 容器上指定的请求和限制。

对于 CPU 和 RAM 请求,容器运行时将保证指定的资源值。因此,你需要确保最大的工作节点池包含能够容纳这些 CPU 或 RAM 请求的工作节点,否则可能会遇到无法在任何工作节点上调度 Pod,进而导致应用程序停机的问题。此外,由于请求值会保证这些资源被预留,因此重要的是不要过度请求资源,避免为不需要或可以用于其他工作负载的额外资源付费。

接下来是 CPU 和内存 (RAM) 限制。Kubernetes 将强制执行这些限制,防止部署的资源超出给定的资源配额。需要注意的是,CPU 和 RAM 限制的行为有所不同。当达到 CPU 限制时,你的 Pod 容器将受到限制,这可能会导致应用程序性能下降,特别是当它们在这些 CPU 限制下长时间运行时。如果出现这种情况,这可能是重新评估 Pod 容器限制并为工作负载提供足够 CPU 的好时机。即便如此,尽管性能可能下降,容器不会因为 CPU 是可压缩资源而被终止或驱逐。你的应用程序将保持可访问性,但性能可能会有所变化。设置限制时要谨慎,如果没有严格的需求,建议避免设置 CPU 限制,这样进程可以使用节点上剩余的任何额外 CPU 资源。如果设置了 CPU 限制,即便节点上有空闲的 CPU 资源,应用程序也可能会受限。一般情况下,我们建议完全忽略 CPU 限制,除非有特殊需求。

另一方面,当 Pod 内存消耗达到限制时,如果容器分配的内存超出了允许的范围,该容器将成为被终止的候选者。此时,你将在 Pod 状态中看到 OOM(内存不足)的状态。这种行为很重要,因为被终止的 Pod 可能会导致应用程序停机。即便 Pod 是由 ReplicaSet 或 Deployment 控制的,客户可能仍会受到 OOM 终止事件的影响,直到新 Pod 被调度并恢复运行。

三、正确调整 Pod 的大小

在 Kubernetes 集群中部署资源时,重要的是要明确为这些资源分配多少 CPU 和内存 (RAM)。通过定义这些资源约束,你可以帮助 kube-scheduler 控制平面组件在将 Pod 分配到工作节点时做出更好的调度决策。一旦 Pod 被分配到工作节点,kubelet 组件将确保容器中的 Pod 按照指定的请求和限制运行。

对于 CPU 和 RAM 请求,容器运行时将保证指定的资源值。因此,你需要确保最大的工作节点池包含能够满足这些 CPU 或 RAM 请求的工作节点,否则可能会遇到无法为 Pod 分配节点,导致应用程序停机的情况。此外,由于请求值保证了资源的预留,因此重要的是不要过度请求资源,以避免为你不需要或可以用于其他工作负载的资源付费。

接下来是 CPU 和内存 (RAM) 限制。Kubernetes 会强制执行这些限制,防止部署的资源超出给定的资源量。需要注意的是,CPU 和 RAM 限制的行为有所不同。当达到 CPU 限制时,你的 Pod 容器将受到限制。这种限制可能会导致应用程序性能下降,特别是如果它们在这些 CPU 限制下长时间运行。如果出现此类问题,这是重新审视 Pod 容器限制并为工作负载提供足够 CPU 的好机会。不过,即使性能下降,容器不会因为 CPU 是一种可压缩资源而被终止或驱逐。你的应用程序仍然可以保持可访问性,但性能可能会发生变化。在设置限制时要小心。如果省略 CPU 限制,进程可以使用节点上剩余的任何额外 CPU 资源。而设置 CPU 限制后,即便节点有空闲的 CPU 资源,应用程序也可能会受到限制。作为一般经验法则,我们建议你完全忽略 CPU 限制,除非你有严格的需求需要设置这些限制。

另一方面,当 Pod 的内存消耗达到限制时,如果容器分配的内存超过允许的值,该容器将成为被终止的候选者。当发生终止时,你会在 Pod 状态中看到 OOM(内存不足)的状态。这种行为很重要,因为被终止的 Pod 可能会导致应用程序停机。即使 Pod 是由 ReplicaSet 或 Deployment 控制,客户可能会在新 Pod 被调度并恢复运行之前受到 OOM 终止事件的影响。

四、为 Pod 分配服务质量

Kubernetes 提供三种服务质量 (QoS) 类别。当节点上没有足够的资源时,需要决定驱逐哪个 Pod,Kubernetes 会使用这些 QoS 类别。分配给 Pod 的 QoS 类别将决定驱逐的优先级。创建 Pod 时,有三种 QoS 类别可供选择:BestEffort、Burstable 和 Guaranteed。在做出驱逐决定时,Kubernetes 将优先终止具有 BestEffort QoS 的 Pod,然后是 Burstable,最后是 Guaranteed。以下是决定 Pod 属于哪个 QoS 类别的标准:

  • Burstable
    • Pod 不满足 QoS 类 Guaranteed 的标准。
    • Pod 中至少有一个容器有内存或 CPU 请求或限制。

  • Guaranteed
    • Pod 中每个容器都必须设置内存限制和内存请求。
    • 对于每个容器,内存限制必须等于内存请求。
    • Pod 中每个容器都必须设置 CPU 限制和 CPU 请求。
    • 对于每个容器,CPU 限制必须等于 CPU 请求。

  • BestEffort
    • Pod 不符合 Burstable 或 Guaranteed 的标准。

因此,根据应用程序的需求,正确设置 CPU 请求和限制以使 Pod 归入适当的 QoS 类别非常重要。例如,对于关键应用程序,你希望它们具有 Guaranteed 的 QoS,这样在 kube-scheduler 调度时会优先处理,并且在节点资源耗尽时它们是最后一个被驱逐的。

要优化 Kubernetes 中的 CPU 和内存分配,你有几种选择:

  1. 使用 Vertical Pod Autoscaler (VPA) 和 Metrics Server:Metrics Server 收集实时资源使用数据,VPA 进行分析,并根据历史使用模式提供 CPU 和内存限制的建议。
  2. 利用 Fairwinds Goldilocks:这是一个建立在 VPA 之上的工具,提供用户友好的资源建议界面。
  3. 使用 Robusta Kubernetes 资源推荐器 (KRR):这是一种轻量级方法,利用 Prometheus 和 cAdvisor 生成资源推荐。它不需要任何代理,你只需在本地机器上运行 CLI 工具即可立即获得结果。

这些工具中的每一个都提供了一种独特的资源优化方法,让您可以选择最适合您的集群需求和现有基础设施的方法。

通过这些工具,你可以选择最适合你的集群需求和现有基础设施的方法来优化资源分配。

五、使用探测器进行健康检查

Kubernetes 容器探测器是确保应用程序可靠性的关键机制。在接下来的部分中,我们将详细讨论不同类型的探测器、它们的使用时机、具体使用方法以及它们在系统可靠性中的关键作用。首先,探测器的定义是用于对应用程序进行全面的检查。你可以使用 HTTP 协议向应用程序发出请求、执行 shell 命令、尝试在指定端口上打开到容器的 TCP 套接字,或者执行 gRPC 健康检查。这些机制中的任何一种都可以用于与应用程序交互并确定其健康状况。接下来,我们将回顾 Kubernetes 探测器的三种不同用例。

第一个探测器是启动探测器,它在应用程序启动缓慢时非常有用。启动探测器用于检测应用程序何时完全启动,并阻止活跃探测器和就绪探测器在此之前运行。你可以在容器的规范中定义启动探测器,其参数通常比就绪探测器或活跃探测器更宽松,且具有更高的故障阈值和延迟,因为启动探测器的主要作用是等待应用程序的启动,而后者则需要快速响应问题。

第二个探测器是就绪探测器。它指示容器是否已准备好接收并处理流量。通常,应用程序在初始化时会执行一些任务,比如从外部保管库中提取机密信息、加载缓存、与其他服务交互等。这些任务完成之前,应用程序不应接收流量。因此,你不希望在这些 pod/容器完全准备好之前向它们发送流量。如果这些 pod 是 Kubernetes 服务的目标,并且它们报告未就绪状态,则它们不会接收流量。默认情况下,如果在支持 Kubernetes 服务的 pod 容器定义中省略了就绪探测器,Kubernetes 会默认将容器视为已就绪状态,并立即考虑让它们接收流量。这是一种需要避免的情况,因为它可能导致系统在容器完成初始化之前短时间内出现错误。

我们将介绍的最后一个探测器是 Liveness 探测器。一旦你的应用程序顺利运行,我们仍然希望确保它保持这种状态。如果检测到任何无法恢复、需要重新启动 Pod 的问题,你可以使用 Liveness 探测器来实现这一点。此探测器会定期检查应用程序的运行状况,如果检查失败,它将终止并重新启动 Pod。Liveness 探测器的典型用例是处理可通过重新启动修复的暂时性问题(例如内存损坏、内存泄漏、死锁),直到找到问题的根本原因。你希望确保就绪探测器(Readiness Probe)首先失败,将 Pod 置于 NotReady 状态,停止流量流向该 Pod,同时保留其恢复为 Ready 状态的可能性。如果问题持续很长时间,且无法恢复,此时 Liveness 探测器失败并触发 Pod 重启是合理的选择。

最后,你需要注意,探测器不应依赖外部服务。在集群中,最不想看到的就是级联故障,这可能导致许多应用程序的探测器失败,进而导致系统无响应或大量 Pod 重新启动。因此,我们建议探测器的逻辑不要与外部组件、API 或第三方服务交互,因为一次故障可能会影响多个应用程序。

六、部署策略

在本节中,我们将介绍两种部署策略,你可以在持续交付管道中引入这些策略,以提高系统在向生产环境引入更改时的可靠性。我们还假设你已经使用指标正确监控了应用程序,并能够跟踪四个关键指标:延迟、流量、错误和饱和度。

我们介绍的第一个部署策略是 金丝雀部署。金丝雀部署会逐步推出新版本,先只更新一小部分副本,并监控生产流量的行为。如果新版本运行正常,你可以逐步增加使用该版本的副本数量。如果出现问题,你可以迅速回滚这些副本,并从日志和指标中收集有关故障域的有用信息。在金丝雀部署中,如果检测到问题,你可以放心,只有一小部分生产流量受到影响。在 Kubernetes 的上下文中,可以考虑以下设置。

如你所见,通过增加或减少金丝雀部署的副本数量,可以轻松调整金丝雀部署在整个集群中的比例。根据应用程序的需求以及通过该部署计划收集的四个关键指标(黄金信号),你可以决定是回滚到旧版本还是继续推进新版本的全面发布。

另一种可以通过 Kubernetes 轻松实现的部署策略是 蓝绿部署。使用这种策略,你将通过两个独立的部署来引入新版本,一个部署包含新版本(绿色),另一个部署包含当前版本(蓝色)。

如你所见,只需更改 Kubernetes 服务选择器字段以匹配新版本 v2 的标签,就可以将整个流量重定向到绿色部署 v2。如果你收集的四个黄金信号指标表明测试成功,你可以放弃蓝色部署(旧版本)并继续使用绿色部署(新版本)。如果测试不成功,则可以轻松回滚到蓝色部署,拆除绿色部署,并通过收集的指标和日志来分析问题所在。

七、使用 Pod 调度(亲和性规则和 Pod 拓扑)

在 DigitalOcean Kubernetes 中,你的集群由一个或多个运行应用程序的工作节点(Droplet)组成。这意味着,如果你的应用程序 pod 没有分布在多个工作节点上,它们可能会受到单个工作节点故障的影响。由于 DOKS 平台仅支持最近的三个次要版本,最终你需要将集群升级到最后支持的 DOKS 版本,这将触发工作节点的重新部署。如果应用程序没有分布在多个节点上,这可能会导致一些可靠性问题。同样,当手动回收工作节点或使用节点自动扩展时,也可能出现类似的传播问题。你可以使用 Pod 拓扑传播约束 来解决此问题。这个 Kubernetes 功能非常强大,允许你控制 pod 在集群节点之间的分布方式。例如,在一个有 3 个工作节点的 DOKS 集群中,如果运行 4 个副本的 App1 部署,你希望避免以下情况:

在上面的示例中,所有 App1 部署 pod 都调度在同一个工作节点上。如果由于上述任何原因移除了工作节点 1,应用程序将经历一段时间的停机。现在让我们将 topologySpreadConstraints 引入到我们的部署资源中。

使用此新配置,你可以在工作节点终止后继续运行应用程序 App1,而无需停机。上面的示例只是拓扑扩展约束功能的一部分,若想了解更多高级配置,可以访问 Kubernetes 文档中的相关内容。

八、规划平稳升级

在规划 DigitalOcean Kubernetes (DOKS) 的平稳升级时,彻底测试应用程序以确保它与快速发展的 Kubernetes API 保持兼容至关重要。了解 DOKS 如何管理升级的细节也很重要。DigitalOcean 默认执行激增升级,这意味着它会在池中一次创建最多十个额外的工作节点,然后回收现有节点。虽然这种方法旨在最大限度地减少停机时间,但如果管理不当,仍然可能中断工作负载。

为了减轻集群升级期间的潜在中断(这是节点关闭的常见原因),为 pod 实施正常关闭程序非常重要。对于需要更多时间从节点中排出的有状态工作负载,DOKS 为节点排空提供长达 30 分钟的宽限期。此外,建议使用 pod 中断预算来保护这些有状态工作负载。在节点替换过程中,中断预算可以帮助确保最少数量的 pod 保持运行,从而保持服务的完整性和可用性。

九、在镜像中使用唯一标签以避免容器中存在不同版本

在创建新 pod 时,容器规范中的默认镜像拉取策略确保只有在节点上没有该镜像时才会拉取它。如果你对应用程序的不同版本使用相同的标签(如“latest”),可能会导致不一致的情况。

例如:你将代码构建到镜像中,使用“latest”标签推送到容器注册表,并在 DOKS 集群中用该标签创建部署。首次部署时,所有节点都会拉取该镜像。然而,如果你更新代码、构建新的容器镜像,并用相同的“latest”标签推送它,触发新的部署,已经运行旧版本“latest”镜像的节点上的 pod 将继续运行旧代码,而你可能不会注意到此问题。而在新节点上的任何 pod 都会拉取新的“latest”镜像,从而导致应用程序版本不一致。

如果你在推送镜像时未明确指定标签,问题会更加隐蔽。在这种情况下,镜像将被隐式标记为“latest”。即使没有执行新的部署,任何发生的 pod 替换都会拉取更新后的“latest”镜像并运行最新版本,导致不一致。

为避免这种不一致,确保你的 pod 始终运行正确版本的应用程序,强烈建议使用语义版本控制标签或提交 SHA 唯一标识每个构建。这种方法有助于区分不同的二进制文件和配置,避免冲突,并确保在所有 pod 中一致部署正确的应用版本。

总结:可靠的最佳实践

确保 DigitalOcean Kubernetes 上应用程序的可靠性涉及在各个方面实施最佳实践,如上所述。这包括正确调整节点和 pod 的大小、为 pod 定义适当的服务质量 (QoS)、利用探测器进行健康监控、采用合适的部署策略、优化 pod 调度、增强升级弹性以及使用唯一的容器镜像标签。通过遵循本指南中的清单和建议,你可以构建可靠且具有弹性的应用程序,能够在故障中恢复并保持 Kubernetes 集群中的最佳性能。

一些供应商专门通过主动识别和解决问题、改进故障排除流程来增强 Kubernetes 的可靠性。著名的供应商包括 Komodor、Robusta 和 Fairwinds。

接下来几篇内容

随着我们继续探索 ISV 采用 Kubernetes 的历程,本系列博客将深入探讨部署的弹性、效率和安全性。

  • 可扩展性(第 4 篇):探索如何管理零停机部署、就绪/活跃度探测、应用程序扩展、DNS 和 CNI,以在不同负载下保持最佳性能。
  • 灾难准备(第 5 篇):讨论制定可靠的灾难恢复计划的重要性,包括备份策略、实践和定期演练,以确保业务连续性。
  • 安全性(第 6 篇):深入研究如何保护你的 Kubernetes 环境,涵盖网络策略、访问控制和应用程序工作负载的保护最佳实践。

这些主题对于应对 Kubernetes 的复杂性、增强基础设施的弹性、可扩展性和安全性至关重要。请继续关注我们的博客,获取更多增强你 Kubernetes 之旅的见解。

准备好踏上变革之旅,并从 DigitalOcean 上的 Kubernetes 中获得最大收益了吗?欢迎免费注册体验 DigitalOcean Kubernetes。如需要从其他云服务迁移至 DigitalOcean 云服务或了解相关的云主机、云存储、GPU 云服务等业务,欢迎联系DigitalOcean 中国区独家战略合作伙伴卓普云

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值