1 介绍

1.1 什么是OCI

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

  • runtime-spec:容器的生命周期管理,包括容器的创建、启动、暂停、恢复和销毁等操作。规定了容器运行时应该如何使用 Linux 内核功能来实现进程隔离、资源限制和文件系统挂载等。还规定了容器配置的格式(config.json),这个文件描述了容器的元数据、根文件系统、网络设置、进程信息等。具体参考 runtime-spec
  • image-spec:定义了容器镜像的格式和内容布局,描述了镜像的层次结构、元数据格式、依赖关系、配置选项等。确保不同的镜像管理工具(例如 Docker、Podman、containerd)都可以按照同一标准构建、分发和运行容器镜像。具体参考 image-spec

1.2 什么是runc

runc 是一个开源的 CLI 工具,用于创建和运行基于 OCI 标准的容器,它是 Docker、containerd 等容器运行时的底层实现,专门用于容器的创建、启动、停止和销毁,设计目标是为容器提供一个清亮且独立的运行时环境。

2 容器的生命周期

在 runc 里面,只有四种状态:createdrunningpausedstopped 四种状态,在本文章中添加了两种状态 initcreating 是为了更好的理解 runc 的生命周期转换过程。

2.1 init

  • 容器在创建过程中首先会进入 init 状态。
  • 当通过 runc create 初始化容器时,runc 会生成一个新的容器实例,并将其设置为 init 状态。在这个状态下,容器的命名空间、cgroup 资源隔离等还没有完全准备好。
  • 这是容器最初的状态,在 init 状态之后会进入 creating 状态。

2.2 creating

  • 容器从 init 过渡到 creating 状态。
  • 在这个状态,runc 会根据 config.json 配置文件进行命名空间、cgroup 、网络等资源的创建和初始化。
  • 容器还未启动主进程,但所有的隔离环境都已准备好,处于等待启动的状态。

2.3 created

  • 容器已经完成初始化,处于 created 状态。
  • 在这个状态下,容器已经分配了所有资源,但还没有执行容器内的任何进程。
  • 可以理解为“预启动”状态,用户可以在这个状态下做一些启动前的检查或操作。

2.4 running

  • 当容器内的主进程启动后,容器会进入 running 状态。
  • 容器的 running 状态意味着主进程正在执行,容器处于活跃状态。
  • 容器的资源限制(如 CPU、内存)开始生效,用户可以与容器内部的进程交互。

2.5 paused

  • 通过 runc pause 命令可以将容器置于 paused 状态。
  • 容器内部的所有进程会被挂起,不再消耗 CPU 时间,所有的 IO 操作也会暂停。
  • 这通常用于临时暂停容器以释放 CPU 资源,稍后可以通过 runc resume 恢复容器的运行。

2.6 stopped

  • 当容器内的主进程退出或被杀死后,容器进入 stopped 状态。
  • 容器内已经没有任何进程在运行,所有的资源(cgroup、命名空间)仍然保留,但不会再消耗系统资源。
  • 容器可以在 stopped 状态下被重新启动(如果使用的是某些运行时),也可以被删除。

3 配置容器环境

3.1 制作根文件系统

在 runc 中,根文件系统是指容器运行时的文件系统环境,它是容器内部进程的根目录。容器的根文件系统与主机系统的根文件系统是隔离的,可以为容器提供一个独立的文件系统视图,从而确保容器内部进程的独立性和安全性。

  1. busybox官网 下载文件系统,将文件系统的内容解压到 rootfs 目录下。
  2. 使用 Docker 下载文件系统,将文件系统的内容放到 rootfs 目录下。
docker pull busybox
mkdir bundle && cd bundle
mkdir rootfs
docker export $(docker create busybox) | tar -C rootfs -xvf -

3.2 编写容器配置

使用下面命令创建一个 config.json 文件:

