Kubernetes 开源容器平台结构简介.

经典结构图

containerd & kubernetes & runc

角色分工和层级

  • Kubernetes:部署容器的平台,属于最上层与用户交互的角色。

    • 主要承担的功能:负责集群成员管理,以及自动化的 Pod 的调度维护。
  • containerd:容器平台(Docker、Kubernetes)的低一层,底层运行时(runc、kata)的高一层,属于中间负责 Pod 级别管理的角色。

    • 主要承担的功能:镜像管理(镜像导入导出删除)、容器管理(容器创建删除停止),文件系统的快照管理。
  • runc:底层运行时,属于直接与底层操作系统交互的角色;类似的 runtime 还有 kata、Firecracker、gVisor 等,用于不同的操作系统平台且遵循 OCI 规范。

    • 主要承担的功能:根据镜像配置,使用系统提供的 cgroups 之类的资源隔离组件,为不同的容器创建运行环境,然后调起 endpoint。
  • containerd 和 kubernetes 都通过 systemd 维护进程,二者之间通过 gRPC 进行通信(遵循 OCI 规范)

  • containerd 和 runc 是直接通过进程调用的方式进行交互和绑定。大白话就是 containerd 为每个容器开了一个 shim 进程;然后 shim 进程拼了一个 runc 命令跑容器,并作为主进程接管所有的 runc 僵尸进程,然后监控容器的状态。

从 kubernetes 到 runc 的流程

  • kuberlet 通过 gRPC 向 containerd 发送命令调用
    • 以前是 kuberlet 调用 CRI-containerd,然后 CRI-containerd 再调用 contaienrd;后来 cri 直接作为一个插件集成进了 containerd,调用链缩短为了 kubelet -> containerd -> runc
    • 源码也可以看到,cri 现在在 containerd 的内部是作为插件进行加载的。
  • containerd 收到请求,创建 containerd-shim 实例
    • containerd-shim 实例才会真正操作容器,负责管理一个容器的整个声明周期,并且对其状态进行监控和上报;容器进程需要一个父进程来做状态收集、维持 stdinfd 打开等工作,假如这个父进程就是 containerd,那如果 containerd 挂掉的话,整个宿主机上所有的容器都得退出了,引入 containerd-shim 可以规避这个问题。
  • 创建 shim 进程(通过 runtime/shimnewInit() 初始化)
    • newInit() 中会调用 process.NewRunc() 创建新的 go-runc 实例,然后 go-runc 去拼 runc 命令并执行
  • runc 启动容器,然后 runc 本身会直接退出,containerd-shim 则会接管容器进程,并负责上报容器进程的状态给 containerd。同时开 subReaper 在容器中 pid1 的进程退出后接管容器中的子进程进行清理, 确保无僵尸进程。
  • 创建容器的 RPC 调用流程
  • cmd\ctr\commands\tasks\tasks_unix.go NewTask() 调用 container.NewTask()
  • container.NewTask() c.client.TaskService().Create(ctx, request)
    • 创建一个 GRPC 客户端然后进行通信

协议规范

CRI(Container Runtime Interface 容器运行时接口)

CRI 是由 Kubernetes 定义的一套对接容器运行时的接口标准,然后 containerd(或者 dockerdhim)等容器运行时会有一套自己的实现。目前官方主流提供 Kubernetes + containerd 的方案,即 containerd 内有一个 CRI 插件实现,提供给 Kubernetes 进行调用。

可以在 Kubernetes 的配置中指定不同的 CRI 实现组件,来达到对接不同的容器运行时的目的。

OCI(Open Container Initiative 开放容器倡议)

围绕容器镜像和运行时创建指定的开放行业标准,维护容器镜像格式的规范,以及容器应该如何运行。也相当与一套标准的接口,然后不同的平台(如 Linux、Windows)上面有不同的组件实现。

CNI(Container Network Interface 容器网络接口)

由一组用于配置 Linux 容器的网络接口的规范和库组成,同时还包含了一些插件。CNI 只提供了两个接口:容器创建分配网络资源、容器删除释放网络资源。

CSI(Container Storage Interface 容器存储接口)

主要用于 Kubernetes 中,旨在能为容器编排引擎和存储系统间建立一套标准的存储调用接口,为容器编排引擎提供存储服务。

开源组件实现

containerd

开源的容器运行时之一,从 docker 中的 libcontainerd 子项目发展而来;目前是 Kubernetes 社区官方使用的 runtime。

CRI-O

开源的容器运行时之一(可能等价为另一种类似 containerd 的中间件),由 RedHat、IBM、英特尔、SUSE、Hyper 等公司从头开始创建的另一套 runtime。

runc

Linux 平台上最底层的 OCI 组件实现,用来根据镜像创建容器的运行环境并拉起容器进程,功能上与 cgroups 和 systemd 高度绑定。

其他子组件

containerd-shim

containerd-shim 是 containerd 的一个子组件。实际的运行环境中的 contaienrd 进程只是充当了一个管理角色(一个节点只有一个),而 containerd-shim 才是真正创建和操作容器的组件,每一个容器都会对应一个 containerd-shim 作为父进程。它的主要职责是做容器的状态收集、维持 stdinfd 等操作。

containerd-shim 有 v1 和 v2 两个版本,在实现上有着非常大的差异;目前官方版本已经在淘汰 v1 版本。

dockershim

dockershim 和 containerd-shim 承担的角色完全不同。dockershim 是 Kubernetes 为了让 docker 的接口适配 OCI 规范而构建的适配器。

目前市面上以 docker 作为 endpoint 的 Kubernetes 集群环境还有很多,所以 dockershim 相关的信息还是很常见。