docker containerd runc

OCI (Open Container Initiative)

OCI(Open Container Initiative)即开放的容器运行时规范,目的在于定义一个容器运行时及镜像的相关标准和规范,其中包括

  • runtime-spec:容器的生命周期管理,具体参考runtime-spec
  • image-spec:镜像的生命周期管理,具体参考image-spec

实现 OCI 标准的容器运行时有runckata等。

CRI (Container Runtime Interface)

CRI 即容器运行时接口,主要用来定义 k8s 与容器运行时的 API 调用,kubelet 通过 CRI 来调用容器运行时,只要实现了 CRI 接口的容器运行时就可以对接到 k8s 的 kubelet 组件。

image-20240818154100206

Containerd

containerd 是一个开源的容器运行时,可以管理和运行容器,是 Docker 默认使用的容器运行时,并实现了 CRI 规范。
containerd 通过其 CRI 插件实现了 Kubernetes 容器运行时接口(CRI),它可以管理容器的整个生命周期,包括从镜像的传输、存储到容器的执行、监控再到网络。

containerd 支持的 shim?

shim:垫片,一般用来表示对第三方组件 API 调用的适配插件,例如 k8s 使用 Dockershim 来实现对 docker 接口的适配调用。
Containerd 目前官方支持的 shim 清单:

io.containerd.runtime.v1.linux

io.containerd.runtime.v1.linux 是最原始的 shim API 和实现的 v1 版本,在 Containerd 1.0 之前被设计出来。该 shim 使用 runc 来执行容器,并且只支持 cgroup v1。目前 v1 版 shim API 已被废弃,并将于 Containerd 2.0 被删除。

io.containerd.runc.v1

io.containerd.runc.v1io.containerd.runtime.v1.linux 的实现类似,唯一的区别是它使用了 v2 版本 shim API。该 shim 仍然只支持 cgroup v1。

io.containerd.runc.v2

该 shim 与 v1 采用了完全不同的实现,并且使用了 v2 版本 shim API,同时支持 cgroup v1 和 v2。该 shim 进程以运行多个容器,用于 Kubernetes 的 CRI 实现,可以在一个 Pod 中运行多个容器。

io.containerd.runhcs.v1

这是 Windows 平台的 shim,使用 Window 的 HCSv2 API 来管理容器。

CRI-O

CRI-O 是另一个实现了容器运行时接口(CRI)的高级别容器运行时,可以使用 OCI(开放容器倡议)兼容的运行时,它是 containerd 的一个替代品。
CRI-O 诞生于 RedHat、IBM、英特尔、SUSE、Hyper 等公司。它是专门从头开始创建的,作为 Kubernetes 的一个容器运行时,它提供了启动、停止和重启容器的能力,就像 containerd 一样。

RunC

runc(run container)是一个基于 OCI 标准实现的一个轻量级容器运行工具,用来创建和运行容器。而 Containerd 是用来维持通过 runc 创建的容器的运行状态。即 runc 用来创建和运行容器,containerd 作为常驻进程用来管理容器。
runc 包含 libcontainer,包括对 namespace 和 cgroup 的调用操作。
安装 docker 后,会自动安装 runc,linux 上可以直接运行 runc 命令。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
root@devops:/usr/bin# runc
NAME:
runc - Open Container Initiative runtime

runc is a command line client for running applications packaged according to
the Open Container Initiative (OCI) format and is a compliant implementation of the
Open Container Initiative specification.

runc integrates well with existing process supervisors to provide a production
container runtime environment for applications. It can be used with your
existing process monitoring tools and the container will be spawned as a
direct child of the process supervisor.

Containers are configured using bundles. A bundle for a container is a directory
that includes a specification file named "config.json" and a root filesystem.
The root filesystem contains the contents of the container.

To start a new instance of a container:

# runc run [ -b bundle ] <container-id>

Where "<container-id>" is your name for the instance of the container that you
are starting. The name you provide for the container instance must be unique on
your host. Providing the bundle directory using "-b" is optional. The default
value for "bundle" is the current directory.

USAGE:
runc [global options] command [command options] [arguments...]

VERSION:
1.7.19
commit: v1.1.13-0-g58aa920
spec: 1.0.2-dev
go: go1.21.12
libseccomp: 2.5.5

COMMANDS:
checkpoint checkpoint a running container
create create a container
delete delete any resources held by the container often used with detached container
events display container events such as OOM notifications, cpu, memory, and IO usage statistics
exec execute new process inside the container
kill kill sends the specified signal (default: SIGTERM) to the container's init process
list lists containers started by runc with the given root
pause pause suspends all processes inside the container
ps ps displays the processes running inside a container
restore restore a container from a previous checkpoint
resume resumes all processes that have been previously paused
run create and run a container
spec create a new specification file
start executes the user defined process in a created container
state output the state of a container
update update container resource constraints
features show the enabled features
help, h Shows a list of commands or help for one command

GLOBAL OPTIONS:
--debug enable debug logging
--log value set the log file to write runc logs to (default is '/dev/stderr')
--log-format value set the log format ('text' (default), or 'json') (default: "text")
--root value root directory for storage of container state (this should be located in tmpfs) (default: "/run/runc")
--criu value path to the criu binary used for checkpoint and restore (default: "criu")
--systemd-cgroup enable systemd cgroup support, expects cgroupsPath to be of form "slice:prefix:name" for e.g. "system.slice:runc:434234"
--rootless value ignore cgroup permission errors ('true', 'false', or 'auto') (default: "auto")
--help, -h show help
--version, -v print the version

Docker

从 Docker 1.11 版本开始,Docker 容器运行就不是简单通过 Docker Daemon 来启动了,而是通过集成 containerd、runc 等多个组件来完成的。

image-20240818151314490

每一个 Containerd 或 Docker 容器都有一个相应的 shim 守护进程,这个守护进程会提供一个 API,Containerd 使用该 API 来管理容器基本的生命周期(启动/停止)。shim 还有一个作用是向 Containerd 报告容器的退出状态,在容器退出状态被 Containerd 收集之前,shim 会一直存在。这一点和僵尸进程很像,僵尸进程在被父进程回收之前会一直存在,只不过僵尸进程不会占用资源,而 shim 会占用资源。

shim 将 Containerd 进程从容器的生命周期中分离出来,具体的做法是 runc 在创建和运行容器之后退出,并将 shim 作为容器的父进程,即使 Containerd 进程挂掉或者重启,也不会对容器造成任何影响。这样做的好处,可以在线升级或者重启 Containerd,不会对运行中的容器产生任何影响。Docker 的 –live-restore 特征也实现了类似的功能。

docker 和 k8s 管理容器的关系图

关系流程如下

  • Docker,Kubernetes 等工具来运行一个容器时会调用容器运行时(CRI)比如 containerd,CRI-O

  • 通过容器运行时来完成容器的创建、运行、销毁等实际工作

  • Docker 使用的是 containerd 作为其运行时;Kubernetes 支持 containerd,CRI-O 等多种容器运行时

  • 这些容器运行时都遵循了 OCI 规范,并通过 runc 来实现与操作系统内核交互来完成容器的创建和运行

image-20240818154926633