云与基础设施
什么时候才真正需要 Kubernetes
Kubernetes 是一个用来构建平台的平台,即使控制平面是托管的,它的运维成本依然实实在在。对许多工作负载而言,ECS on Fargate、Cloud Run 或 Azure Container Apps 这类容器服务才是更好的工程决策。
· 阅读约 4 分钟 · Starone 工程团队
Kubernetes 把一个难题解决得很好:在一组机器上调度大量容器化工作负载,保持它们持续运行,并为围绕它们的一切提供统一的 API。它已经成了“容器跑在哪里”这个问题的默认答案,哪怕有些系统根本没有它所解决的那个问题。
我们的看法是,采用 Kubernetes 应当是一个有明确理由的决定。对于少量无状态服务,托管容器服务能达到同样的结果,而需要运维的面却小得多。当工作负载或组织需要只有 Kubernetes 才能提供的能力时,它才是正确的选择。
接手一个集群意味着什么
EKS、GKE 或 AKS 这类托管产品负责运行控制平面,但并没有免去其余的工作。一个生产集群是由许多组件拼装而成的,总得有人去选型、配置、升级和排障。
- 每年数次的版本升级,每次都伴随 API 弃用,还要在控制平面、节点和附加组件(add-on)之间遵守版本偏差(version skew)策略。
- 节点生命周期:操作系统补丁、节点池容量规划、使用 Cluster Autoscaler 或 Karpenter 自动扩缩容,以及在不丢流量的前提下排空节点。
- 网络:CNI 插件、IP 地址规划、Ingress 或 Gateway API 控制器、DNS 和网络策略。
- 安全:RBAC、Pod 安全标准、用于访问云资源的工作负载身份、准入策略、密钥集成和镜像来源验证。
- 一整套附加组件:证书管理、外部 DNS、指标与日志采集,每一个都有自己的发布周期。
成本主要在人
集群费用很少是账单上的大头。真正的大头是工程师的精力:既要让平台保持最新,又要诊断跨越多个层次的故障,例如一个 Pod 因为节点上的 conntrack 上限而无法解析 DNS,或者一次部署因为 Pod disruption budget 阻止了节点排空而卡住。
应用团队同样要付出代价。他们需要理解 requests 与 limits、探针、发布策略,以及一份 manifest 是如何变成运行中的 Pod 的。一个设置不当的 liveness 探针,会在高负载下重启一个原本健康的服务。缺少资源 requests,会让一个工作负载把邻居饿死。如果没有平台团队提供合理的默认值,这些知识就必须存在于每一个团队之中。
更简单的服务在哪些情况下就够了
如果系统只是少数几个无状态 HTTP 服务加上一些后台 worker,托管容器平台就能满足需求。Amazon ECS on Fargate 运行容器时无需管理节点,并且与 IAM、负载均衡器和 CloudWatch 直接集成。Google Cloud Run 能把请求驱动的容器缩容到零再拉起。Azure Container Apps 和 App Service 在 Azure 上扮演同样的角色。
这些服务提供滚动部署、自动扩缩容、健康检查、密钥注入和私有网络。它们不提供的是扩展定制能力:没有自定义控制器,对调度的控制有限,网络模型也更窄。对大多数业务应用来说,这一限制永远不会成为真正的约束。由于部署单元仍然是容器镜像,日后迁移到 Kubernetes 只是更换部署描述文件和流水线,而不是重写。
说明 Kubernetes 是合适工具的信号
以下情况中有好几条成立时,选择 Kubernetes 的理由就很充分。
- 数十个服务分属多个团队,一致的部署与策略模型值得为之配备一个专门的平台。
- 工作负载需要精细的调度控制:推理场景的 GPU 共享、节点亲和性、拓扑分布,或批处理作业的 bin-packing。
- 依赖 operator 和更广泛的生态,例如用 Argo CD 做 GitOps、用 KEDA 做事件驱动扩缩容,或用服务网格实现双向 TLS。
- 需要在多家云厂商、本地机房或边缘运行同一套平台。
- 软件本身就是分发给客户、由客户在自己的集群中运行的。
有状态工作负载需要单独决策
借助 StatefulSet 和成熟的 operator,在 Kubernetes 上运行数据库和 broker 是可行的;对于具备相应专业能力或有可移植性要求的团队,这是合理的选择。但它也把备份、故障切换、存储性能和升级风险都压在了运行集群的同一个团队身上。只要托管数据库或托管 Kafka 服务能满足需求,我们就优先选用,把集群留给无状态计算。
中间地带
这并不是一道非此即彼的选择题。GKE Autopilot 和 EKS Auto Mode 免去了节点管理,同时保留了 Kubernetes API,适合想要这个生态又不想自己负责数据平面的团队。它们的单位算力成本更高,也限制了部分底层配置,这通常是一笔公平的交换。
可移植性常被用来作为尽早采用 Kubernetes 的理由。这只说对了一部分。manifest 是可移植的,但身份、负载均衡、storage class 和托管数据服务在每个平台上都与厂商绑定。一个容器化的应用,只要配置放在环境中、业务逻辑里没有对厂商 SDK 的调用,其可移植性就足以让这个决定延后。
我们如何决定
我们从工作负载和团队出发:现在有多少个服务,两年内实际会有多少个;其中是否有服务需要更简单的平台所欠缺的调度特性;以及凌晨三点由谁来负责升级和故障。如果最后一个问题没有可信的答案,那么无论技术上多么有吸引力,更简单的平台才是负责任的选择。
相反的错误同样存在。一个团队的需求超出了简单平台的能力,于是在它周围自建调度器、服务发现和部署工具,结果只是拙劣地重造了一遍 Kubernetes。当各种变通方案开始越积越多时,采用标准平台比维护一套定制平台更省成本。
