docker · 2024-07-07 0

RunC 使用

一、RunC

RunC 是一个轻量级的工具,它是用来运行容器的,它就是个命令行工具(CLI),可以不用通过 docker 引擎,直接创建、启动、停止容器等。

RunC 是标准化的产物,它根据 OCI (Open Container Initiative) 标准来创建和运行容器。RunC 是一个低级的容器运行时。容器运行时是负责容器进程的创建、启动、停止、移动和删除的管理工具。它提供了一种标准化的方式来打包、分发和运行应用程序,使得应用程序可以在不同的环境中可靠地运行。

自从 2013 年 docker 发布之后,docker 项目逐渐成为了一个庞然大物。为了能够降低项目维护的成本,内部代码能够回馈社区,他们于 2014 年开源了 libcontainer,docker 公司将 libcontainer 的实现移动到 runC 并捐赠给了 OCI。在 2016 年,docker 开源并将 containerd 捐赠给了 CNCF。

OCI (open container Initiative) 容器规范:

  • Runtime Spec
  • Image format Spect
ctr cli ---grpc---> containerd ---exec---> containerd-shim-runc ---exec---> runc
crictl 与 kubelet ---CRI---> containerd ---exec---> containerd-shim-runc ---exec---> runc

RunC 希望提供一个“ OCI 包”,它只是一个根文件系统和一个 config.json 文件。而不是 Podman 或 Docker 那样有“镜像”概念,所以不能只执行 runc run nginx:latest 这样来启动一个容器。

Runc 符合 OCI 规范(具体来说,是 runtime-spec ),这意味着它可以使用 OCI 包并从中运行一个容器。值得重申的是,这些 bundle 并不是“容器镜像”,它们要简单得多。层、标签、容器注册表和存储库等功能,所有这些都不是 OCI 包甚至运行时规范的一部分。

根据 OCI Runtime 规范,Linux ABI 下的应用程序会期望 Linux 环境提供以下特殊的文件系统:

  • /proc 文件夹,挂载 proc 文件系统
  • /sys 文件夹,挂载 sysfs 文件系统
  • /dev/pts 文件夹,挂载 devpts 文件系统
  • /dev/shm 文件夹,挂载 tmpfs 文件系统

二、容器状态

OCI 都定义了哪几种容器状态:

  • creating:使用 create 命令创建容器,这个过程称为创建中。
  • created:容器已经创建出来,但是还没有运行,表示镜像文件和配置没有错误,容器能够在当前平台上运行。
  • running:容器里面的进程处于运行状态,正在执行用户设定的任务。
  • stopped:容器运行完成,或者运行出错,或者 stop 命令之后,容器处于暂停状态。这个状态,容器还有很多信息保存在平台中,并没有完全被删除。
  • paused:暂停容器中的所有进程,可以使用 resume 命令恢复这些进程的执行。

三、准备 OCI bundle

RunC 是运行容器的运行时,它负责利用符合标准的文件等资源运行容器,但是它不包含 docker 那样的镜像管理功能。

所以要用 runC 运行容器,我们先得准备好容器的文件系统。所谓的 OCI bundle 就是指容器的文件系统和一个 config.json 文件。

有了容器的文件系统后我们可以通过 runc spec 命令来生成 config.json 文件。使用 docker 可轻松的生成容器的文件系统。

准备一个运行 busybox 容器所需的文件系统:

zxm@zxm-pc:~$ docker pull busybox:1.35.0
zxm@zxm-pc:~$ docker create --name busybox_1 busybox:1.35.0
zxm@zxm-pc:~$ docker ps -a
CONTAINER ID   IMAGE            COMMAND                   CREATED         STATUS                      PORTS      NAMES
b3d365c57f04   busybox:1.35.0   "sh"                      3 seconds ago   Created                                busybox_1
zxm@zxm-pc:~$ docker export busybox_1 -o busybox_1.tar
zxm@zxm-pc:~$ mkdir -p mycontainer/rootfs/
zxm@zxm-pc:~$ tar -xvf busybox_1.tar -C mycontainer/rootfs/

现在 rootfs 目录下就是 busybox 镜像的文件系统,然后使用 runc spec 生成 config.json 文件:

zxm@zxm-pc:~$ ls mycontainer/rootfs/
bin  dev  etc  home  lib  lib64  proc  root  sys  tmp  usr  var
zxm@zxm-pc:~/mycontainer$ ls
rootfs
zxm@zxm-pc:~/mycontainer$ runc spec
zxm@zxm-pc:~/mycontainer$ ls
config.json  rootfs

如果直接使用生成的 config.json,接下来的演示不会太流畅,所以简单起见,我们稍微修改一下刚刚生成的 config.json 文件。就是把 "terminal": true 改为 false,把 "args": ["sh"] 改为 "args": ["sleep", "30"]:

