Contents
概述
Containerd 在 release1.5 之后内置了 cri。通过暴露 CRIService 供 kubelet 调用。CRI 的封装并不复杂,都是利用了 containerd 本身的功能模块。通过 CRI 管理 pod 主要分为三个模块:
- Sandbox 的管理:RunPodSandbox、StopPodSandbox、RemovePodSandbox、PodSandboxStatus、ListPodSandbox
- Container 的管理:CreateContainer、StartContainer、StopContainer、RemoveContainer、ListContainers、UpdateContainerResources、ContainerStats、ListContainerStats、UpdateRuntimeConfig、Status
- 容器其他方面的管理:ReopenContainerLog、ExecSync、Exec、Attach、PortForward
Sandbox 的管理
Sandbox 和 Container 在实现上类似,通过启动一个特殊的 pause 容器来创建一个沙箱环境。通常情况下,这个沙箱环境具有和主机隔离的 pid,uts,mount,network,ipc,user。
以 RunPodSandbox 为例,containerd 会执行以下操作:
- Containerd CRI 在 RunPodSandbox 时,会先保证 sandbox 的镜像是否存在。如果不存在则会进行 pull 操作。
- 创建 pod network namespace,并调用 CNI
- 使用 container task 来创建容器
- 启动 container task,也就是执行二进制文件 pause
- 更新 sandbox 的状态,包括 pid 置为 task pid,state 置为 ready 以及更新创建时间。
- 在新的 goroutine 中启动 sandbox exit monitoring。这样就可以在 sandbox 进程退出时,执行清理工作。然后更新 sandbox 的状态。
以 StopPodSandbox 为例,containerd 会执行以下操作:
- 使用 containerStore 来 list 出所有 container,并使用 container.SandboxID 过滤出属于当期 sandbox 的 container
- 依次停止 sandbox 下的 container
- 清理 sandbox 的文件,比如 unmount dev shm
- 如果 sandbox container(pause) 的状态是 Ready 或 Unknown,使用 SiGKILL 终止 sandbox container
- 调用 cni 清理 network namespace,然后移除。
Container 管理
在 sandbox 创建好之后,kubelet 就可以通过 CRI 来创建 container 了。CreateContainer 的逻辑如下:
- 获取 sandbox 的信息,后面创建的容器需要使用和 sandbox 同样的 runtime,namespace。
- 获取 container mounts,包括
/etc/hosts
,/etc/resolv.con
,dev shm
。然后设置 - 设置 container log path。
- 设置 container io,包括 stdin, stdout, stderr, terminal。
- 在 container store(metadata) 中创建容器记录。此时 container 处于 CREATED 状态。
创建好 container 后,调用 StartContainer 即可运行容器。步骤如下:
- 更新 container 状态为 Running,防止重复 start。
- 设置 container stdout,stderr 到 log 文件上。
- 启动 task 来运行 container 进程
- 更新 container status 的 Pid 和 StartedAt
- 在新的 goroutine 中启动 sandbox exit monitoring 来监控 container 的进程状态。
StopContainer 的步骤如下:
- 如果 container 设置了 stop timeout,使用 task.Kill 发送 SIGTERM 信号。然后等待 timeout 时间
- 使用 task.Kill 发送 SIGKILL 信号。
RemoveConrtainer 的步骤如下:
- 如果当前 container 处于 RUNNING 或者 UNKNOWN 状态,则使用 timeout 为 0 的 stopContainer 来强制停止容器。
- 设置 container 的状态为 removing。
- 从 store 中删除 container,checkpoint 以及一些缓存信息。
容器管理
Containerd CRI 除了实现了 sandbox 和 container 的生命周期管理,也提供了对容器其他方面的管理。比如:
- 在 kubelet 对 logfile rotate 之后,调用 ReopenContainerLog 来将 container log 输出到新的日志文件中
- 通过 GRPC 提供 GetAttach,返回 attach http endpoint 和 token,供 kubelet 通过 http stream 连接到 process 的 stdin,stdout 和 stderr 上。
- 通过 GRPC 提供 GetExec,返回 exec http endpoint 和 token,供 kubelet 通过 http stream 在容器命名空间内执行命令。
- 通过 GRPC 提供 GetPortforward,返回 portforward http endpoint 和 token。kubelet 通过 http stream 连接上后,使用 netns 在 sandbox network namespace 下 dial 容器内的端口,使用 io.Copy 转发输入输出流。