容器编排器生态:Swarm、Kubernetes、Nomad 是最有生命力三个产品

2022-11-28 16:46:27 浏览数 (1)

作者 | Jordan Webb

译者 | 平川

策划 | 褚杏娟

尽管复杂,Kubernetes 仍然是目前最流行的编排器,但 HashiCorp 在 Nomad 上的成功也表明,Kubernetes 的替代方案还有发展空间。

Docker 及其他容器引擎可以从许多方面简化服务端应用程序的部署,但许多应用程序不只包含一个容器。随着部署的应用程序和服务增加,管理一组容器的难度越来越大,一类名为容器编排器的工具由此发展了起来,截至目前最著名的是 Kubernetes,容器编排的历史以它为界也分成了前后两段。

在享受容器带来的便利的同时,我们也要做好一些权衡取舍。如果一个人严格遵守 Docker“每个服务都应有自己的容器”的理念,那么最终他将运行海量的容器。即使是一个访问数据库的简单 Web 界面也可能需要为数据库服务器和应用程序运行单独的容器,它可能还包括一个单独的 Web 服务器容器用于提供静态文件服务、一个单独的代理服务器容器用于终止 SSL/TLS 连接、一个键 / 值存储容器充当缓存,或者一个辅助应用程序容器用于处理后台作业及计划任务。

一位管理员如果负责了数个这样的系统,很快就会发现需要一个工具来简化自己的工作,这就是容器编排器的用途所在了。

容器编排器是一个工具,它可以将一组容器当成一个单元来管理。编排器让你可以将多台服务器合并成一个集群,并自动在集群节点之间分配容器工作负载,而不是单独在一台服务器操作。

Docker Compose 和 Swarm

Docker Compose 称不上是一个不完全的编排器,但这是 Docker 第一次尝试创建一个工具来简化多容器应用程序的管理。Compose 读取一个 YAML 文件,通常命名为 docker-compose.yml,并使用 Docker API 创建文件中声明的资源;Compose 还会为所有资源添加标签,以便在创建完成后把它们当成一个组来管理。实际上,它是 Docker 命令行接口(CLI,可操作容器组)的一个替代方案。Compose 文件可以定义三类资源:

  • 服务(services):声明要启动的容器,其中每一条都相当于一个 docker run 命令。
  • 网络(networks):声明可以附加到容器(Compose 文件中定义的)的网络,其中每一条相当于一个 docker network create 命令。
  • 卷(volumes):定义可以附加到容器的命名卷。在 Docker 术语中,卷是可以挂在到容器的持久存储。命名卷由 Docker daemon 管理。其中每一条相当于一个 docker volume create 命令。

网络和卷可以直接连接到 Docker 所在主机的网络和文件系统,也可以通过插件提供。网络插件可以帮我们实现像将容器连接到 V** 这样的事情;卷插件可以帮我们将卷存储在一台 NFS 服务器或一个对象存储服务上。

在管理多容器应用程序方面,Compose 提供了一种方便许多的方式,但在最初的版本中,它只能工作在单台主机上,创建的所有容器也运行在相同的机器上。为了让它能够覆盖多台主机,Docker 在 2016 年推出了 Swarm mode。实际上,这是 Docker 第二个带有“Swarm”字样的产品——2014 年的一款产品实现了一种完全不同的、跨多台主机运行容器的方式,但 Docker 已经不再维护,它被 SwarmKit 所取代,后者是 Docker Swarm 当前版本的基础。

Swarm mode 包含在 Docker 中,无需安装其他软件。创建集群很简单,只需在初始节点上运行 docker swarm init,然后在每个要添加的节点上运行 docker swarm join。Swarm 集群包含两种类型的节点。管理节点提供了一个 API,用于启动集群中的容器,并使用基于 Raft 一致性算法的协议互相通信,在所有管理节点间同步集群状态。工作节点完成运行容器的具体工作。这些集群可以到多大我们并不是很确定,Docker 的文档说一个集群的管理节点不应超过 7 个,但并没有说明工作节点的数量限制。跨节点桥接容器网络功能是自带的,但跨节点共享存储不是,需要借助第三方卷插件来提供跨节点的共享持久存储。