zxm@zxm-pc:~/mycontainer$ cat config.json 
{
    "ociVersion": "1.0.2-dev",
    "process": {
        "terminal": false,
        "user": {
            "uid": 0,
            "gid": 0
        },
        "args": [
            "sleep", "30"
        ],
        ...
        ...
    }
}

四、RunC 命令

1.创建容器

使用 create 命令创建容器

sudo runc create <container_name>
zxm@zxm-pc:~/mycontainer$ sudo runc create mybusybox

2.查看容器

使用 list 命令查看当前存在的容器

zxm@zxm-pc:~/mycontainer$ sudo runc list
ID          PID         STATUS      BUNDLE                  CREATED                          OWNER
mybusybox   12475       created     /home/zxm/mycontainer   2024-07-07T09:04:06.921387816Z   root

2.查看状态

使用 state 命令查看容器的状态

zxm@zxm-pc:~/mycontainer$ sudo runc state mybusybox
{
  "ociVersion": "1.0.2-dev",
  "id": "mybusybox",
  "pid": 12475,
  "status": "created",
  "bundle": "/home/zxm/mycontainer",
  "rootfs": "/home/zxm/mycontainer/rootfs",
  "created": "2024-07-07T09:04:06.921387816Z",
  "owner": ""
}

3.查看容器内进程

使用 ps 命令看看容器内运行的进程

zxm@zxm-pc:~/mycontainer$ sudo runc ps mybusybox
UID          PID    PPID  C STIME TTY          TIME CMD
root       12475    1538  0 17:04 ?        00:00:00 runc init

ps 查看:

zxm@zxm-pc:~$ ps -ef
zxm         1538       1  0 7月07 ?       00:00:01 /lib/systemd/systemd --user
root       12475    1538  0 7月07 ?       00:00:00 runc init
...

3.启动容器任务

使用 start 命令执行容器中定义的任务

容器状态变为 running,过段时间变为 stopped

zxm@zxm-pc:~/mycontainer$ sudo runc start mybusybox
zxm@zxm-pc:~/mycontainer$ sudo runc ps mybusybox
UID          PID    PPID  C STIME TTY          TIME CMD
root       12475    1538  0 17:04 ?        00:00:00 sleep 30
zxm@zxm-pc:~/mycontainer$ sudo runc state mybusybox
{
  "ociVersion": "1.0.2-dev",
  "id": "mybusybox",
  "pid": 12475,
  "status": "running",
  "bundle": "/home/zxm/mycontainer",
  "rootfs": "/home/zxm/mycontainer/rootfs",
  "created": "2024-07-07T09:04:06.921387816Z",
  "owner": ""
}
zxm@zxm-pc:~/mycontainer$ sudo runc state mybusybox
{
  "ociVersion": "1.0.2-dev",
  "id": "mybusybox",
  "pid": 0,
  "status": "stopped",
  "bundle": "/home/zxm/mycontainer",
  "rootfs": "/home/zxm/mycontainer/rootfs",
  "created": "2024-07-07T09:04:06.921387816Z",
  "owner": ""
}

ps 查看:

zxm@zxm-pc:~$ ps -ef
zxm         1538       1  0 7月07 ?       00:00:01 /lib/systemd/systemd --user
root       12475    1538  0 7月07 ?       00:00:00 sleep 30
...

4.执行容器命令

使用 exec 命令在容器中执行命令

zxm@zxm-pc:~/mycontainer$ sudo runc exec mybusybox ls
bin
dev
etc
home
lib
lib64
proc
root
sys
tmp
usr
var

5.删除容器

使用 delete 命令删除容器

zxm@zxm-pc:~/mycontainer$ sudo runc list
ID          PID         STATUS      BUNDLE                  CREATED                          OWNER
mybusybox   0           stopped     /home/zxm/mycontainer   2024-07-07T09:04:06.921387816Z   root
zxm@zxm-pc:~/mycontainer$ sudo runc delete mybusybox
zxm@zxm-pc:~/mycontainer$ sudo runc list
ID          PID         STATUS      BUNDLE      CREATED     OWNER

6.创建并运行容器

使用 run 命令创建并运行容器,当容器中的命令退出后容器随即被删除。

zxm@zxm-pc:~/mycontainer$ sudo runc run mybusybox

7.停止容器中的任务

使用 kill 命令停止容器中的任务

zxm@zxm-pc:~$ sudo runc kill mybusybox

默认它会优雅的结束容器中的进程,但是碰到特殊情况,你就得使用终极信号 9:

zxm@zxm-pc:~$ sudo runc kill mybusybox 9

8.暂停容器进程

执行 pause 命令后,容器的状态由 running 变成了 paused

zxm@zxm-pc:~$ sudo runc pause mybusybox

9.恢复容器进程

通过 resume 命令恢复容器中进程的执行,此时容器的状态又恢复到了 running

zxm@zxm-pc:~$ sudo runc resume mybusybox