runc spec
展示文件内容
{
  // OCI 规范版本,用于指定该配置文件的格式和内容遵循的标准。
  "ociVersion": "1.2.0",

  // 容器中要运行的进程的配置。
  "process": {
    // 是否为进程分配伪终端(TTY),用于交互式操作。
    "terminal": true,

    // 设置容器中进程的用户 ID(uid)和组 ID(gid)。
    "user": {
      "uid": 0, // 容器内进程将以 root 用户(uid 0)身份运行。
      "gid": 0  // 容器内进程将以 root 组(gid 0)身份运行。
    },

    // 容器启动时运行的命令及其参数列表。
    "args": [
      "sh"  // 容器启动后执行 `sh`,表示进入一个 shell 环境。
    ],

    // 容器内进程的环境变量配置。
    "env": [
      // 设置 PATH 环境变量,指定可执行文件查找路径。
      "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
      // 设置终端类型为 xterm。
      "TERM=xterm"
    ],

    // 容器启动时进程的工作目录,这里设置为根目录。
    "cwd": "/",

    // 进程的权限配置,定义容器内进程的能力(Capabilities)。
    "capabilities": {
      // 设置进程在 Bounding 集合中的能力。
      "bounding": [
        "CAP_AUDIT_WRITE", // 审计日志写入能力。
        "CAP_KILL",        // 发送信号杀死进程的能力。
        "CAP_NET_BIND_SERVICE" // 绑定网络端口的能力。
      ],
      // 设置进程在 Effective 集合中的能力。
      "effective": [
        "CAP_AUDIT_WRITE",
        "CAP_KILL",
        "CAP_NET_BIND_SERVICE"
      ],
      // 设置进程在 Permitted 集合中的能力。
      "permitted": [
        "CAP_AUDIT_WRITE",
        "CAP_KILL",
        "CAP_NET_BIND_SERVICE"
      ],
      // 设置进程在 Ambient 集合中的能力。
      "ambient": [
        "CAP_AUDIT_WRITE",
        "CAP_KILL",
        "CAP_NET_BIND_SERVICE"
      ]
    },

    // 设置进程的资源限制。
    "rlimits": [
      {
        // 限制进程能够打开的文件描述符数量。
        "type": "RLIMIT_NOFILE",
        "hard": 1024, // 设置硬限制,即最大文件描述符数为 1024。
        "soft": 1024  // 设置软限制,即文件描述符数警告值为 1024。
      }
    ],

    // 防止执行 execve 系统调用时提升进程权限。
    "noNewPrivileges": true
  },

  // 容器的根文件系统配置。
  "root": {
    // 设置根文件系统的路径,即容器的根目录(默认为 "rootfs" 目录)。
    "path": "rootfs",
    // 将根文件系统以只读模式挂载,提升安全性。
    "readonly": true
  },

  // 容器启动时的主机名配置。
  "hostname": "runc",

  // 挂载点配置,指定容器启动时的文件系统挂载点。
  "mounts": [
    {
      "destination": "/proc",  // 挂载 /proc 文件系统到容器内 /proc 目录。
      "type": "proc",          // 挂载类型为 proc。
      "source": "proc"         // 来源为 proc。
    },
    {
      "destination": "/dev",   // 挂载 /dev 目录,用于设备文件。
      "type": "tmpfs",         // 挂载类型为 tmpfs(内存文件系统)。
      "source": "tmpfs",
      "options": [
        "nosuid",              // 禁止 set-user-ID 和 set-group-ID 位。
        "strictatime",         // 严格时间戳记录模式。
        "mode=755",            // 目录权限设置为 755(所有者可读写执行)。
        "size=65536k"          // 设置 tmpfs 的大小为 64M。
      ]
    },
    {
      "destination": "/dev/pts", // 挂载伪终端文件系统到 /dev/pts。
      "type": "devpts",          // 挂载类型为 devpts。
      "source": "devpts",
      "options": [
        "nosuid",               // 禁止 set-user-ID 和 set-group-ID 位。
        "noexec",               // 禁止执行权限。
        "newinstance",          // 创建新的伪终端实例。
        "ptmxmode=0666",        // 设置 /dev/ptmx 文件的权限为 0666(所有用户可读写)。
        "mode=0620",            // 设置伪终端设备权限为 0620(所有者可读写,组可读写)。
        "gid=5"                 // 设置设备的组 ID 为 5。
      ]
    },
    {
      "destination": "/dev/shm", // 挂载共享内存到 /dev/shm。
      "type": "tmpfs",
      "source": "shm",
      "options": [
        "nosuid",
        "noexec",
        "nodev",                // 禁止设备文件。
        "mode=1777",            // 设置目录权限为 1777(所有用户可读写)。
        "size=65536k"           // 设置大小为 64M。
      ]
    },
    {
      "destination": "/dev/mqueue", // 挂载消息队列文件系统到 /dev/mqueue。
      "type": "mqueue",
      "source": "mqueue",
      "options": [
        "nosuid",
        "noexec",
        "nodev"                 // 禁止设备文件。
      ]
    },
    {
      "destination": "/sys", // 挂载 sysfs 到 /sys,提供系统信息。
      "type": "sysfs",
      "source": "sysfs",
      "options": [
        "nosuid",
        "noexec",
        "nodev",
        "ro"                   // 设置挂载为只读。
      ]
    },
    {
      "destination": "/sys/fs/cgroup", // 挂载 cgroup 文件系统到 /sys/fs/cgroup。
      "type": "cgroup",
      "source": "cgroup",
      "options": [
        "nosuid",
        "noexec",
        "nodev",
        "relatime",
        "ro"                    // 设置挂载为只读。
      ]
    }
  ],

  // Linux 特有的配置选项。
  "linux": {
    // 资源限制配置,如设备访问控制。
    "resources": {
      "devices": [
        {
          "allow": false, // 禁止访问所有设备。
          "access": "rwm" // 禁止读取、写入和访问设备。
        }
      ]
    },

    // 命名空间配置,用于隔离容器与宿主机之间的资源。
    "namespaces": [
      { "type": "pid" },      // 进程 ID 命名空间。
      { "type": "network" },  // 网络命名空间。
      { "type": "ipc" },      // 进程间通信命名空间。
      { "type": "uts" },      // UTS(主机名和域名)命名空间。
      { "type": "mount" },    // 挂载命名空间。
      { "type": "cgroup" }    // cgroup 控制组命名空间。
    ],

    // 屏蔽的系统路径,防止容器内进程读取这些路径。
    "maskedPaths": [
      "/proc/acpi",
      "/proc/asound",
      "/proc/kcore",
      "/proc/keys",
      "/proc/latency_stats",
      "/proc/timer_list",
      "/proc/timer_stats",
      "/proc/sched_debug",
      "/sys/firmware",
      "/proc/scsi"
    ],

    // 设置为只读的系统路径。
    "readonlyPaths": [
      "/proc/bus",
      "/proc/fs",
      "/proc/irq",
      "/proc/sys",
      "/proc/sysrq-trigger"
    ]
  }
}

