本文致力于梳理containerd的架构与运行原理。
参考文章:https://github.com/containerd/containerd/blob/main/core/runtime/v2/README.md
https://www.cnblogs.com/zhangmingcheng/p/17524721.html
核心运行思路
containerd
使用Runtime v2
并引入shim API
,使得containerd
可以和很多运行时集成。基于此,contaienrd
仅仅作为协调下发容器配置、容器数据的高级管理器,实际由低级运行时启停管理容器。
举例:kubernetes
调用containerd
创建容器,containerd
部署容器依赖的文件系统与提供必要配置信息,contained
通知低级运行时启动pod
。
实现模式:
- 单个二进制运行时,同时提供服务监听、启停等容器操作。
- 提供一个分离的、只负责服务监听的shim垫片程序,其调用对应的单独的运行时引擎以启停容器等操作。
使用第二个模式可以做到更轻松的集成对各种容器运行时(仅需其符合OCI运行时规范),即可通过一个runtime shim
处理ttRPC协议请求,便于同时使用多个不同的容器运行时引擎(Runtime Class)。
所以第二个模式也是默认模式,即runc
作为默认运行时引擎,containerd
调用containerd-shim-runc-v2
垫片告知runc
引擎对容器的启停等操作。
垫片与引擎
垫片由containerd
调用,主要为containerd
提供通信端口和配置信息,其在被启动时可以指定极少的参数,且是作为守护进程。containerd-shim
启动containerd-shim-runc-v2
后立即退出,containerd-shim-runc-v2
父进程就成为了systemd(1)
,这样containerd-shim-runc-v2
就和containerd
脱离了关系,即便containerd
退出也不会影响到容器。
垫片在socket
监听来自containerd
的ttRPC
命令,而后通过fork
/exec
调用引擎以启停容器等操作。如shim
(io.containerd.runc.v2)调用runc
。
引擎如runc
负责启停容器等操作,containerd
默认提供了containerd-shim-runc-v2
垫片以调用runc
,runc
再通过系统接口调用libcontainer
启停容器等。
由于每个 shim 实例都作为守护进程与 containerd 进行通信,同时通过调用独立运行时来为容器建立父子关系,因此可以为多个容器和调用使用一个 shim。例如,您可以让一个containerd-shim-runc-v2容器与一个容器进行通信,并且它可以调用十个不同的容器。
垫片与容器可以是一对一或一对多的关系,常常Pod就是一对多。
流程:containerd
收到创建容器的请求,containerd
布置容器的文件系统,并创建必要的容器配置信息,containerd
调用shim
,包括容器配置,它使用该信息来决定是否启动新的套接字侦听器(1:1 shim 到容器)或使用现有的侦听器(1:many).如果存在,则返回现有套接字的地址并退出;如果是新的,垫片创建一个新进程来在套接字上侦听来自 containerd
的ttRPC
命令,返回该套接字的地址给containerd
后退出.containerd
向shim
发送启动容器的命令,shim
调用runc
来创建/启动/停止容器。