8年Kubenetes生产环境的经验总结

在我加入 Urb-it 之前,我们早早地决定将 Kubernetes 作为我们云原生战略的基石。做出这个选择的考虑是我们预期的快速扩展,再加上对容器编排能力的利用,以获得更动态、更具弹性和更高效的应用环境。而且对于我们的微服务架构来说,Kubernetes 很适合。

译自 Learnings From Our 8 Years Of Kubernetes In Production — Two Major Cluster Crashes, Ditching Self-Managed, Cutting Cluster Costs, Tooling, And More。作者 Anders Jönsson 。

早期决策

这个决策是早早地做出的,当然,这应该受到质疑,因为它代表了一个重要的依赖和大量的知识负担,对于一家初创公司(或任何公司)来说都是如此。此外,我们在那个阶段甚至是否面临 Kubernetes 所解决的问题?有人可能会争论说,我们最初可以选择一个庞大的单体架构,并依靠它直到扩展和其他问题变得痛苦,然后再转向 Kubernetes(或其他东西)。此外,Kubernetes 当时仍处于早期开发阶段。但这个问题我们以后再深入讨论。

在生产中的 8 年

运行 Kubernetes 生产环境已经超过八年(每个环境都有一个单独的集群),我们做出了一系列好坏参半的决定。有些错误仅仅是“otur när vi tänkte”(在我们的决策中运气不佳)的结果,而另一些则源于我们并没有完全(甚至根本没有)理解底层技术本身。Kubernetes 强大,但也具有复杂的层次。

我们之前毫无在规模上运行它的经验,就全力以赴。

从 AWS 的自管理迁移到 Azure 的托管(AKS)

在最初的几年里,我们在 AWS 上运行了一个自管理的集群。如果我没记错的话,最初我们没有选择使用 Azure Kubernetes 服务(AKS)、Google Kubernetes 引擎(GKE)、Amazon 弹性 Kubernetes 服务(EKS),因为它们还没有提供官方的托管解决方案。在亚马逊网络服务(AWS)上自行托管的集群中,我们经历了 Urb-it 历史上第一个也是最可怕的集群崩溃,但稍后再详述。

由于我们是一个小团队,跟上我们所需的所有新功能是具有挑战性的。与此同时,管理一个自托管的集群需要持续的关注和维护,这增加了我们的工作量。

当托管解决方案普遍可用时,我们花了一些时间评估了 AKS、GKE 和 EKS。它们都比我们自己管理要好多次,我们可以很容易地看到通过迁移获得的快速投资回报。

我们当时的平台是 50% .Net 和 50% Python,并且我们已经在使用 Azure Service Bus、Azure SQL Server 和其他 Azure 服务。因此,将我们的集群迁移到 Azure 不仅可以更轻松地以一体化的方式使用它们,而且通过利用 Azure 骨干网络基础设施,可以避免与外部网络和虚拟网络之间离开/进入所带来的成本,这是我们在混合 AWS 和 Azure 设置之间存在的。此外,我们的许多工程师都熟悉 Azure 及其生态系统。

我们还应该提到,对于我们在 AKS 上的初始设置,我们不必为控制平面节点(主节点)支付费用 —— 这是一个额外的奖励(节省节点上的费用)。

我们在 2018 年冬季进行了迁移,尽管在这些年里我们遇到了一些 AKS 的问题,但我们从未后悔过这次迁移。

集群崩溃 #1

在 AWS 的自管理期间,我们经历了一次严重的集群崩溃,导致我们的大部分系统和产品崩溃。根 CA 证书、etcd 证书和 API 服务器证书过期,导致集群停止工作,阻止我们对其进行管理。当时,在 kube-aws 中解决此问题的支持有限。我们请来了一位专家,但最终我们不得不从头开始重建整个集群。

我们以为所有的值和 Helm Chart 都在每个 git 存储库中,但是,令人惊讶的是,这并不是所有服务的情况。除此之外,创建集群的所有配置都没有存储。这变成了一个与时间赛跑的过程,要重新设置集群并填充所有我们拥有的服务和产品。其中一些需要重新创建 Helm Chart 来创建缺失的配置。有时候会出现像 Dev1 对 Dev2 说:“你还记得这个服务应该有多少 CPU 或 RAM,或者应该有什么网络和端口访问权限吗?”更不用说那些随风而逝的秘密了。

我们花了几天时间才将其重新启动。毫不夸张地说,这不是我们最自豪的时刻。

集群崩溃 #2

现在你可能会说:第二次崩溃不可能是由于证书问题,因为你们必须从第一次崩溃中吸取了教训,对吗?是的和不是。在从崩溃 #1 重新创建集群时,不幸的是,我们使用的 kube-aws 的特定版本存在问题。当它创建新的集群时,它没有将 etcd 证书的到期日期设置为提供的到期日期;它默认为一年。所以在第一次集群崩溃一年后的正好一年后,证书到期了,我们又经历了一次集群崩溃。然而,这次更容易恢复;我们不必重建一切。但那仍然是一个糟糕的周末。