3.3 进入容器

可以通过 run 命令创建并进入这个容器。

runc run container

也可以先创建一个容器后再进入这个容器,先创建一个 sock 文件用于通信,然后重新开一个终端再创建容器。 创建 sock 文件是通过 tests/cmd/recvtty 目录下的 recvtty.go 创建。

recvtty socket.sock

runc create --console-socket socket.sock container

runc start container

runc exec -t container /bin/sh

如果是在 Docker 容器里面进行 runc 源码的查看和调试,需要执行下面脚本,目的是:检测系统支持 cgroup v2 时启用 cgroup v2 的嵌套控制,这个只有在 Docker 容器里面需要执行,在 Linux 的主机系统上时不需要的。

展示文件内容
#!/bin/bash

set -eu -o pipefail

if [ -f "/sys/fs/cgroup/cgroup.controllers" ]; then
	echo >&2 "Enabling cgroup v2 nesting"
    mkdir -p /sys/fs/cgroup/init
    xargs -rn1 </sys/fs/cgroup/cgroup.procs >/sys/fs/cgroup/init/cgroup.procs || :
    sed -e 's/ / +/g' -e 's/^/+/' </sys/fs/cgroup/cgroup.controllers \
    >/sys/fs/cgroup/cgroup.subtree_control
fi

exec "$@"

4 项目结构