服务使用 Compose 文件部署到 Swarm 上。Swarm 扩展了 Compose 模式,为每个服务添加了一个 deploy 键,用于指定该服务应该运行多少实例以及应该在哪些节点上运行。遗憾的是,这导致了 Compose 和 Swarm 的分化,进而导致了一些混乱,因为对于像 CPU 和内存配额这样的选项,它们提供了不同的指定方式。

在这段时期内,为了避免歧义,用于 Swarm 的文件被称作“栈文件”,而不是 Compose 文件;所幸,在 Swarm 和 Compose 的当前版本中,这些差异似乎已经被抹平。如果引用的栈文件和 Compose 文件截然不同,那多半是从网上搜来的。现在,Compose 格式已经有了一个开放的规范,并且它自己的 GitHub 社区也提供了参考实现。

Swarm 的未来还存在一定的不确定性。它曾是 Docker Cloud 服务的基础,但该服务 2018 年突然关闭了;它也曾被认为是 Docker 企业版的关键特性,但那个产品自此卖给了另外一家公司,它现在的名称为 Mirantis Kubernetes Engine。与此同时,Compose 的最新版本已经具备向亚马逊和微软托管服务部署容器的能力。一直没有弃用声明,但记忆中也没有任何其他类型的公告。在 Docker 的网站上搜索“Swarm”这个词,也只能够搜到过去一些曾提及它的信息。

Kubernetes

Kubernetes (有时候称为 k8s)项目的灵感来自谷歌内部的一个工具 Borg。Kubernetes 可以在多达数千个节点的集群上管理资源并协调工作负载的运行;它在容器编排领域的统治地位就像谷歌在搜索领域的地位一样。2014 年,谷歌希望与 Docker 合作开发 Kubernetes,但 Docker 决定走自己的路,继续开发 Swarm。然而,Kubernetes 在云原生计算基金会(CNCF)的主持下发展壮大。截至 2017 年,Kubernetes 已经变得如此流行,以至于 Docker 也宣布将其集成到自己的产品中。

除了受欢迎之外,Kubernetes 主要以其复杂性而闻名。手动配置一个新集群是一项非常复杂的任务,除了 Kubernetes 之外,管理员还需要选择并配置多个第三方组件。和 Linux 内核需要结合额外的软件形成完整的操作系统一样,Kubernetes 只是一个编排器,它需要结合其他的软件才能组成一个完整的集群。它需要一个容器引擎来运行容器;它还需要网络插件以及持久卷插件。

Kubernetes 发行版的存在填补了这一空白。和 Linux 发行版一样,Kubernetes 发行版也将 Kubernetes 与安装程序以及精心挑选的第三方组件捆绑在一起。不同发行版的存在是为了满足不同的细分市场;似乎每个具有一定规模的公司都有自己的发行版和 / 或托管服务,以迎合企业的需求。minikube 项目让开发人员可以轻松地在本地搭建一个试验环境。和 Linux 发行版不同,Kubernetes 发行版经过了 CNCF 的一致性认证;为了得到认证,每个发行版都必须实现基本的功能,这样它们才能使用“Certified Kubernetes”标识。

一个 Kubernetes 集群包含多个软件组件。集群中的每个节点都运行着一个名为 kubelet 的代理,用于维护集群的成员关系并从它接收工作。它还运行着一个容器引擎和 kube-proxy,后者负责与在其他节点上运行的容器进行网络通信。

维护集群状态的组件以及决定资源分配的组件合称为控制平面——这包括一个名为 etcd 的分布式键值存储,一个给集群节点分配工作的调度器,一个或多个响应集群状态变化的控制器进程,负责触发所需的操作,使集群的状态符合预期。用户和集群节点通过 Kubernetes API Server 与控制平面交互。要实现修改,用户会通过 API Server 设定期望的集群状态,而 kubelet 会向控制器进程报告每个集群节点的实际状态。

Kubernetes 在一个名为 pod 的抽象中运行容器,其中可以包含一个或多个容器,不过,并不建议在一个 pod 中运行多个服务的容器。相反,一个 pod 通常只有一个提供服务的主容器,可能会有一个或多个“边”容器负责从运行服务的主容器中收集指标或日志。一个 pod 中的所有容器都将调度到同一台机器上,共享同一个网络命名空间——在同一 pod 中运行的容器可以通过 loopback 接口相互通信。每个 pod 在集群中都有自己独一无二的 IP 地址。运行在不同 pod 中的容器可以使用它们的集群 IP 地址相互通信。

