Kubernetes 中的容器运行时接口

原文:Introducing Container Runtime Interface (CRI) in Kubernetes

文中多次出现了个单词 shim,胡翻成代理了,虽然垫片还是比鲁棒啥的好听。。

归根结底,Kubernetes Node 的最底层就是启动和停止容器的组件了,这一部分我们称之为容器运行时( Container Runtim ),这其中最知名的也就是 Docker 了,这一领域正在快速成长,他并不孤独。为了让 Kubernetes 更具扩展性,我们投入了不少精力,在 Kubernetes 中加入了容器运行时插件 API,我们称之为 “CRI”。

CRI 是什么?Kubernetes 需要他么?

每一种容器运行时都有其长处,因此不少用户希望 Kubernetes 能够支持更多的运行时。在 Kubernetes 1.5 中,我们引入了 CRI,这一插件接口让 Kubernetes 无需重新编译就可以使用更多的容器运行时。CRI 包含 Protocol BuffersgRPC API、以及运行库支持,还有尚在开发的标准规范和工具。 CRI 在 Kubernetes 1.5 中发布了 Alpha 版本。

可替代的容器运行时支持是 Kubernetes 中的新概念。在 1.3 时,我们发布了 rktnetes 项目,让 rkt 容器引擎 成为 Docker 之外的又一选择。然而不管是 Docker 还是 rkt,都是用的 Kubelet 的内部接口,同 Kubelet 源码纠缠不清。这种程度的集成,需要对 Kubelet 内部机制有非常深入的了解,还会给社区带来管理压力。这样就给新生代容器运行时造成了难于跨越的集成壁垒。我们用清晰定义的抽象层清除了这一壁垒,让开发者能够专注于容器运行时本身。在通向插件式容器支持以及建设健康生态环境的路上,这是一小步,也是重要的一步。

CRI 概览

Kubelet 使用 gRPC 框架利用 Unix socket 同容器运行时(或者是 CRI 代理)进行通信,这一过程中 Kubelet 是客户端,CRI 代理是服务端。

OVERVIEW

Protocol Buffers API 包含两个 gRPC 服务,ImageServiceRuntimeServiceImageService 提供从仓库拉取镜像、查看和移除镜像的功能。RuntimeService 包含了对 Pod 和容器的生命周期管理、和容器的交互( exec/attach/port-forward )。rtk 和 Docker 这样的容器运行时可以利用一个 Socket 同时提供两个服务。在 Kubelet 中可以用 --container-runtime-endpoint--image-service-endpoint 参数设置这个 socket。

Pod 和容器的生命周期管理

service RuntimeService {
    // Sandbox operations.
    rpc RunPodSandbox(RunPodSandboxRequest) returns (RunPodSandboxResponse) {}
    rpc StopPodSandbox(StopPodSandboxRequest) returns (StopPodSandboxResponse) {}
    rpc RemovePodSandbox(RemovePodSandboxRequest) returns (RemovePodSandboxResponse) {}
    rpc PodSandboxStatus(PodSandboxStatusRequest) returns (PodSandboxStatusResponse) {}
    rpc ListPodSandbox(ListPodSandboxRequest) returns (ListPodSandboxResponse) {}
    // Container operations.
    rpc CreateContainer(CreateContainerRequest) returns (CreateContainerResponse) {}
    rpc StartContainer(StartContainerRequest) returns (StartContainerResponse) {}
    rpc StopContainer(StopContainerRequest) returns (StopContainerResponse) {}
    rpc RemoveContainer(RemoveContainerRequest) returns (RemoveContainerResponse) {}
    rpc ListContainers(ListContainersRequest) returns (ListContainersResponse) {}
    rpc ContainerStatus(ContainerStatusRequest) returns (ContainerStatusResponse) {}
    ...
}