runc 的代码结构非常清晰,下面介绍大致结构:

  • contrib :包含一些附加工具、脚本或示例。
  • internal :这些模块只在 runc 的其他部分中使用。
  • libcontainer :核心库,提供容器相关的功能和操作,包括容器的创建、管理和销毁。
  • types :定义各种数据结构和类型,用于描述容器的状态、配置和其他相关信息。
  • checkpoint.go :处理容器检查点和恢复的逻辑,允许在运行时保存容器的状态,并在需要时恢复。
  • create.go :处理容器创建的逻辑,包括根据配置文件创建新的容器实例。
  • delete.go :负责删除容器的实现,确保容器资源被正确释放。
  • events.go :处理事件系统,允许 runc 发送和接收事件(例如容器状态变化)。
  • exec.go :管理在容器中执行命令的功能,允许用户在运行的容器内执行指定的程序。
  • features.go :管理 runc 支持的功能特性,涉及容器的功能开启和配置。
  • init.go :容器初始化相关的代码,包括设置容器的基本环境和运行状态。
  • kill.go :处理向容器发送信号(例如终止、挂起等)的逻辑。
  • list.go :提供列出当前所有运行中的容器的功能。
  • main.go :runc 的入口点,包含主函数和主要的命令行解析逻辑。
  • notify_socket.go :用于处理与容器通信的通知套接字,支持与容器的控制通信。
  • pause.go :实现容器暂停和恢复的逻辑。
  • ps.go :管理进程状态,会处理容器内运行的进程信息。
  • restore.go :实现从检查点恢复容器的功能,与 checkpoint.go 配合使用。
  • rlimit_linux.go :处理 Linux 下的资源限制(rlimit),确保容器的资源使用不超过配置限制。
  • rootless_linux.go :处理无根用户模式下的容器运行,支持在没有 root 权限的情况下运行容器。
  • run.go :处理容器启动和运行的逻辑。
  • signals.go :管理信号处理,例如响应用户的终止信号。
  • spec.go :定义容器的规范(spec),描述容器的配置和元数据。
  • start.go :处理容器启动的逻辑。
  • state.go :管理容器的状态信息,包括容器的当前运行状态。
  • tty.go :处理终端(tty)的相关逻辑,为容器提供交互式终端支持。
  • update.go : 允许用户更新容器的配置或状态。
  • utils.go :包含一些通用的工具函数,供其他文件调用。
  • utils_linux.go :与 Linux 特定的功能相关的工具函数。

5 基本使用

5.1 create

runc 的 create 命令用于创建一个容器,但是不启动它。主要用于初始化容器的资源分配和配置(如根文件系统、挂载点、命令空间、Cgroups 等),之后可以使用 startexec 来启动容器。

命令格式:

runc create [command options] <container-id>

可选参数如下:

  • --bundle value, -b value :指定 bundle 的目录,如果不指定,默认为当前目录。
  • --console-socket value :指定一个 AF_UNIX 套接字通信,用于接受指向控制台伪终端的文件描述符。
  • --pidfd-socket value :指定一个 AF_UNIX 套接字路径,用于接收指向 init 进程的文件描述符。
  • --pid-file value :指定一个文件路径,将进程 ID 写入该文件。
  • --no-pivot :不使用 pivot root 机制将进程限制在 rootfs 中,通常在 rootfs 位于 ramdisk 时使用。
  • --no-new-keyring :不为容器创建新的 session keyring,这将导致容器继承调用进程的 session key。
  • --preserve-fds value :向容器传递额外的文件描述符(默认传递 0 个),格式为 stdio + $LISTEN_FDS + N

使用样例:

  1. 创建一个 sock 文件,在 runc 中 sock 文件用于与外部程序通信。
recvtty socket.sock
  1. 创建一个容器。
runc create --console-socket socket.sock container

5.2 start

runc 的 start 命令用于启动一个已经创建的容器,它的作用是将 created 状态的容器切换到 running 状态,从而开始执行容器内的进程。

命令格式:

runc start <container-id>

使用样例:

runc start container

5.3 exec

runc 的 exec 命令用于在已经运行的容器内部执行新的进程。

命令格式:

runc exec [command options] <container-id> <command> [command options]

