这篇文章已超过一年。较旧的文章可能包含过时的内容。请检查页面中的信息自发布以来是否已变得不正确。
Gardener - Kubernetes 植物学家
如今,Kubernetes 是在云中运行软件的自然选择。越来越多的开发人员和公司正在将他们的应用程序容器化,并且他们中的许多人正在采用 Kubernetes 来自动部署他们的云原生工作负载。
有许多开源工具可以帮助创建和更新单个 Kubernetes 集群。然而,你需要的集群越多,操作、监控、管理以及保持它们全部正常运行和更新就越困难。
而这正是项目 “Gardener” 的重点。它不仅仅是另一个配置工具,而是被设计为管理 Kubernetes 集群作为一项服务。它在各种云提供商上提供 符合 Kubernetes 标准的 集群,并能够在规模上维护数百或数千个集群。在 SAP,我们不仅在我们自己的平台上,而且在我们所有大大小小的客户实施 Kubernetes 和云原生时都面临着这种异构多云和本地挑战。
受 Kubernetes 的可能性和自托管能力的启发,Gardener 的基础是 Kubernetes 本身。虽然自托管,即将 Kubernetes 组件在 Kubernetes 内部运行是社区中一个热门话题,但我们应用了一种特殊的模式,以满足以最小的总拥有成本运行大量集群的需求。我们采用一个初始的 Kubernetes 集群(称为“种子”集群),并将最终用户集群的控制平面组件(例如 API 服务器、调度程序、控制器管理器、etcd 等)作为简单的 Kubernetes pod 播种。本质上,种子集群的重点是在规模上提供强大的控制平面即服务。按照我们的植物学术语,当最终用户集群准备好发芽时,它们被称为“幼苗”集群。考虑到网络延迟和其他故障情况,我们建议每个云提供商和区域使用一个种子集群来托管许多幼苗集群的控制平面。
总的来说,这种重用 Kubernetes 原语的概念已经简化了控制平面的部署、管理、扩展和修补/更新。由于它建立在高度可用的初始种子集群之上,因此我们可以避免幼苗集群控制平面的多个仲裁数量的主节点要求,并减少浪费/成本。此外,实际的幼苗集群仅由工作节点组成,可以授予各自所有者完全的管理访问权限,从而构建必要的分离关注点以提供更高水平的 SLO。因此,架构角色和运营所有权定义如下(参见 图 1
)
- Kubernetes 作为服务提供商拥有、运营和管理花园和种子集群。它们代表所需环境/基础设施的一部分。
- 幼苗集群的控制平面在种子中运行,因此在服务提供商的单独安全域内运行。
- 幼苗集群的机器在客户的所有权和云提供商帐户和环境中运行,但仍由 Gardener 管理。
- 对于本地或私有云场景,可以委托种子集群(和 IaaS)的所有权和管理。

