根據官方定義
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