或者:

runc exec -p process.json <container-id>

可选参数如下:

  • --console-socket value :指定一个 AF_UNIX 套接字的路径,用于接收指向控制台伪终端主端的文件描述符。
  • --pidfd-socket value :指定一个 AF_UNIX 套接字的路径,用于接收指向 exec 进程的文件描述符。
  • --cwd value :在容器中指定新的进程的当前工作目录。
  • --env, -e value :为新进程设置环境变量。
  • --tty, -t :为新进程分配一个伪终端(pseudo-TTY)。
  • --user, -u value :为新进程设置用户 ID 和组 ID 格式(格式为 <uid>[:<gid>])。
  • --additional-gids, -g value :为新进程设置附加组 ID。
  • --process, -p value :指定一个 process.json 文件路径,用于定义要执行的进程及其配置。
  • --detach, -d :将新进程从当前 runc 命令中分离,独立运行。
  • --pid-file value :指定文件路径,将进程 ID 写入该文件。
  • --process-label value :设置新进程的 asm 标签(通常与 SELinux 相关)。
  • --apparmor value :为新进程设置 AppArmor 配置文件。
  • --no-new-privs :启用 no_new_privs 选项,禁止新进程获取更高的权限。
  • --cap, -c value :为新进程增加能力。
  • --preserve-fds value :向容器传递额外的文件描述符(默认传递 0 个),格式为 stdio + $LISTEN_FDS + N
  • --cgroup value :指定新进程运行在哪个(已存在的)子 cgroup 中。格式为 [<controller>:]<cgroup>
  • --ignore-pause :允许在暂停的容器中执行 exec

使用样例:

runc exec -t container /bin/sh

5.4 run

runc 的 run 命令用于创建和运行一个容器。

命令格式:

runc run [command options] <container-id>

可选参数如下:

  • --bundle value, -b value :指定 bundle 的目录,如果不指定,默认为当前目录。
  • --console-socket value :指定一个 AF_UNIX 套接字的路径,用于接收指向控制台伪终端主端的文件描述符。
  • --pidfd-socket value :指定一个 AF_UNIX 套接字路径,用于接收指向 init 进程的文件描述符。
  • --pid-file value :指定一个文件路径,将进程 ID 写入该文件。
  • --detach, -d :将新进程从当前 runc 命令中分离,独立运行。
  • --keep :容器退出后不删除容器实例(使用 runc run 默认情况下容器退出后会自动删除)。
  • --no-subreaper :禁用 subreaper,subreaper 是 Linux 的一种机制,用于清理孤立进程(即那些没有父进程的进程)。
  • --no-pivot :不使用 pivot root 机制将进程限制在 rootfs 中,通常在 rootfs 位于 ramdisk 时使用。

使用样例:

runc run container

5.5 pause

runc 的 pause 命令用于暂停容器内的所有进程,将其置于 paused 状态,暂停后的容器不会继续执行任何操作,直到恢复或终止。

命令格式:

runc pause <container-id>

使用样例:

runc pause container

5.6 kill

runc 的 kill 命令用于向指定容器的 init 进程发送信号,默认信号是 SIGTERM。该命令可以用于终止容器中的进程,控制容器的状态或进行调试。

命令格式:

runc kill <container-id> [signal]

使用样例:

runc kill container SIGKILL

常用信号及其作用

信号 数值 描述 常见用途
SIGHUP 1 终端挂起(挂起或断开连接) 通常用于通知进程重新读取配置文件
SIGINT 2 终端中断(通常由 Ctrl+C 发出) 中断正在运行的程序(例如停止某个程序的执行)
SIGQUIT 3 终端退出(通常由 Ctrl+\ 发出) 生成核心转储文件(core dump)并退出
SIGKILL 9 强制终止进程 无法被捕获和忽略,立即终止进程
SIGUSR1 10 用户定义信号 1 用户自定义,用于进程间传递特定信号
SIGUSR2 12 用户定义信号 2 用户自定义,用于进程间传递特定信号
SIGTERM 15 请求进程正常终止 默认信号,通常用于优雅地终止进程
SIGCONT 18 恢复暂停的进程 恢复被 SIGSTOP 暂停的进程
SIGSTOP 19 暂停进程(无法被捕获和忽略) 强制暂停进程,类似于 Ctrl+Z