附注 1:其他公司也受到了这个 bug 的影响,就像我们一样,这并没有帮助我们的客户……

附注 2:我们的计划是在一年后更新所有证书,但为了给自己留有余地,我们将到期时间设置为两年(如果我记得正确的话)。所以我们有更新证书的计划,但 bug 先于我们发现了它。

自 2018 年以来,我们再也没有遇到过任何集群崩溃……会不会被诅咒?是的。

收获

  • Kubernetes 很复杂

你需要有兴趣并且愿意从事 Kubernetes 基础设施和运维方面工作的工程师。在我们的情况下,我们需要一些工程师在需要时把时间专注于 Kubernetes。要轮换和分割整个团队的工作是不可能的;这项技术太复杂了,无法每两周“跳进跳出”。当然,每个人都需要知道如何使用它(部署、调试等)——但要在更具挑战性的方面表现出色,需要专注的时间。此外,拥有一个有远见并且有关集群发展战略的领导者是很重要的。

  • Kubernetes 证书

经历了两次由证书过期引起的集群崩溃,了解内部 Kubernetes 证书及其到期日期的细节至关重要。

  • 保持 Kubernetes & Helm 的更新

当你落后时,这变得昂贵且乏味。我们总是等待几个月,然后才跳到最新版本上,以确保其他人会先面对任何新版本的问题。但即使保持更新,由于 Kubernetes 和 Helm 的新版本(Kubernetes API 从 alfa 到 beta,beta 到 1.0 等),我们也面临许多耗时的配置文件和 Chart 重写。我知道 Simon 和 Martin 很喜欢所有的 Ingress 变化。

  • 集中式 Helm Chart

在 Helm 图表方面,我们厌倦了每个版本更改时更新所有 70 多个图表,因此我们采用了更通用的“一图表统治一切”的方法。集中式 Helm 图表方法有许多利弊,但最终,这更适合我们的需求。

  • 灾难恢复计划

我无法再强调了:确保有办法在需要时重新创建集群。是的,你可以在 UI 中点击几下来创建新集群,但这种方法永远不会在规模上或及时地起作用。有不同的处理方法,从简单的 shell 脚本到更高级的方法,比如使用 Terraform(或类似)。Crossplane 也可以用于管理基础设施即代码(IaC)等。对于我们来说,由于团队带宽有限,我们选择存储和使用 shell 脚本。无论你选择的方法是什么,请确保定期测试流程,以确保在需要时可以重新创建集群。

  • 备份秘钥

要有备份和存储秘钥的策略。如果你的集群消失了,所有的秘钥都会丢失。相信我,我们亲身经历过这一点;当你有多个不同的微服务和外部依赖时,要重新搞定一切需要花费很多时间。

  • 厂商无关 VS “全情投入”

起初,迁移到 AKS 后,我们试图保持我们的集群与厂商无关,意味着我们会继续使用其他服务,比如容器注册表、身份验证、密钥库等。这样做的想法是,我们可以很容易地转移到另一个托管解决方案。虽然保持厂商无关是一个很好的想法,但对我们来说,它带来了很高的机会成本。过了一段时间,我们决定全情投入到与 AKS 相关的 Azure 产品中,比如容器注册表、安全扫描、身份验证等。对我们来说,这带来了改善的开发者体验、简化的安全性(使用 Azure Entra Id 的集中式访问管理)等,这导致了更快的上市时间和降低的成本(量的优惠)。

  • 客户资源定义

是的,我们全情投入到 Azure 产品中,但我们的指导之星是尽可能少地使用自定义资源定义,而是使用内置的 Kubernetes 资源。然而,我们有一些例外,比如 Traefik,因为 Ingress API 并不能满足我们所有的需求。

  • 安全性

见下文。

  • 可观测性

见下文。

  • 已知高峰期的预先缩放

即使使用了自动扩展器,有时我们的扩展速度仍然太慢。通过使用交通数据和常识(我们是一家物流公司,在假期有高峰),我们在高峰到来的前一天手动扩展了集群(ReplicaSet),然后在高峰后的第二天逐渐缩减(缓慢处理可能发生的第二波高峰)。

  • 集群内的无人机

我们将 Drone 构建系统保留在舞台集群中;它有一些好处,但也有一些缺点。由于它在同一个集群中,因此易于扩展和使用。然而,当同时构建过多时,它几乎消耗了所有资源,导致 Kubernetes 匆忙地启动新节点。最好的解决方案可能是将其作为纯 SaaS 解决方案,不必担心托管和维护产品本身。

  • 选择正确的节点类型

这非常具体于上下文,但根据节点类型,AKS 会预留大约 ~10-30% 的可用内存(用于内部 AKS 服务)。因此,对我们来说,使用较少但更大的节点类型是有益的。此外,由于我们许多服务都在运行 .Net,我们需要选择具有高效且大小合适的 IO 的节点类型。(.Net 经常会写入磁盘进行 JIT 和日志记录,如果这需要网络访问,那么速度就会变慢。我们还确保节点磁盘/缓存的大小至少与总配置的节点磁盘大小相同,以再次防止需要网络跳转)。

  • 预留实例