图 1 具有组件的 Gardener 技术架构。
Gardener 被开发为聚合的 API 服务器,并附带一组捆绑的控制器。它在另一个专用的 Kubernetes 集群(称为“花园”集群)内部运行,并使用自定义资源扩展了 Kubernetes API。最重要的是,Shoot 资源允许以声明方式描述用户 Kubernetes 集群的整个配置。相应的控制器将像本机 Kubernetes 控制器一样,监视这些资源并将世界的实际状态带到所需状态(导致创建、协调、更新、升级或删除操作)。以下示例清单显示了需要指定的内容
apiVersion: garden.sapcloud.io/v1beta1
kind: Shoot
metadata:
name: dev-eu1
namespace: team-a
spec:
cloud:
profile: aws
region: us-east-1
secretBindingRef:
name: team-a-aws-account-credentials
aws:
machineImage:
ami: ami-34237c4d
name: CoreOS
networks:
vpc:
cidr: 10.250.0.0/16
...
workers:
- name: cpu-pool
machineType: m4.xlarge
volumeType: gp2
volumeSize: 20Gi
autoScalerMin: 2
autoScalerMax: 5
dns:
provider: aws-route53
domain: dev-eu1.team-a.example.com
kubernetes:
version: 1.10.2
backup:
...
maintenance:
...
addons:
cluster-autoscaler:
enabled: true
...
一旦发送到花园集群,Gardener 将会提取它并配置实际的幼苗。上面未显示的是,每个操作都将丰富 Shoot
的 status
字段,指示操作当前是否正在运行,并记录最后一个错误(如果有)以及所涉及组件的运行状况。用户能够以真正的 Kubernetes 样式配置和监控其集群的状态。我们的用户甚至编写了自己的自定义控制器来监视和修改这些 Shoot
资源。
技术深入探讨
Gardener 实现了 Kubernetes 初创方法;因此,它利用 Kubernetes 功能来执行其操作。它提供了几个控制器(参见 [A]
)来监视 Shoot
资源,而主控制器负责创建、更新和删除等标准操作。另一个名为“幼苗护理”的控制器执行定期运行状况检查和垃圾回收,而第三个控制器(“幼苗维护”)的任务是涵盖诸如将幼苗的机器映像更新到最新可用版本之类的操作。
对于每个幼苗,Gardener 都会在种子中创建一个专用的 Namespace
,其中包含适当的安全策略,并在其中预先创建以后需要的证书,这些证书作为 Secrets
进行管理。
etcd
Kubernetes 集群的后备数据存储 etcd(参见 [B]
)部署为具有一个副本和一个 PersistentVolume(Claim)
的 StatefulSet
。为了遵循最佳实践,我们运行另一个 etcd 分片实例来存储幼苗的 Events
。无论如何,主 etcd pod 都通过 sidecar 增强,该 sidecar 验证静态数据并定期拍摄快照,然后将这些快照有效地备份到对象存储中。如果 etcd 的数据丢失或损坏,则 sidecar 会从最新可用的快照中恢复它。我们计划开发增量/连续备份,以避免在恢复时恢复的 etcd 状态与实际状态之间存在差异 [1]。
Kubernetes 控制平面
如上所述,我们将其他 Kubernetes 控制平面组件放入本机 Deployments
中,并使用滚动更新策略运行它们。通过这样做,我们不仅可以利用 Kubernetes 现有的部署和更新功能,还可以利用其监控和活跃度能力。虽然控制平面本身使用集群内通信,但 API 服务器的 Service
通过负载均衡器暴露以进行外部通信(参见 [C]
)。为了统一生成部署清单(主要取决于 Kubernetes 版本和云提供商),我们决定使用 Helm 图表,而 Gardener 仅利用 Tillers 的渲染功能,但直接部署生成的清单,而根本不运行 Tiller [2]。
基础设施准备
创建集群的首要要求之一是在云提供商端准备充分的基础设施,包括网络和安全组。在我们当前的 Gardener 提供商特定的树内实现(称为“植物学家”)中,我们使用 Terraform 来完成此任务。Terraform 为主要的云提供商提供了不错的抽象,并实现了诸如并行性、重试机制、依赖关系图、幂等性等功能。然而,我们发现 Terraform 在错误处理方面具有挑战性,并且它不提供用于提取错误根本原因的技术接口。目前,Gardener 根据幼苗规范生成 Terraform 脚本,并将其存储在种子集群的相应命名空间中的 ConfigMap
中。Terraformer 组件 然后作为 Job
(参见 [D]
)运行,执行已挂载的 Terraform 配置,并将生成的状态写回到另一个 ConfigMap
中。以这种方式使用 Job 原语有助于继承其重试逻辑,并实现针对临时连接问题或资源约束的容错能力。此外,Gardener 只需要访问种子集群的 Kubernetes API 即可提交底层 IaaS 的 Job。此设计对于私有云场景很重要,在私有云场景中,IaaS API 通常不会公开公开。
机器控制器管理器
接下来需要的是集群实际工作负载要调度的节点。然而,Kubernetes 没有提供请求节点的原语,这迫使集群管理员使用外部机制。考虑因素包括完整的生命周期,从初始配置开始,持续到提供安全修复、执行健康检查和滚动更新。虽然我们最初是通过实例化静态机器或利用云提供商的实例模板来创建工作节点,但我们得出结论(也从我们之前运行云平台的生产经验中得出),这种方法需要大量的工作。在 2017 年 KubeCon 的讨论中,我们认识到管理集群节点的最佳方式,当然是再次应用核心 Kubernetes 概念,并教会系统自我管理其运行的节点/机器。为此,我们开发了 machine controller manager (参见 [E]
),它使用 MachineDeployment
、MachineClass
、MachineSet
和 Machine
资源扩展了 Kubernetes,并实现了从 Kubernetes 上下文中对(虚拟)机器的声明式管理,就像 Deployments
、ReplicaSets
和 Pods
一样。我们重用了现有 Kubernetes 控制器中的代码,只需要为创建、删除和列出专用驱动程序中的机器抽象一些 IaaS/云提供商特定的方法。当比较 Pod 和 Machine 时,一个细微的差异变得明显:直接创建虚拟机将导致成本,如果发生任何不可预见的事情,这些成本可能会迅速增加。为了防止此类失控,machine controller manager 配备了一个安全控制器,该控制器会终止孤立的机器,并将 MachineDeployment 和 MachineSet 的推出冻结在某些阈值和超时时间之外。此外,我们利用现有的官方 cluster-autoscaler,其中已经包括了确定要横向扩展或缩减哪个节点池的复杂逻辑。由于其云提供商接口设计良好,我们使 autoscaler 能够在触发横向扩展或缩减时直接修改相应 MachineDeployment
资源中的副本数量。
插件
除了提供正确设置的控制平面外,每个 Kubernetes 集群都需要一些系统组件才能工作。通常,这些组件是 kube-proxy、覆盖网络、集群 DNS 和入口控制器。除此之外,Gardener 允许订购用户可配置的可选插件(在 shoot 资源定义中),例如 Heapster、Kubernetes Dashboard 或 Cert-Manager。同样,Gardener 通过 Helm 图表(部分改编和整理自 上游图表存储库)呈现所有这些组件的清单。然而,这些资源在 shoot 集群中进行管理,因此用户可以使用完全的管理访问权限进行调整。因此,Gardener 通过利用现有的监视器 kube-addon-manager (参见 [F]
) 来确保这些部署的资源始终与计算/期望的配置匹配。
网络气隙
虽然 shoot 集群的控制平面在由您友好的平台提供商管理和提供的 seed 中运行,但工作节点通常在用户的单独云提供商(计费)帐户中进行配置。通常,这些工作节点被放置在私有网络 [3] 中,seed 控制平面中的 API 服务器使用基于 ssh 的简单 VPN 解决方案 (参见 [G]
) 与其建立直接通信。我们最近已将基于 SSH 的实现迁移到基于 OpenVPN 的实现,这显着提高了网络带宽。
监控和日志记录
监控、警报和日志记录对于监视集群并保持其健康以避免中断和其他问题至关重要。Prometheus 已成为 Kubernetes 领域中最常用的监控系统。因此,我们在每个 seed 的 garden
命名空间中部署一个中央 Prometheus 实例。它从所有 seed 的 kubelet 收集指标,包括在 seed 集群中运行的所有 Pod 的指标。此外,在每个控制平面旁边,都为 shoot 本身配置了一个专用的租户 Prometheus 实例 (参见 [H]
)。它收集其自身控制平面以及在 shoot 工作节点上运行的 Pod 的指标。前者是通过从中央 Prometheus 的联邦端点获取数据并筛选特定 shoot 的相关控制平面 Pod 来完成的。除此之外,Gardener 还部署了两个 kube-state-metrics 实例,一个负责控制平面,另一个负责工作负载,从而公开集群级指标以丰富数据。节点导出器 提供更详细的节点统计信息。一个专用的租户 Grafana 仪表板通过清晰的仪表板显示分析和见解。我们还为关键事件定义了警报规则,并使用 AlertManager 在触发任何警报时向操作员和支持团队发送电子邮件。
[1] 这也是不支持时间点恢复的原因。到目前为止,Kubernetes 中没有实现可靠的基础架构协调。因此,在不刷新相关集群的实际工作负载和状态的情况下从旧备份恢复通常无济于事。
[2] 做出此决定的最相关标准是,Tiller 需要端口转发连接才能进行通信,我们发现这对于我们的自动化用例来说太不稳定且容易出错。尽管如此,我们期待 Helm v3 希望使用 CustomResourceDefinitions
与 Tiller 进行交互。
[3] Gardener 提供了使用 Terraformer 创建和准备这些网络,或者可以指示它重用预先存在的网络。
可用性和交互
尽管仅需要熟悉的 kubectl
命令行工具来管理所有 Gardener,但我们提供了一个中央 仪表板 以方便交互。它使用户可以轻松跟踪其集群的运行状况,并使操作员可以监视、调试和分析他们负责的集群。Shoots 被分组到逻辑项目中,在这些项目中,管理一组集群的团队可以协作,甚至可以通过集成的票证系统(例如 GitHub Issues)跟踪问题。此外,该仪表板还可以帮助用户添加和管理其基础架构帐户机密,并在一个地方查看所有 shoot 集群的最相关数据,同时独立于它们部署到的云提供商。
图 2:动画 Gardener 仪表板。
更专注于开发人员和操作员的职责,Gardener 命令行客户端 gardenctl
通过引入简单的更高级抽象和简单的命令来简化管理任务,这些命令有助于从/向大量 seed 和 shoot 集群压缩和多路复用信息和操作。
$ gardenctl ls shoots
projects:
- project: team-a
shoots:
- dev-eu1
- prod-eu1
$ gardenctl target shoot prod-eu1
[prod-eu1]
$ gardenctl show prometheus
NAME READY STATUS RESTARTS AGE IP NODE
prometheus-0 3/3 Running 0 106d 10.241.241.42 ip-10-240-7-72.eu-central-1.compute.internal
URL: https://user:[email protected]
展望和未来计划
Gardener 已经能够在 AWS、Azure、GCP、OpenStack [4] 上管理 Kubernetes 集群。实际上,由于它仅依赖于 Kubernetes 原语,因此它可以很好地满足私有云或本地部署要求。从 Gardener 的角度来看,唯一的区别是底层基础架构的质量和可扩展性 - Kubernetes 的通用语言确保了我们方法的强大可移植性保证。
然而,未来仍存在挑战。我们正在探索一种可能性,即在此开源项目中包含一个选项,以创建一个联邦控制平面,该平面委托给多个 shoot 集群。在前面的章节中,我们没有解释如何 引导 garden 和 seed 集群本身。您确实可以使用任何生产就绪的集群配置工具或云提供商的 Kubernetes 即服务产品。我们已经构建了一个基于 Terraform 的统一工具,称为 Kubify,并重用了许多提到的 Gardener 组件。我们设想所需的 Kubernetes 基础架构能够通过初始引导 Gardener 完全生成,并且我们已经在讨论如何实现这一点。
我们关注的另一个重要主题是灾难恢复。当 seed 集群发生故障时,用户的静态工作负载将继续运行。但是,将无法再管理集群。我们正在考虑将受灾难影响的 shoot 的控制平面移动到另一个 seed。从概念上讲,这种方法是可行的,并且我们已经具备实现该方法所需的组件,例如自动 etcd 备份和还原。该项目的贡献者不仅有权开发用于生产的 Gardener,而且我们中的大多数人甚至以真正的 DevOps 模式运行它。我们完全信任 Kubernetes 概念,并致力于遵循“吃自己的狗粮”的方法。
为了使 Botanists(包含实现的基础架构提供商特定部分)能够进行更独立的发展,我们计划描述明确定义的接口并将 Botanists 分解为自己的组件。这类似于 Kubernetes 目前对 cloud-controller-manager 所做的事情。目前,所有云特定信息都是核心 Gardener 存储库的一部分,这为扩展或支持新的云提供商设置了一个软障碍。
当查看 shoot 的实际配置方式时,我们需要获得更多关于具有数千个节点和 Pod(或更多)的真正大型集群如何运行的经验。我们可能需要以横向扩展的方式部署 API 服务器和其他组件,以分散大型集群的负载。幸运的是,基于 Prometheus 的自定义指标的横向 Pod 自动缩放将使我们的设置相对容易实现。此外,在我们的集群上运行生产工作负载的团队的反馈是,Gardener 应该支持预先安排的 Kubernetes QoS。毋庸置疑,我们的目标将是整合并为 Kubernetes Autopilot 的愿景做出贡献。
[4] 原型已经验证了 CTyun 和阿里云。
Gardener 是开源的
Gardener 项目以开源形式开发,并在 GitHub 上托管:https://github.com/gardener
SAP 自 2017 年年中以来一直在开发 Gardener,并专注于构建一个可以轻松发展和扩展的项目。因此,我们现在正在寻找该项目的更多合作伙伴和贡献者。如上所述,我们完全依赖于 Kubernetes 原语、插件和规范,并采用其创新的 Cloud Native 方法。我们期待与 Kubernetes 社区保持一致并为其做出贡献。事实上,我们设想将整个项目贡献给 CNCF。
目前,与社区合作的一个重要重点是几个月前在 SIG Cluster Lifecycle 中成立的 Cluster API 工作组。其主要目标是定义一个代表 Kubernetes 集群的可移植 API,包括控制平面和底层基础设施的配置。我们现有的 Shoot 和 Machine 资源与社区正在开发的内容之间的重叠非常显著。因此,我们加入了这个工作组,并积极参与他们的定期会议,试图回馈我们在生产中的经验。从私心来说,塑造一个健壮的 API 也符合我们的利益。
如果您看到了 Gardener 项目的潜力,请在 GitHub 上了解更多信息,并通过提出问题、参与讨论和贡献代码来帮助我们使 Gardener 变得更好。此外,请尝试我们的快速入门设置。
我们期待在那里见到您!