Pod 指定一组要运行的容器,但 pod 的定义并没有说明在哪里运行这些容器,或者是运行多长时间——没有这些信息,Kubernetes 就会在集群上的随便什么地方启动容器,但不会在它们退出时重启,而且,如果控制平面判断其他工作负载需要它们占用的资源,Kubernetes 可能会终止它们。为此,pod 很少单独使用;取而代之,pod 的定义通常是封装在用于定义持久服务的 Deployment 对象中。和 Compose 及 Swarm 一样,由 Kubernetes 管理的对象是在 YAML 中声明的;对于 Kubernetes,YAML 声明是通过 kubectl 工具提交到集群的。

除了 pod 和 Deployment 之外,Kubernetes 还可以管理许多其他类型的对象,如负载均衡器和授权策略。它支持的 API 清单还在不断增长,根据 Kubernetes 的版本以及集群运行的哪个发行版会有所不同。自定义资源可以用于向集群添加 API,用于管理额外的对象类型。例如,KubeVirt 添加的 API 使 Kubernetes 可以运行虚拟机。可以通过 kubectl api-versions 命令查看特定集群支持的完整 API 清单。

和 Compose 不同,这些对象中的每一种都是在单独的 YAML 文档中声明的,虽然多个 YAML 文档可以内联到一个文件中,并用“---”隔开(参加 Kubernetes 文档)。一个复杂的应用程序可能包含许多对象,它们的定义分散在多个文件中;在维护这样一个应用程序时,保持所有这些定义彼此同步会非常繁琐。为了简化这项工作,有些 Kubernetes 管理员转而采用像 Jsonnet 这样的模板工具。

Helm 让模板工具更上一层楼。和 Kubernetes 类似,Helm 也是在 CNCF 的主持下开发的;它号称是“Kubernetes 包管理器”。Helm 从一套名为 chart 的模板和变量声明生成 Kubernetes 的 YAML 配置。它使用的模板语言不同于 Ansible 使用的 Jinja 模板,但看上去非常相似;熟悉 Ansible Roles 的人看到 Helm Charts 也会感觉很熟悉。

Helm Charts 集可以发布到 Helm 存储库;Artifact Hub 提供了一个很大的公共 Helm 存储库目录。管理员可以将这些存储库添加到他们的 Helm 配置中,使用已经编制好的 Heml chart 将流行应用的预打包版本部署到集群上。Helm 的最新版本还支持向容器注册中心推送或从注册中心拉取 chart,为管理员提供了一个选型,让他们可以将 chart 和容器镜像存储在一起。

Kubernetes 的增长势头还没有任何减缓的趋势。按照设计,它可以管理任何类型的资源;这种灵活性(已为 KubeVirt 虚拟机控制器所证明)使得,即使容器化工作负载最终不再流行,Kubernetes 仍然有继续存在的价值。开发有序进行,新的主版本定期发布。版本支持周期为 1 年;似乎没有长期支持版本。集群可以升级,但有人喜欢新建一个集群,并将服务迁移过去。

Nomad

Nomad 是 HashiCorp 推出的一个编排器,号称是一个比 Kubernetes 更简单的替代方案。和 Docker 及 Kubernetes 类似,Nomad 是一个开源项目。它包含一个名为 nomad 的二进制文件,可以用于启动一个名为代理的守护进程。它还提供了一个 CLI,用于和代理通信。根据配置方式不同,代理进程可以在两种模式下运行。在服务器模式下运行的代理可以接受作业,并为它们分配集群资源。在客户端模式下运行的代理会接收作业,运行它们,并将作业状态报告给服务器。代理还可以在开发模式下运行,同时承担客户端和服务器的角色,成为一个用于测试目的的单节点集群。