Pod 由一组应用容器组成,其中包含了共有的环境和资源约束。在 CRI 里,这个环境被称为 PodSandbox。我们有意的给容器运行时留下了一些发挥空间,他们可以根据自己的内部实现来解释 PodSandbox。对于 Hypervisor 类的运行时,PodSandbox 会具体化为一个虚拟机。其他的例如 Docker,会是一个 Linux 命名空间。在 v1alpha1 API 中,Kubelet 会创建 Pod 级别的 cgroup 传递给容器运行时,并以此运行所有进程来保障 PodSandbox 对 Pod 的资源保障。

在启动 Pod 之前,Kubelet 调用 RuntimeService.RunPodSandbox 来创建环境。这一过程包括为 Pod 设置网络(分配 IP)。PodSandbox 激活之后,就可以独立的创建、启动、停止和删除不同的容器了。Kubelet 会在停止和删除 PodSandbox 之前首先停止和删除其中的容器。

Kubelet 的职责在于通过 RPC 管理容器的生命周期,实现容器生命周期的钩子,以及存活和健康监测,执行 Pod 的重启策略等。

为什么 CRI 是围绕容器进行的?

Kubernetes 有一个 Pod 资源的接口。我们曾经可能采用的一个 CRI 的设计就是抽象复用 Pod 对象,容器运行时就可以自行实现自己的控制逻辑和状态转换,这样一来,就能极大地简化 API,让 CRI 能够更广泛的适用于多种容器运行时。但是经过深入讨论之后,我们放弃了这一想法。

首先,Kubelet 有很多的 Pod 级功能和机制(例如循环崩溃的处理),交给容器运行时实现的话,会造成很重的负担;第二,更重要的是,Pod 标准还在高速前进。很多的新功能(例如容器初始化)是由 Kubelet 直接管理容器的,而无需容器运行时进行变更。

CRI 选择了围绕容器进行实现,这样容器运行时能够共享这些通用特性,获得更好的开发进度。这并不意味着我们设计哲学的改变 —— Kubelet 要负责保证实际状态和声明状态的一致性。

Exec/attach/port-forward 请求

service RuntimeService {
    ...
    // ExecSync runs a command in a container synchronously.
    rpc ExecSync(ExecSyncRequest) returns (ExecSyncResponse) {}
    // Exec prepares a streaming endpoint to execute a command in the container.
    rpc Exec(ExecRequest) returns (ExecResponse) {}
    // Attach prepares a streaming endpoint to attach to a running container.
    rpc Attach(AttachRequest) returns (AttachResponse) {}
    // PortForward prepares a streaming endpoint to forward ports from a PodSandbox.
    rpc PortForward(PortForwardRequest) returns (PortForwardResponse) {}
    ...
}

Kubernetes 为用户提供了和 Pod 以及其中的容器进行交互的能力(kubectl exec/attach/port-forward)。Kubelet 目前支持两种方式来支持这些功能:调用容器的本地方法,或者使用 Node 上的工具(例如 nsenter 以及 socat)。因为多数工具假设 Pod 利用 Linux namespace 做了隔离,因此使用 Node 上的工具并不是一个可移植的方案。在 CRI 中,我们显式的定义这些调用,让运行时可以做特定实现。

当下还有一个潜在问题是,Kubelet 处理所有的请求连接,所以他有成为 Node 通信瓶颈的可能。在设计 CRI 的时候,我们采纳了一些反馈,让运行时能够排除中间人。容器运行时可以启动一个单独的流服务器处理请求(还能为 Pod 的资源使用进行记录),并把服务器地址返回给 Kubelet。这样 Kubelet 就能反馈信息给 API Server,使之可以直接连接到容器运行时的服务,并连接到客户端。

CRI 还有很多本文没有提及的内容,可以参考设计文档来获得更多内容。

当前状态

虽然 CRI 还比较初级,但也已经有了很多项目在尝试把各种容器运行时纳入 CRI:

如果对这些运行时感兴趣,可以浏览一下他们的仓库,获取最新的进展情况和相关资料。

