根据官方定义
runC简介
runC功能包括
runc 命令总览, 后面版本参数有小改动,但影响不大;
$ runc -h
VERSION: 1.0.0-rc4+dev commit: 3f2f8b84a77f73d38244dd690525642a72156c64 spec: 1.0.0 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 init initialize the namespaces and launch the process (do not call it outside of runc) 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 help, h Shows a list of commands or help for one command GLOBAL OPTIONS: --debug enable debug output for logging --log value set the log file path where internal debug information is written (default: "/dev/null") --log-format value set the format used by logs ('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" --help, -h show help --version, -v print the version
源码文件入口
... ... func main() { app := cli.NewApp() app.Name = "runc" app.Usage = usage var v []string if version != "" { v = append(v, version) } if gitCommit != "" { v = append(v, "commit: "+gitCommit) } v = append(v, "spec: "+specs.Version) v = append(v, "go: "+runtime.Version()) if seccomp.IsEnabled() { major, minor, micro := seccomp.Version() v = append(v, fmt.Sprintf("libseccomp: %d.%d.%d", major, minor, micro)) } app.Version = strings.Join(v, "\n") xdgRuntimeDir := "" root := "/run/runc" if shouldHonorXDGRuntimeDir() { if runtimeDir := os.Getenv("XDG_RUNTIME_DIR"); runtimeDir != "" { root = runtimeDir + "/runc" xdgRuntimeDir = root } } app.Flags = []cli.Flag{ cli.BoolFlag{ Name: "debug", Usage: "enable debug output for logging", }, cli.StringFlag{ Name: "log", Value: "", Usage: "set the log file path where internal debug information is written", }, cli.StringFlag{ Name: "log-format", Value: "text", Usage: "set the format used by logs ('text' (default), or 'json')", }, cli.StringFlag{ Name: "root", Value: root, Usage: "root directory for storage of container state (this should be located in tmpfs)", }, cli.StringFlag{ Name: "criu", Value: "criu", Usage: "path to the criu binary used for checkpoint and restore", }, cli.BoolFlag{ Name: "systemd-cgroup", Usage: "enable systemd cgroup support, expects cgroupsPath to be of form \"slice:prefix:name\" for e.g. \"system.slice:runc:434234\"", }, cli.StringFlag{ Name: "rootless", Value: "auto", Usage: "ignore cgroup permission errors ('true', 'false', or 'auto')", }, } // 全部子命令的入口 app.Commands = []cli.Command{ checkpointCommand, createCommand, deleteCommand, eventsCommand, execCommand, initCommand, killCommand, listCommand, pauseCommand, psCommand, restoreCommand, resumeCommand, runCommand, specCommand, startCommand, stateCommand, updateCommand, } app.Before = func(context *cli.Context) error { if !context.IsSet("root") && xdgRuntimeDir != "" { // According to the XDG specification, we need to set anything in // XDG_RUNTIME_DIR to have a sticky bit if we don't want it to get // auto-pruned. if err := os.MkdirAll(root, 0700); err != nil { fmt.Fprintln(os.Stderr, "the path in $XDG_RUNTIME_DIR must be writable by the user") fatal(err) } if err := os.Chmod(root, 0700|os.ModeSticky); err != nil { fmt.Fprintln(os.Stderr, "you should check permission of the path in $XDG_RUNTIME_DIR") fatal(err) } } if err := reviseRootDir(context); err != nil { return err } return logs.ConfigureLogging(createLogConfig(context)) } // If the command returns an error, cli takes upon itself to print // the error on cli.ErrWriter and exit. // Use our own writer here to ensure the log gets sent to the right location. cli.ErrWriter = &FatalWriter{cli.ErrWriter} if err := app.Run(os.Args); err != nil { fatal(err) } } ...
$ docker pull busybox $ mkdir -p /tmp/mycontainer/rootfs $ cd /tmp/mycontainer $ docker export $(docker create busybox) | tar -C rootfs -xvf -
# 该命令是根据OCI 规范来生成配置文件,后面会该命令源码的解析,这里再也不展开; $ runc spec $ ls config.json rootfs
{ "ociVersion": "1.0.0", "process": { "terminal":false, "user": { "uid": 0, "gid": 0 }, "args": [ "/bin/sleep", "3600" ], ... }
creating
:使用 create 命令建立容器,这个过程称为建立中。created
:容器已经建立出来,可是尚未运行,表示镜像文件和配置没有错误,容器可以在当前平台上运行。running
:容器里面的进程处于运行状态,正在执行用户设定的任务。stopped
:容器运行完成,或者运行出错,或者kill 命令以后,容器处于暂停状态。这个状态,容器还有不少信息保存在平台中,并无彻底被删除。paused
:暂停容器中的全部进程,可使用 resume 命令恢复这些进程的执行ps: 演示时有些结果和实际书写时内容有些不一致,但不影响实验过程。
html
$ cd /tmp/mycontainer
$ sudo runc create demo 1 $ runc list
$ sudo runc state demo1 { "ociVersion": "1.0.0", "id": "demo1", "pid": 29314, "status": "created", "bundle": "/tmp/mycontainer/", "rootfs": "/tmp/mycontainer/rootfs", "created": "2020-11-29T06:42:30.366937499Z", "owner": "" }
$ sudo runc ps demo1
能够看到如今该状态是跑着一个叫init 的进程,咱们须要执行的sleep 命令并未执行;init 进程是帮咱们初始化整个容器的运行环境,后面源码会详细介绍;node
$ sudo runc start demo1 $ sudo runc list
$ sudo runc ps demo1
$ sudo runc exec demo1 ps
$ sudo runc exec -t demo1 /bin/sh # 进入了demo1 的进程空间 / $ ls / bin dev etc home proc root sys tmp usr var / $ ls ~ / $ / $ ps aux PID USER TIME COMMAND 1 root 0:00 /bin/sleep 100000000 9 root 0:00 /bin/sh 15 root 0:00 ps aux / $
* 如何使用freezer,期待后面的源码分析linux
$ sudo runc state demo1 { "ociVersion": "1.0.0", "id": "demo1", "pid": 29314, # 当前status 是running "status": "running", "bundle": "/data/docker_lab/bundle", "rootfs": "/data/docker_lab/bundle/rootfs", "created": "2020-11-29T06:42:30.366937499Z", "owner": "" } $ sudo runc pause demo1 $ sudo runc state demo1 { "ociVersion": "1.0.0", "id": "demo1", "pid": 29314, # 当前状态是 paused "status": "paused", "bundle": "/data/docker_lab/bundle", "rootfs": "/data/docker_lab/bundle/rootfs", "created": "2020-11-29T06:42:30.366937499Z", "owner": "" } $ sudo runc resume demo1 $ sudo runc state demo1 { "ociVersion": "1.0.0", "id": "demo1", "pid": 29314, "status": "running", "bundle": "/data/docker_lab/bundle", "rootfs": "/data/docker_lab/bundle/rootfs", "created": "2020-11-29T06:42:30.366937499Z", "owner": "" }
# 15 是kill信号,默认也是15 $ sudo runc kill demo1 15 $ sudo runc state demo1 { "ociVersion": "1.0.0", "id": "demo1", "pid": 0, "status": "stopped", "bundle": "/data/docker_lab/bundle", "rootfs": "/data/docker_lab/bundle/rootfs", "created": "2020-11-29T06:42:30.366937499Z", "owner": "" } # 删除容器 $ sudo runc delete demo1
*events 命令可以向咱们报告容器事件及其资源占用的统计信息git
$ sudo runc events demo1 {"type":"stats","id":"demo1","data":{"cpu":{"usage":{"total":10140103,"percpu":[2003042,8137061],"kernel":0,"user":0},"throttling":{}},"memory":{"usage":{"limit":9223372036854771712,"usage":208896,"max":671744,"failcnt":0},"swap":{"limit":9223372036854771712,"usage":208896,"max":671744,"failcnt":0},"kernel":{"limit":9223372036854771712,"usage":172032,"max":176128,"failcnt":0},"kernelTCP":{"limit":9223372036854771712,"failcnt":0},"raw":{"active_anon":0,"active_file":0,"cache":0,"dirty":0,"hierarchical_memory_limit":9223372036854771712,"hierarchical_memsw_limit":9223372036854771712,"inactive_anon":0,"inactive_file":0,"mapped_file":0,"pgfault":66,"pgmajfault":0,"pgpgin":66,"pgpgout":43,"rss":98304,"rss_huge":0,"shmem":0,"swap":0,"total_active_anon":0,"total_active_file":0,"total_cache":0,"total_dirty":0,"total_inactive_anon":0,"total_inactive_file":0,"total_mapped_file":0,"total_pgfault":66,"total_pgmajfault":0,"total_pgpgin":66,"total_pgpgout":43,"total_rss":98304,"total_rss_huge":0,"total_shmem":0,"total_swap":0,"total_unevictable":0,"total_writeback":0,"unevictable":0,"writeback":0}},"pids":{"current":1},"blkio":{},"hugetlb":{"2MB":{"failcnt":0}}}}
$ sudo runc update -h Note: if data is to be read from a file or the standard input, all other options are ignored. --blkio-weight value Specifies per cgroup weight, range is from 10 to 1000 (default: 0) --cpu-period value CPU CFS period to be used for hardcapping (in usecs). 0 to use system default --cpu-quota value CPU CFS hardcap limit (in usecs). Allowed cpu time in a given period --cpu-share value CPU shares (relative weight vs. other containers) --cpu-rt-period value CPU realtime period to be used for hardcapping (in usecs). 0 to use system default --cpu-rt-runtime value CPU realtime hardcap limit (in usecs). Allowed cpu time in a given period --cpuset-cpus value CPU(s) to use --cpuset-mems value Memory node(s) to use --kernel-memory value Kernel memory limit (in bytes) --kernel-memory-tcp value Kernel memory limit (in bytes) for tcp buffer --memory value Memory limit (in bytes) --memory-reservation value Memory reservation or soft_limit (in bytes) --memory-swap value Total memory usage (memory + swap); set '-1' to enable unlimited swap --pids-limit value Maximum number of pids allowed in the container (default: 0)
$ sudo runc update --memory 104857600 demo1 # 查看demo1 的cgroup 是否被设置了 $ cat /sys/fs/cgroup/memory/user.slice/demo1/memory.limit_in_bytes 104857600
容器的热迁移简介
实现原理
注意/参考
Docker版本
、Linux内核
、CRIU版本
一致,不然生成的文件会会有所不一样,致使恢复不了。演示
$ runc checkpoint demo1 criu failed: type NOTIFY errno 0 log file: /run/runc/demo1/criu.work/dump.log # 查看dump 文件, 大概意思找不到标准输入文件, 具体缘由(能力有限)不清楚为何;google了下 也有很多issue 提到这个问题 $ cat/run/runc/demo1/criu.work/dump.log ... (00.021115) Error (criu/files-reg.c:1294): Can't lookup mount=26 for fd=0 path=/dev/pts/0 ...
$ cd /tmp/mycontainer $ runc run demo1
$ runc list
# 保存了当前进程的运行状态,如快照,并退出了进程 $ runc checkpoint demo1 # 会发现程序已经被中止 $ runc list # 查看当前目录, 多了个checkpoint 目录 $ ls ./ checkpoint config.json rootfs # 能够看到不少img 文件,就是保存程序运行时的一些状态;用于恢复使用; $ ls ./checkpoint cgroup.img files.img inventory.img mm-1.img pagemap-1.img route6-9.img tmpfs-dev-63.tar.gz.img core-1.img fs-1.img ip6tables-9.img mountpoints-12.img pages-1.img route-9.img tmpfs-dev-65.tar.gz.img descriptors.json ids-1.img ipcns-var-10.img netdev-9.img pipes-data.img seccomp.img tmpfs-dev-66.tar.gz.img fdinfo-2.img ifaddr-9.img iptables-9.img netns-9.img pstree.img tmpfs-dev-61.tar.gz.img utsns-11.img
# 必须在此目录下执行 $ cd/tmp/mycontainer # 恢复后也是在前台执行的; $ runc restore demo1 # 在另外一个终端查看容器是否执行 $ runc list