5.7 delete

runc 的 delete 命令用于删除容器占用的资源,通常用于已经停止或分离的容器实例,删除操作不会直接停止或影响正在运行的容器,而是清理容器在停止后遗留的所有资源,比如挂载点、进程管理信息等。

命令格式:

runc delete [command options] <container-id>

参数:

  • --force, -f :如果容器仍在运行,使用该选项可以强制删除容器。这会向容器的 init 进程发送 SIGKILL 信号,强制终止所有进程,然后清理资源。

使用样例:

runc delete -f container

5.8 list

runs 的 list 命令用于列出当前由 runc 启动的所有容器实例,并显示它们的状态信息。可以使用该命令查看哪些容器正在运行、已经停止或处于其他状态。

命令格式:

runc list [command options]

参数:

  • --format, -f <value> :指定输出格式,可选 table 或 json 格式(默认值为 table)。
  • --quiet, -q :只显示容器的 ID,不显示其他信息(如状态、PID、创建时间等)。

使用样例:

runc list -f json -q

5.9 ps

runc 的 ps 命令用于查看指定容器中正在运行的所有进程信息,类似于 Linux 系统中的 ps 命令,但它专门用于查看容器内部的进程状态。它可以帮助用户了解某个容器内的进程是否正常运行、占用的资源情况以及进程状态(如运行中、睡眠中、僵尸进程等)。

命令格式:

runc ps [command options] <container-id> [ps options]

参数:

  • --format, -f <value> :指定输出格式,可选 table 或 json 格式(默认值为 table)。

使用样例:

runc ps container

5.10 state

runc 的 state 命令用于查看指定容器的当前状态信息。它可以帮助用户了解容器的运行状态、进程信息和配置信息。

命令格式:

runc state <container-id>

使用样例:

runc state container

输出内容如下:

{
  "ociVersion": "1.2.0",
  "id": "container",
  "pid": 21381,
  "status": "running",
  "bundle": "/root/bundle",
  "rootfs": "/root/bundle/rootfs",
  "created": "2024-10-04T15:56:26.847867629Z",
  "owner": ""
}
  • ociVersion :OCI 的版本。
  • id :容器的唯一标识符。在创建容器时,用户会指定一个 ID,该 ID 用于唯一标识主机上的这个容器实例。
  • pid :容器内部 init 进程的 PID(进程 ID)。该 PID 表示在宿主机上该容器主进程的进程号。
  • status :当前容器的状态。
  • bundle :容器绑定的目录路径。该目录通常包含容器的 config.json 文件和 rootfs 根文件系统。
  • rootfs : 容器的根文件系统路径。runc 运行容器时会将该路径视为容器的根目录(/),并隔离于宿主机的文件系统。
  • created :容器创建的时间戳,格式为 ISO 8601 标准时间格式(UTC 时区)。
  • owner :容器的所有者字段。

5.11 resume

runc 的 resume 命令用于恢复先前被暂停(paused)的容器中的所有进程。主要功能是恢复一个已经被暂停的容器。它会让容器中的所有进程从暂停状态继续执行,而不是重新启动或重建容器。当一个容器被 runc pause 暂停后,所有进程都处于“冻结”状态(通常使用 SIGSTOP 信号),无法处理外部事件或请求。使用 runc resume 命令,可以让这些进程恢复执行(类似于 SIGCONT 信号的作用),继续处理之前未完成的任务。

命令格式:

runc resume <container-id>

使用样例:

runc resume container

5.12 checkpoint

runc 的 checkpoint 命令用于将正在运行的容器的当前状态保存为检查点(checkpoint),以便之后可以使用 runc restore 命令恢复到该状态。这个命令主要用于容器迁移调试回滚长时间任务中断后的恢复等场景。

命令格式:

runc checkpoint [command options] <container-id>