你可以辩论说,这种方法在某种程度上违背了云的灵活性,但对我们来说,预留关键实例一年或两年带来了巨大的成本节省。在许多情况下,与“按需付费”的方法相比,我们可以节省 50–60%。是的,这对团队来说是足够多的“蛋糕”。

  • k9s

https://k9scli.io/ 是一个很棒的工具,适用于任何想要比纯 kubectl 更高一级抽象的人。

可观测性

监控

确保随着时间的推移跟踪内存、CPU 等的使用情况,这样你就可以观察集群的性能,并确定新功能是否改善或恶化了其性能。有了这个,更容易找到并设置不同 pod 的“正确”限制(找到正确的平衡很重要,因为如果 pod 用尽了内存,它就会被杀掉)。

警报

我们不断完善我们的警报系统,但最终,我们将所有警报都指向了我们的 Slack 频道。这种方法使得在集群出现预期之外的功能或出现任何意外问题时方便接收通知。

日志记录

将所有日志集中到一个地方,以及一个强大的跟踪 ID 策略(例如 OpenTelemetry 或类似的)对于任何微服务架构都至关重要。我们花了 2-3 年的时间才做到这一点。如果我们早些实施它,就会节省大量时间。

安全

Kubernetes 安全性

Kubernetes 中的安全性是一个广泛的话题,我强烈建议彻底研究它,以了解所有的细微差别(例如,查看 NSA、CISA 发布的 Kubernetes 硬化指南)。以下是我们的一些关键观点,但请注意,这绝不是挑战的完整画面。

访问控制

简而言之,Kubernetes 默认情况下并不过于严格。因此,我们花了大量时间 tightening 访问,为 pod 和容器实施了最小特权原则。另外,由于特定的漏洞,可能会出现未经特权处理的攻击者,潜在地将其权限升级为 root,绕过 Linux 命名空间限制,并在某些情况下甚至逃脱容器以获取主机节点上的 root 访问权限。至少可以说这不是好事。

你应该设置只读根文件系统,禁用服务账户令牌自动挂载,禁用特权升级,丢弃所有不必要的能力等。在我们具体的设置中,我们使用 Azure 策略和 Gatekeeper 来确保我们没有部署不安全的容器。

在我们的 AKS Kubernetes 设置中,我们利用基于角色的访问控制 (RBAC) 的强大性来进一步增强安全性和访问管理。

容器漏洞

有许多优秀的工具可以扫描和验证容器以及 Kubernetes 的其他部分。我们使用 Azure Defender 和 Azure Defender for Containers 来满足我们的一些需求。

**注意:**与其陷入“分析瘫痪”中寻找完美工具,即那些拥有所有“花里胡哨”功能的工具,不如选择一样东西,让学习开始吧。

多年来的设置

部署

与许多其他人一样,我们使用 Helm 来管理和简化在 Kubernetes 上部署和打包我们的应用程序。由于我们很早就开始使用 Helm,并且最初混合使用了 .Net/Go/Java/Python/PHP,我们重写了 Helm charts 的次数比我敢记的还要多。

可观测性

我们最开始使用 Loggly 结合 FluentD 进行集中日志记录,但几年后,我们转向了 Elastic 和 Kibana(ELK 堆栈)。对我们来说,使用 Elastic 和 Kibana 更容易,因为它们被广泛使用,而且在我们的设置中,它们更便宜。

容器注册表

我们最初使用的是 Quay,这是一个很好的产品。但随着迁移到 Azure,使用 Azure 容器注册表变得更自然,因为它集成了,因此对我们来说是更“原生”的解决方案。(然后我们还将我们的容器置于 Azure 安全顾问之下)。

流水线

从一开始,我们就使用 Drone 来构建我们的容器。当我们刚开始时,支持容器和 Docker 的 CI 系统并不多,也没有提供代码配置。多年来,Drone 为我们服务得很好。当 Harness 收购它后,事情变得有点混乱,但在我们屈服并转向高级版本后,我们拥有了所有需要的功能。

游戏规则改变者

在过去的几年里,Kubernetes 对我们来说是一个改变游戏规则的工具。它解锁了能力,使我们能够更有效地扩展(处理波动的流量),优化基础架构成本,改善开发人员的体验,更容易测试新想法,因此大大缩短了新产品和服务的上市时间/赚钱时间。

我们开始使用 Kubernetes 有点太早,之前我们并没有真正遇到它可以解决的问题。但从长远来看,特别是在最近几年,它已被证明为我们提供了巨大的价值。

结束语

回顾了八年的经验,我们有很多故事可以分享,其中许多已经淡入了记忆。希望您喜欢阅读我们的设置、我们犯的错误以及我们在路上学到的经验。

感谢您的阅读。

特别感谢 MartinSimonNiklas 对本文提供的宝贵反馈。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注