Container Runtime Interface (CRI)

容器运行时插件(Container Runtime Interface,简称 CRI)是 Kubernetes v1.5 引入的容器运行时接口,它将 Kubelet 与容器运行时解耦,将原来完全面向 Pod 级别的内部接口拆分成面向 Sandbox 和 Container 的 gRPC 接口,并将镜像管理和容器管理分离到不同的服务。

运行时插件 CRI - 图1

CRI 最早从从 1.4 版就开始设计讨论和开发,在 v1.5 中发布第一个测试版。在 v1.6 时已经有了很多外部容器运行时,如 frakti 和 cri-o 等。v1.7 中又新增了 cri-containerd 支持用 Containerd 来管理容器。

采用 CRI 后,Kubelet 的架构如下图所示:

image-20190316183052101

CRI 接口

CRI 基于 gRPC 定义了 RuntimeService 和 ImageService 等两个 gRPC 服务,分别用于容器运行时和镜像的管理。其定义在

Kubelet 作为 CRI 的客户端,而容器运行时则需要实现 CRI 的服务端(即 gRPC server,通常称为 CRI shim)。容器运行时在启动 gRPC server 时需要监听在本地的 Unix Socket (Windows 使用 tcp 格式)。

开发 CRI 容器运行时

开发新的容器运行时只需要实现 CRI 的 gRPC Server,包括 RuntimeService 和 ImageService。该 gRPC Server 需要监听在本地的 unix socket(Linux 支持 unix socket 格式,Windows 支持 tcp 格式)。

一个简单的示例为

  1. import (
  2. // Import essential packages
  3. "google.golang.org/grpc"
  4. runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
  5. )
  6. // Serivice implements runtime.ImageService and runtime.RuntimeService.
  7. type Service struct {
  8. ...
  9. }
  10. func main() {
  11. service := &Service{}
  12. s := grpc.NewServer(grpc.MaxRecvMsgSize(maxMsgSize),
  13. grpc.MaxSendMsgSize(maxMsgSize))
  14. runtime.RegisterRuntimeServiceServer(s, service)
  15. runtime.RegisterImageServiceServer(s, service)
  16. lis, err := net.Listen("unix", "/var/run/runtime.sock")
  17. if err != nil {
  18. logrus.Fatalf("Failed to create listener: %v", err)
  19. }
  20. go s.Serve(lis)
  21. // Other codes
  22. }

对于 Streaming API(Exec、PortForward 和 Attach),CRI 要求容器运行时返回一个 streaming server 的 URL 以便 Kubelet 重定向 API Server 发送过来的请求。在 v1.10 及更早版本中,容器运行时必需返回一个 API Server 可直接访问的 URL(通常跟 Kubelet 使用相同的监听地址);而从 v1.11 开始,Kubelet 新增了 --redirect-container-streaming(默认为 false),默认不再转发而是代理 Streaming 请求,这样运行时可以返回一个 localhost 的 URL(当然也不再需要配置 TLS)。

image-20190316183005314

详细的实现方法可以参考 dockershim 或者 cri-o

Kubelet 配置

在启动 kubelet 时传入容器运行时监听的 Unix Socket 文件路径,比如

  1. kubelet --container-runtime=remote --container-runtime-endpoint=unix:///var/run/runtime.sock --image-service-endpoint=unix:///var/run/runtime.sock

容器运行时

CRI 容器运行时 维护者 主要特性 容器引擎
Dockershim Kubernetes 内置实现、特性最新 docker
cri-o Kubernetes OCI标准不需要Docker OCI(runc、kata、gVisor…)
cri-containerd Containerd 基于 containerd 不需要Docker OCI(runc、kata、gVisor…)
Frakti Kubernetes 虚拟化容器 hyperd、docker
rktlet Kubernetes 支持rkt rkt
PouchContainer Alibaba 富容器 OCI(runc、kata…)
Virtlet Mirantis 虚拟机和QCOW2镜像 Libvirt(KVM)

目前基于 CRI 容器引擎已经比较丰富了,包括

  • Docker: 核心代码依然保留在 kubelet 内部(pkg/kubelet/dockershim),是最稳定和特性支持最好的运行时
  • OCI 容器运行时:
    • 社区有两个实现
    • 支持的 OCI 容器引擎包括
      • runc:OCI 标准容器引擎
      • gVisor:谷歌开源的基于用户空间内核的沙箱容器引擎
      • Clear Containers:Intel 开源的基于虚拟化的容器引擎
      • Kata Containers:基于虚拟化的容器引擎,由 Clear Containers 和 runV 合并而来
  • PouchContainer:阿里巴巴开源的胖容器引擎
  • Frakti:支持 Kubernetes v1.6+,提供基于 hypervisor 和 docker 的混合运行时,适用于运行非可信应用,如多租户和 NFV 等场景
  • Rktlet:支持 rkt 容器引擎(rknetes 代码已在 v1.10 中弃用)
  • Virtlet:Mirantis 开源的虚拟机容器引擎,直接管理 libvirt 虚拟机,镜像须是 qcow2 格式
  • Infranetes:直接管理 IaaS 平台虚拟机,如 GCE、AWS 等

Containerd

以 Containerd 为例,在 1.0 及以前版本将 dockershim 和 docker daemon 替换为 cri-containerd + containerd,而在 1.1 版本直接将 cri-containerd 内置在 Containerd 中,简化为一个 CRI 插件。

运行时插件 CRI - 图4

Containerd 内置的 CRI 插件实现了 Kubelet CRI 接口中的 Image Service 和 Runtime Service,通过内部接口管理容器和镜像,并通过 CNI 插件给 Pod 配置网络。

运行时插件 CRI - 图5

RuntimeClass

RuntimeClass 是 v1.12 引入的新 API 对象,用来支持多容器运行时,比如

  • Kata Containers/gVisor + runc
  • Windows Process isolation + Hyper-V isolation containers

RuntimeClass 表示一个运行时对象,在使用前需要开启特性开关 RuntimeClass,并创建 RuntimeClass CRD:

  1. kubectl apply -f https://github.com/kubernetes/kubernetes/tree/master/cluster/addons/runtimeclass/runtimeclass_crd.yaml

然后就可以定义 RuntimeClass 对象

  1. apiVersion: node.k8s.io/v1alpha1 # RuntimeClass is defined in the node.k8s.io API group
  2. kind: RuntimeClass
  3. metadata:
  4. name: myclass # The name the RuntimeClass will be referenced by
  5. # RuntimeClass is a non-namespaced resource
  6. spec:
  7. runtimeHandler: myconfiguration # The name of the corresponding CRI configuration

而在 Pod 中定义使用哪个 RuntimeClass:

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: mypod
  5. spec:
  6. runtimeClassName: myclass
  7. # ...

参考文档