参数:

  • --image-path value :指定保存容器 checkpoint 文件的路径。
  • --work-path value :指定保存工作文件和日志的路径。
  • --parent-path value :指定之前的 CRIU checkpoint 文件路径,在 pre-dump 模式下使用。
  • --leave-running :检查点后保持容器继续运行。
  • --tcp-established :允许 checkpoint 操作保留已建立的 TCP 连接。
  • --ext-unix-sk :允许 checkpoint 操作保留与外部 Unix 套接字的连接。
  • --shell-job :允许 checkpoint 操作保存 shell 作业(shell jobs)。
  • --lazy-pages :使用 userfaultfd 机制进行懒加载(lazily restore)内存页。
  • --status-fd value :指定一个文件描述符(FD),当 lazy-pages 模式准备好时,CRIU 会向该 FD 写入一个 \0 字符。
  • --page-server value :指定一个页服务器(page server)的 ADDRESS:PORT 地址,用于传输 checkpoint 的内存页。
  • --file-locks :处理文件锁。
  • --pre-dump :只保存容器的内存信息,而不停止容器。
  • --manage-cgroups-mode value :指定 cgroup 的管理模式:
    • soft(默认):尽量保存 cgroup 的状态,但不强制。
    • full :完全保存 cgroup 的状态。
    • strict :严格保存 cgroup 的状态,如果无法保存则报错。
    • ignore :忽略 cgroup 状态,不进行保存。
  • --empty-ns value :创建一个命名空间(namespace),但不保存该命名空间的属性。
  • --auto-dedup :启用自动内存去重。

使用样例:

runc checkpoint --leave-running --image-path /runc/image container

5.13 restore

runc 的 restore 命令用于从之前的检查点(checkpoint)恢复容器的状态。该命令通常用于容器的迁移暂停后恢复场景。

命令格式:

runc restore [command options] <container-id>

参数:

  • --console-socket value :指定一个 AF_UNIX 套接字的路径,用于接收指向控制台伪终端主端的文件描述符。
  • --image-path value :指定存放容器的 ·checkpoint·(检查点)文件的路径。
  • --work-path value :指定用于存放工作文件和日志的路径。
  • --tcp-established :允许恢复时保持已建立的 TCP 连接。
  • --ext-unix-sk :允许恢复外部的 Unix 套接字。
  • --shell-job :允许恢复 shell 作业(shell jobs)。
  • --file-locks :处理文件锁。
  • --manage-cgroups-mode value :指定 cgroup 的管理模式,有以下几种模式:
    • soft(默认):尽量恢复 cgroup 的状态,但不强制。
    • full :完全恢复 cgroup 的状态。
    • strict :严格恢复 cgroup 的状态,如果无法恢复则报错。
    • ignore :忽略 cgroup 状态,不进行恢复。
  • --bundle value, -b value :指定容器的 bundle 目录路径,该目录包含了 config.jsonrootfs(根文件系统)。
  • --detach, -d :以分离模式恢复容器。
  • --pid-file value :指定一个文件路径,用于保存恢复后的容器的进程 ID(PID)。
  • --no-subreaper :禁用 subreaper,subreaper 是 Linux 的一种机制,用于清理孤立进程(即那些没有父进程的进程)。
  • --no-pivot :不使用 pivot root 机制将进程限制在 rootfs 中,通常在 rootfs 位于 ramdisk 时使用。
  • --empty-ns value :创建一个命名空间(namespace),但不恢复该命名空间的属性。
  • --auto-dedup :启用自动内存去重。
  • --lazy-pages :使用 userfaultfd 机制,懒加载(lazily restore)内存页。
  • --lsm-profile value :指定一个 Linux 安全模块(LSM)的配置文件用于恢复,格式为 TYPE:NAME
  • --lsm-mount-context value :指定 LSM 挂载上下文(mount context)用于恢复。

6 总结

本篇文章首先介绍了 OCI 以及 runc 是什么,OCI 是一个容器运行时规范,runc 是一个实现了 OCI 标准的 CLI 工具,用于创建和管理容器。然后还介绍了 runc 的容器的一些状态,以及它们如何相互转换。最后介绍了 runc 的基本命令的使用。在下一篇文章中将会解读 create 的源码实现。