本文已超过一年。旧文章可能包含过时内容。请检查页面信息自发布以来是否已不再准确。
Hypernetes:将安全和多租户带到 Kubernetes
虽然许多开发者和安全专业人士认为 Linux 容器是有效的隔离边界,但许多用户需要更强的隔离级别,特别是在多租户环境中运行的用户。遗憾的是,如今,这些用户被迫在虚拟机内部运行其容器,甚至每个容器使用一个虚拟机。
不幸的是,这导致失去了许多云原生部署的优势:虚拟机启动慢;每个容器的内存消耗;利用率低导致资源浪费。
在本文中,我们将介绍 HyperContainer,一种基于管理程序的容器,并探讨它如何自然地融入 Kubernetes 设计,使用户能够直接使用虚拟化容器为其客户提供服务,而不是将它们封装在成熟的虚拟机内部。
HyperContainer
HyperContainer 是一种基于管理程序的容器,它允许您使用标准管理程序(KVM、Xen 等)启动 Docker 镜像。作为一个开源项目,HyperContainer 由一个兼容 OCI 的运行时实现,名为 runV,以及一个管理守护程序,名为 hyperd 组成。HyperContainer 背后的想法非常直接:结合虚拟化和容器的最佳特性。
我们可以将容器视为两部分(就像 Kubernetes 所做的那样)。第一部分是容器运行时,HyperContainer 使用虚拟化而不是命名空间和 cgroup 来实现执行隔离和资源限制。第二部分是应用数据,HyperContainer 利用 Docker 镜像。因此,在 HyperContainer 中,虚拟化技术使得构建一个具有独立访客内核(因此像 top
和 /proc 这样的命令都能工作)的完全隔离沙箱成为可能,但从开发者的角度来看,它具有可移植性,表现得像一个标准容器。
HyperContainer 作为 Pod
HyperContainer 的有趣之处不仅在于它对于多租户环境(例如公共云)足够安全,还在于它如何很好地融入 Kubernetes 的理念。
Kubernetes 中最重要的概念之一是 Pod。Pod 的设计是根据现实世界工作负载(Borg 论文第 8.1 节)吸取的教训,在许多情况下,人们需要由多个容器组成的原子调度单元(请查看此示例了解更多信息)。在 Linux 容器的背景下,Pod 将几个容器封装并包装成一个逻辑组。但在 HyperContainer 中,管理程序充当自然边界,并且 Pod 作为一等对象引入
HyperContainer 将一组轻量级应用容器封装成一个 Pod,并在 Pod 级别公开容器接口。在 Pod 内部,会启动一个名为 HyperKernel 的极简 Linux 内核。HyperKernel 使用一个名为 HyperStart 的微小 Init 服务构建。它将充当 PID 1 进程,创建 Pod,设置 Mount 命名空间,并从加载的镜像中启动应用。
这种模型与 Kubernetes 配合得很好。如标题所示,HyperContainer 与 Kubernetes 的集成构成了 Hypernetes 项目。
Hypernetes
Kubernetes 最好的部分之一是它被设计为支持多种容器运行时,这意味着用户不会被锁定到单一供应商。我们非常高兴地宣布,我们已经开始与 Kubernetes 团队合作,将 HyperContainer 集成到 Kubernetes 上游。这种集成包括:
- 容器运行时优化和重构
- 新的客户端-服务器模式运行时接口
- containerd 集成以支持 runV
OCI 标准和 kubelet 的多运行时架构使得这种集成变得更加容易,即使 HyperContainer 不是基于 Linux 容器技术栈。
另一方面,为了在多租户环境中运行 HyperContainer,我们还创建了一个新的网络插件,并修改了一个现有的卷插件。由于 Hypernetes 将 Pod 作为它们自己的虚拟机运行,它可以利用您现有的 IaaS 层技术来实现多租户网络和持久卷。当前的 Hypernetes 实现使用了标准的 Openstack 组件。
下面我们将详细介绍上述所有内容的实现方式。
身份和认证
在 Hypernetes 中,我们选择 Keystone 来管理不同的租户,并在任何管理操作期间对租户进行身份识别和认证。由于 Keystone 来自 OpenStack 生态系统,它可以与我们在 Hypernetes 中使用的网络和存储插件无缝协作。
多租户网络模型
对于一个多租户容器集群,每个租户需要与其他租户有强大的网络隔离。在 Hypernetes 中,每个租户都有自己的网络。无需使用 OpenStack(这很复杂)来配置新网络,使用 Hypernetes,您只需创建一个如下所示的 Network 对象。
apiVersion: v1
kind: Network
metadata:
name: net1
spec:
tenantID: 065f210a2ca9442aad898ab129426350
subnets:
subnet1:
cidr: 192.168.0.0/24
gateway: 192.168.0.1
请注意,tenantID 由 Keystone 提供。这个 yaml 文件将自动创建一个新的 Neutron 网络,其中包含一个默认路由器和一个 192.168.0.0/24 子网。
Network 控制器将负责管理用户创建的任何 Network 实例的生命周期。此 Network 可以分配给一个或多个 Namespace,属于同一 Network 的任何 Pod 可以通过 IP 地址直接互相访问。
apiVersion: v1
kind: Namespace
metadata:
name: ns1
spec:
network: net1
如果 Namespace 没有 Network 规范,它将使用默认的 Kubernetes 网络模型,包括默认的 kube-proxy。因此,如果用户在关联了 Network 的 Namespace 中创建 Pod,Hypernetes 将遵循 Kubernetes 网络插件模型为此 Pod 设置 Neutron 网络。这是一个高级示例:
{: HyperContainer 封装了一组轻量级应用容器的 Pod}
Hypernetes 使用一个独立的 gRPC handler 名为 kubestack,用于将 Kubernetes Pod 请求转换为 Neutron 网络 API。此外,kubestack 还负责处理另一个重要的网络特性:多租户 Service 代理。
在多租户环境中,默认基于 iptables 的 kube-proxy 无法访问单个 Pod,因为它们被隔离到不同的网络中。取而代之的是,Hypernetes 在每个 HyperContainer 中使用内置的 HAproxy 作为入口。这个 HAproxy 将代理该 Pod 所在 namespace 中的所有 Service 实例。Kube-proxy 将通过遵循标准的 OnServiceUpdate 和 OnEndpointsUpdate 流程来负责更新这些后端服务器,以便用户不会注意到任何区别。这种方法的一个缺点是 HAproxy 必须监听某些特定端口,这可能与用户容器发生冲突。这就是为什么我们计划在下一个版本中使用 LVS 替换此代理。
借助基于 Neutron 的网络插件,Hypernetes Service 能够提供 OpenStack 负载均衡器,就像 GCE 上的“外部”负载均衡器一样。当用户使用外部 IP 创建 Service 时,将创建一个 OpenStack 负载均衡器,并且端点将通过上述 kubestack 工作流自动更新。
持久存储
在考虑存储时,我们实际上是在 Kubernetes 中构建租户感知的持久卷。我们决定不使用现有的 Kubernetes Cinder 卷插件的原因是其模型在虚拟化场景下不起作用。具体来说:
Cinder 卷插件要求 OpenStack 作为 Kubernetes 提供商。
OpenStack 提供商将找到目标 Pod 运行在哪个虚拟机上
Cinder 卷插件将把 Cinder 卷挂载到 Kubernetes 宿主虚拟机内部的某个路径。
kubelet 会将此路径作为卷绑定挂载到目标 Pod 的容器中。
但在 Hypernetes 中,事情变得简单得多。得益于 Pod 的物理边界,HyperContainer 可以像普通虚拟机一样,直接将 Cinder 卷作为块设备挂载到 Pod 中。这种机制消除了在上述现有 Cinder 卷工作流中查询 Nova 以找出目标 Pod 所在虚拟机所需的额外时间。
当前 Hypernetes 中 Cinder 插件的实现基于 Ceph RBD 后端,它的工作方式与其他所有 Kubernetes 卷插件相同,只需要记住事先创建 Cinder 卷(如下面的 volumeID 引用所示)即可。
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
volumeMounts:
- name: nginx-persistent-storage
mountPath: /var/lib/nginx
volumes:
- name: nginx-persistent-storage
cinder:
volumeID: 651b2a7b-683e-47e1-bdd6-e3c62e8f91c0
fsType: ext4
因此,当用户提供包含 Cinder 卷的 Pod yaml 文件时,Hypernetes 将检查 kubelet 是否使用 Hyper 容器运行时。如果是,Cinder 卷可以直接挂载到 Pod,无需任何额外的路径映射。然后,卷元数据将作为 HyperContainer spec 的一部分传递给 Kubelet RunPod 进程。完成!
得益于 Kubernetes 网络和卷的插件模型,即使 HyperContainer 本质上与传统的 Linux 容器不同,我们也能轻松构建上述针对 HyperContainer 的解决方案。在运行时集成完成后,我们还计划遵循 CNI 模型和卷插件标准,向上游 Kubernetes 提案这些解决方案。
我们相信所有这些开源项目都是容器生态系统的重要组成部分,它们的成长在很大程度上取决于 Kubernetes 团队的开源精神和技术愿景。
结论
本文介绍了 HyperContainer 和 Hypernetes 项目的一些技术细节。我们希望人们会对这种新型安全容器及其与 Kubernetes 的集成感兴趣。如果您想尝试 Hypernetes 和 HyperContainer,我们刚刚宣布了我们新的安全容器云服务(Hyper_)的公开 Beta 版,该服务基于这些技术构建。即使您在本地运行,我们也相信 Hypernetes 和 HyperContainer 将使您能够以更安全的方式运行 Kubernetes。