本文发表已超过一年。较旧的文章可能包含过时内容。请检查页面信息自发表以来是否仍然正确。

我们如何在 Kubernetes 中运行 Kubernetes,又名 Kubeception

Giant Swarm 的容器基础设施最初的目标是成为一种开发者部署容器化微服务的简便方式。我们的第一代广泛使用 fleet 作为基础设施组件的基础层,以及用于调度用户容器。

为了给用户提供更强大的方式管理容器,我们在 2016 年初将 Kubernetes 引入了我们的技术栈。然而,由于我们需要一种灵活、快速地弹性启动和管理不同用户 Kubernetes 集群的方式,我们保留了底层的 fleet 层。

由于我们坚持在容器中运行所有底层基础设施组件,fleet 提供了使用 systemd unit 文件来声明式地定义我们的基础设施组件的灵活性。我们自主开发的部署工具使我们能够部署和管理基础设施,无需命令式配置管理工具。

然而,fleet 只是一个分布式 init,而不是一个完整的调度和编排系统。除了在我们的工具上做了大量工作之外,它还需要在 peers 之间的通信、其协调循环以及我们需要解决的稳定性方面进行重大改进。此外,Kubernetes 使用量的增加将确保更快地发现和修复问题。

由于我们在用户侧引入 Kubernetes 方面取得了良好经验,并且随着 rktnetesstackanetes 等最新进展的出现,我们觉得是时候也将我们的基础层迁移到 Kubernetes 了。

为什么要在 Kubernetes 中运行 Kubernetes

现在,你可能会问,为什么会有人想在一个 Kubernetes 集群内部运行多个 Kubernetes 集群?我们疯了吗?答案是高级多租户用例,以及其可操作性和自动化。

Kubernetes 自身提供了一套不断增长的用于多租户用例的功能。然而,我们的目标是为用户提供完全托管的 Kubernetes,不限制他们在任何原版 Kubernetes 环境中可获得的功能,包括对节点的特权访问。此外,在更大型的企业场景中,单个 Kubernetes 集群及其内置的隔离机制通常不足以满足合规性和安全要求。更高级的(防火墙隔离的)分区或分层安全概念用单个安装很难实现。仅通过命名空间隔离,无论是特权访问还是防火墙隔离区域,都很难在不规避安全措施的情况下实现。

你可以设置多个完全独立(并联邦化)的 Kubernetes 安装。然而,自动化这些集群的部署和管理将需要额外的工具和复杂的监控设置。此外,我们希望能够按需启动和关闭集群、伸缩它们、更新它们、跟踪哪些集群可用,并能够灵活地将它们分配给组织和团队。事实上,这种设置可以与联邦控制平面结合使用,通过一个 API 端点将部署联邦化到各个集群。

有没有一个为此服务的 API 和前端岂不更好?

Giantnetes 登场

基于上述需求,我们开始构建我们称之为 Giantnetes 的系统,或者如果你喜欢电影,可以称之为 Kubeception。在最基础的抽象层面上,它是一个外部 Kubernetes 集群(即 Giantnetes 本身),用于运行和管理多个完全隔离的用户 Kubernetes 集群。

物理机通过使用我们的 CoreOS Container Linux 引导工具 Mayu 进行引导。Giantnetes 组件本身是自托管的,即一个 kubelet 负责自动引导位于 manifests 文件夹中的组件。这可以称为 Kubeception 的第一层。

Giantnetes 集群运行起来后,我们用它来调度用户 Kubernetes 集群,以及我们用于管理和保护它们的工具。

我们选择 Calico 作为 Giantnetes 的网络插件,以确保安全性、隔离性和适当的性能,用于所有运行在 Giantnetes 之上的应用。

然后,为了创建内部 Kubernetes 集群,我们启动一些 Pod,这些 Pod 配置网络桥接,创建证书和令牌,并启动虚拟机用于未来的集群。为此,我们使用轻量级技术,例如 KVM 和 qemu,来供应 CoreOS Container Linux VM,这些 VM 成为内部 Kubernetes 集群的节点。这可以称为 Kubeception 的第二层。

目前这意味着我们启动包含 Docker 容器的 Pod,这些容器进而使用 KVM 和 qemu 启动 VM。然而,我们正在研究使用 rkt qemu-kvm 来实现这一点,这将使得我们的 Giantnetes 使用 rktnetes 架构。

内部 Kubernetes 集群的网络解决方案有两层。它依赖于 flannel 的服务器/客户端架构模型和 Calico BGP 的组合。flannel 客户端用于在每个虚拟化内部 Kubernetes 集群的 VM 之间创建网络桥接,而 Calico 运行在虚拟机内部,连接不同的 Kubernetes 节点,并为内部 Kubernetes 创建一个单一网络。通过使用 Calico,我们在每个 Kubernetes 集群内部模仿 Giantnetes 的网络解决方案,并通过 Kubernetes 网络策略 API 提供原语来保护和隔离工作负载。

关于安全性,我们致力于尽可能分离特权,并使操作可审计。目前这意味着我们使用证书来保护对集群的访问,并加密构成集群的所有组件之间的通信(例如 VM 到 VM、Kubernetes 组件之间、etcd master 到 Calico workers 等)。为此,我们为每个集群创建一个 PKI 后端,然后按需在 Vault 中为每个服务颁发证书。每个组件使用不同的证书,因此,如果任何组件或节点被攻陷,可避免暴露整个集群。我们还会定期轮换证书。

为了确保从外部访问每个内部 Kubernetes 集群的 API 和服务,我们在 Giantnetes 中运行多层 HAproxy Ingress 控制器设置,该设置将 Kubernetes VM 连接到硬件负载均衡器。

使用 kubectl 查看 Giantnetes

让我们看看一个 Giantnetes 的最小示例部署。

Screen Shot 2016-11-14 at 12.08.40 PM.png

在上面的例子中,你看到一个用户 Kubernetes 集群 customera 在 Giantnetes 之上的 VM-容器中运行。我们目前使用 Job 来进行网络和证书设置。

窥探用户集群内部,你看到 DNS Pod 和一个正在运行的 helloworld 应用。

Screen Shot 2016-11-14 at 12.07.28 PM.png

这些用户集群中的每一个都可以独立地被调度和使用。它们可以按需启动和关闭。

结论

总而言之,我们展示了 Kubernetes 如何不仅能够轻松自托管,还能灵活调度大量内部 Kubernetes 集群,同时确保更高的隔离性和安全性。此设置的一个亮点是安装的可组合性和自动化,以及 Kubernetes 组件之间强大的协调能力。这使得我们能够轻松地按需创建、销毁和重新调度集群,而不影响用户或损害基础设施的安全性。它还允许我们通过在创建集群时更改一些参数来启动具有不同大小、配置甚至版本的集群。

这个设置仍处于早期阶段,我们的路线图规划在许多领域进行改进,例如透明升级、集群的动态重新配置和伸缩、性能改进以及(更多的)安全性。此外,我们期待通过利用不断发展的 Kubernetes 运维工具和即将推出的特性来改进我们的设置,例如 Init Container、Scheduled Job、Pod 和 Node 亲和性与反亲和性等。

最重要的是,我们正在努力将内部 Kubernetes 集群打造成一个第三方资源,然后可以由自定义控制器管理。其结果将非常类似于 CoreOS 的 Operator 概念。为了确保广大社区可以从这个项目中受益,我们将在近期将其开源。