创建 Nomad 集群相当简单。在 Nomad 最基本的操作模式中,必须启动初始服务器代理,然后使用 nomad server join 命令向集群添加额外的节点。HashiCorp 还提供了 Consul,这是一个通用的服务网格和发现工具。虽然 Nomad 可以单独使用,但最好是和 Consul 搭配使用。Nomad 代理可以使用 Consul 自动发现并加入一个集群,它还可以执行健康检查,提供 DNS 记录,并为集群上运行的服务提供 HTTPS 代理。

Nomad 支持复杂的集群拓扑。每个集群被划分成一个或多个“数据中心”。和 Swarm 类似,同一数据中心中的服务器代理使用一种基于 Raft 的协议相互通信;这种协议有严格的延迟要求,但多个数据中心可以用 gossip 协议连接起来,从而使信息可以在集群中传播,而又不需要每个服务器与其他服务器保持直接连接。从用户的角度来看,以这种方式连接起来的数据中心就和一个集群一样。这种架构让 Nomad 在扩展到大量集群时颇有优势。按照官方说法,Kubernetes 最多支持 5000 个节点和 3 万个容器,而 Nomad 的文档中提到了一个有 1 万多节点的集群示例和一个有 20 万容器的集群示例。

和 Kubernetes 类似,Nomad 并没有包含一个容器引擎或运行时,它使用任务驱动器来运行作业。它包含使用 Docker 和 Podman 来运行容器的任务驱动器;社区提供了面向其他容器引擎的驱动器。Nomad 的野心也不限于容器,这点也和 Kubernetes 类似。它还提供了面向其他工作负载类型的任务驱动器,包括在主机上运行命令的 fork/exec 驱动器,运行虚拟机的 QEMU 驱动器,启动 Java 应用程序的 Java 驱动器。社区支持的任务驱动器可以将 Nomad 连接到其他类型的工作负载。

与 Docker 或 Kubernetes 不同,Nomad 不使用 YAML,而是使用 HashiCorp 配置语言(HCL)。HCL 最初是为 HashiCorp 的另一个项目创建的,用于配置名为 Terraform 的云资源。虽然在其他地方的应用有限,但 HashiCorp 的整个产品线都在使用 HCL。用 HCL 编写的文档很容易转换成 JSON,但它的目标是提供一种比 JSON 更便捷、比 YAML 更不容易出错的语法。

HashiCorp 提供的相当于 Helm 的产品是 Nomad Pack。和 Helm 类似,Nomad Pack 会处理一个满是模板和变量声明的目录,生成作业配置。Nomad 还有一个预打包应用程序的社区注册中心,但其选择空间比 Helm 的 Artifact Hub 要小很多。

Nomad 不像 Kubernetes 那么受欢迎。和 Swarm 一样,它的开发似乎主要是由其创建者推动的;尽管有许多大公司部署了 HashiCorp,但 HashiCorp 仍然是 Nomad 相关社区的中心。目前看来,该项目似乎不太可能获得足够的发展势头,进而从母公司独立出来。对用户来说,与 Docker 在 Swarm 上做的工作相比,HashiCorp 对 Nomad 的开发和推广或许更有保证。

小 结

Swarm、Kubernetes 和 Nomad 并不是仅有的容器编排器,但它们是其中最有生命力的三个。Apache Mesos 也可以用来运行容器,但它在 2021 年就几乎被封存了;DC/OS 基于 Mesos,但很像 Docker 企业版,支持其开发的公司现在也专注于 Kubernetes。其他大多数容器编排项目,如 OpenShift 和 Rancher,实际上只是增强(和认证)的 Kubernetes 发行版,即使它们的名字中没有 Kubernetes。

尽管 Kubernetes 非常复杂,它仍然是目前最流行的编排器,但 HashiCorp 在 Nomad 上的成功表明,替代方案也还有它的发展空间。有些用户仍然热衷于 Docker Swarm 的简单性,但它的未来存在不确定性。至此,其他替代方案看上去已经基本被放弃了。现在的生态似乎主要围绕着这三个玩家,但容器编排仍然是一个相对不那么成熟的领域。十年前,这种技术几乎还不存在,而它现在仍在快速发展。容器编排领域可能还会出现许多令人兴奋的新想法和新发展。

原文链接:

The container orchestrator landscape (https://lwn.net/Articles/905164/)

声明:本文为InfoQ翻译,未经许可禁止转载。

0 人点赞