开发者如果有兴趣集成新的容器运行时,需要阅读开发者指南,会在这里得到 API 中已知的限制和问题。我们乐于从早期开发者这里获取反馈,加以改进来促进项目成长。开发者需要有对突发故障的心理准备,谁让我们还在 Alpha 呢。

尝试新的 CRI Docker

Kubelet 还没有把 CRI 作为缺省选项,我们正在积极促成这一转变。第一步就是用 CRI 的方式重新对 Docker 进行 Kubelet 的集成。在 1.5 中,我们让 Kubelet 开始支持 CRI,还给 Kubelet 加入了内嵌的 Docker CRI 代理。这样 Kubelet 就可以启动 Docker 的 gRPC 服务了。要尝试新的 Kubelet-CRI-Docker 集成,只需要简单的给 API-Server 参数加上 --feature-gates=StreamingProxyRedirects=true 开关,就启用了新的请求重定向特性,然后用 --experimental-cri=true 开关来启动 Kubelet。

虽说现在的实现还有少量功能缺失,不过已经通过了主要的端到端测试,我们计划扩展测试覆盖范围,也再次邀请社区多多提供反馈来促进我们的工作。

Minikube 和 CRI

如果想要测试新功能,却没时间部署新的测试集群。Minikube 让你能够快速的启动一个本地集群。

  • 检查可用的 Kubernetes 版本,选择最新的 1.5.x,这里使用的是 v1.5.9-beta.1:minikube get-k8s-versions
  • 启动一个带有内置 Docker CRI 集成的 Minikube 集群:

    $ minikube start --kubernetes-version=v1.5.0-beta.1  \
    --extra-config=kubelet.EnableCRI=true \
    --network-plugin=kubenet \
    --extra-config=kubelet.PodCIDR=10.180.1.0/24 \
    --iso-url=http://storage.googleapis.com/minikube/iso/buildroot/minikube-v0.0.6.iso
    

--extra-config=kubelet.EnableCRI=true 启用 Kubelet 的 CRI 实现,--network-plugin=kubenet--extra-config=kubelet.PodCIDR=10.180.1.0/24 为网络插件提供了网络设置,分配 PodCIDR 给 Node。这里也可以使用 cni 插件,就无需依赖 PodCIDR 了。--iso-url 给 Minikube 指定一个例子中使用的 ISO 镜像。

  • 检查 Minikube 日志,确认 CRI 的启用

    $ minikube logs | grep EnableCRI
    I1209 01:48:51.150789    3226 localkube.go:116] Setting EnableCRI to true on kubelet.
    
  • 创建一个 Pod 并检查状态,应该会看到 “SandboxReceived” 事件,这表明 Kubelet 正在使用 CRI

    $ kubectl run foo --image=gcr.io/google_containers/pause-amd64:3.0
    deployment "foo" created
    $ kubectl describe pod foo
    ...
    ... From                Type   Reason          Message
    ... -----------------   -----  --------------- -----------------------------
    ...{default-scheduler } Normal Scheduled       Successfully assigned foo-141968229-v1op9 to minikube
    ...{kubelet minikube}   Normal SandboxReceived Pod sandbox received, it will be created.
    ...
    

注意 kubectl attach/exec/port-forward 目前还不能对启用 CRI 模式的 Miniqube 生效,新版本将会加入支持。

相关引用

  • CRIhttps://github.com/kubernetes/kubernetes/blob/242a97307b34076d5d8f5bbeb154fa4d97c9ef1d/docs/devel/container-runtime-interface.md
  • cri-ohttps://github.com/kubernetes-incubator/cri-o
  • rktlethttps://github.com/kubernetes-incubator/rktlet
  • fraktihttps://github.com/kubernetes/frakti
  • Docker CRI 代理https://github.com/kubernetes/kubernetes/tree/release-1.5/pkg/kubelet/dockershim
Avatar
崔秀龙

简单,是大师的责任;我们凡夫俗子,能做到清楚就很不容易了。

comments powered by Disqus
下一页
上一页

相关