也许你并不需要 Kubernetes
来源:插图由 freepik 创作,Nomad 标志由 HashiCorp 提供。
Kubernetes 是 container orchestration(容器编排)领域的 800 磅大猩猩。
它支撑着全球一些规模最大的部署,但也伴随着相应的代价。
尤其对于小团队来说,维护它可能非常耗时,而且学习曲线陡峭。对于我们在 trivago 的四人团队想要实现的目标而言,它带来的额外负担太大了。因此,我们开始寻找替代方案——最终爱上了 Nomad。
愿望清单
我们的团队运行着许多用于监控和性能分析的典型服务:用 Go 编写的指标 API 端点、Prometheus exporters、Logstash 或 Gollum 这样的日志解析器,以及 InfluxDB 或 Elasticsearch 这样的数据库。这些服务各自运行在独立的容器中。我们需要一个简单的系统来确保这些任务持续运行。
我们首先列出了一份对 container orchestration 的要求:
- 在多台机器上运行一组服务。
- 提供运行中服务的概览。
- 允许服务之间相互通信。
- 服务停止运行时自动重启。
- 能够由一个小团队进行管理。
除此之外,以下功能虽然有了更好,但并非严格要求:
- 根据机器的能力为其添加标签(例如,为 I/O 密集型服务标记出配备高速磁盘的机器)。
- 能够让这些服务独立于任何 orchestrator 运行(例如在开发环境中)。
- 提供一个共享配置和 secrets 的公共位置。
- 提供用于指标和日志记录的端点。
为什么 Kubernetes 不适合我们
在使用 Kubernetes 创建原型时,我们注意到,为了运行这些服务,我们开始不断加入越来越复杂的逻辑,而我们还在隐式地依赖这些逻辑。
例如,Kubernetes 允许通过 ConfigMaps 嵌入服务配置。尤其是在合并多个配置文件,或向一个 pod 添加更多服务时,很快就会变得相当混乱。Kubernetes——或者说 helm——还允许动态注入外部配置,以确保关注点分离。但这可能导致项目与 Kubernetes 之间形成紧密的、隐式的耦合。Helm 和 ConfigMaps 都是可选功能,所以你不必使用它们。你完全可以直接把配置复制到 Docker 镜像中。然而,人们很容易沿着这条路走下去,构建出一些不必要的抽象,而这些抽象日后可能反过来给你带来麻烦。
此外,Kubernetes 生态仍在快速发展。要跟上最佳实践和最新工具,需要投入相当多的时间和精力。Kubectl、minikube、kubeadm、helm、tiller、kops、oc——工具清单还在不断变长。并非所有工具都是开始使用 Kubernetes 所必需的,但很难知道究竟哪些是必需的,因此你至少得了解它们的存在。正因如此,学习曲线相当陡峭。
何时使用 Kubernetes
具体在 trivago,许多团队都在使用 Kubernetes,并且对它相当满意。不过,这些实例由 Google 或 Amazon 管理,而它们有能力承担这项工作。
Kubernetes 提供了一些令人惊叹的功能,让大规模的 container orchestration 更易于管理:
- 细粒度的 权限管理
- Custom controllers 允许将逻辑引入集群。这些其实就是与 Kubernetes API 交互的程序。
- Autoscaling!Kubernetes 可以根据需求扩展或缩减服务。它利用服务指标来完成这一点,无需人工干预。
问题在于,你是否真的需要所有这些功能。你不能指望这些抽象开箱即用;你必须弄清楚其底层究竟发生了什么。
尤其是对我们这个团队来说,大多数服务都运行在本地部署环境中(因为这与 trivago 的核心基础设施密切相关),我们不想承担运行自有 Kubernetes 集群的成本;我们想做的是交付服务。

不附带电池
Nomad 提供了 20% 的服务编排能力,却能让你完成 80% 的工作。它所做的全部事情就是管理部署:负责滚动发布,并在容器出错时重启它们,仅此而已。
Nomad 的核心理念就是做得更少:它不包含细粒度权限管理或高级网络策略,而这是有意为之。这些组件要么由第三方以企业服务的形式提供,要么根本不提供。
我认为 Nomad 在易用性和表现力之间取得了一个绝佳的平衡点。它适合小型且大多相互独立的服务。如果你需要更多控制权,就得自己构建,或者采用不同的方法。Nomad 只是一个 orchestrator。
Nomad 最棒的一点是容易替换。它几乎不存在厂商锁定,因为它提供的功能很容易集成到任何其他服务管理系统中。它只是在集群中的每台机器上以一个普通的单一二进制文件运行;就这么简单!
由松耦合组件构成的 Nomad 生态
Nomad 的真正实力在于其生态系统。它与其他完全可选的产品集成得非常好,例如 Consul(键值存储)或 Vault(用于 secrets handling(机密信息处理))。在 Nomad 文件中,你可以添加从这些服务获取数据的配置段:
template {
data = <<EOH
LOG_LEVEL="{{key \"service/geo-api/log-verbosity\"}}"
API_KEY="{{with secret \"secret/geo-api-key\"}}{{.Data.value}}{{end}}"
EOH
destination = "secrets/file.env"
env = true
}这段配置会从 Consul 读取 service/geo-api/log-verbosity 键,并将其作为 LOG_LEVEL 环境变量暴露在你的任务中。它还会将 Vault 中的 secret/geo-api-key 暴露为 API_KEY。简单,却很强大!
正因为简单,Nomad 也可以通过其 API 轻松扩展,与其他服务协作。例如,可以为任务添加标签,以实现服务发现。在 trivago,我们会为所有暴露指标的服务添加 trv-metrics 标签。这样,Prometheus 就能通过 Consul 找到这些服务,并定期抓取它们的 /metrics 端点来获取新数据。通过集成 Loki 等工具,也可以对日志执行同样的操作。
扩展性还有许多其他例子:
- 使用 webhook 触发 Jenkins 任务,并通过 Consul watches 在服务配置发生变化时重新部署 Nomad 任务。
- 使用 Ceph 为 Nomad 添加分布式文件系统。
- 使用 fabio 实现负载均衡。
所有这些都让我们得以有机地扩展基础设施,而不必在一开始就做出过多承诺。
郑重提醒
没有任何系统是完美的。我建议你现在不要在生产环境中使用任何花哨的新功能。当然会有缺陷和缺失的功能——但 Kubernetes 也一样。
与 Kubernetes 相比,Nomad 背后的发展势头要小得多。截至目前,Kubernetes 大约有 75,000 次提交和 2,000 名贡献者,而 Nomad 则约有 14,000 次提交和 300 名贡献者。Nomad 很难跟上 Kubernetes 的发展速度,但也许它并不需要这样做!它的范围要窄得多,而较小的社区也可能意味着,与 Kubernetes 相比,你的 pull request 更容易被接受。
总结
归根结底:不要仅仅因为所有人都在用 Kubernetes,就跟着使用它。仔细评估你的需求,确认哪个工具最适合你的工作。
如果你计划在大规模基础设施上部署一组同质服务,Kubernetes 可能是正确选择。只是要注意额外的复杂性和运维成本。使用 Google Kubernetes Engine 或 Amazon EKS 这样的托管 Kubernetes 环境,可以避免其中一部分成本。
如果你只是在寻找一个可靠、易于维护且可扩展的 orchestrator,为什么不试试 Nomad 呢?它能带你走多远,可能会让你感到惊讶。
如果 Kubernetes 是一辆汽车,那么 Nomad 就是一辆滑板车。有时你更喜欢前者,有时则更喜欢后者。两者都有存在的理由。
随机一篇博客