containerd-shim源码分析-v0.2.4
containerd-shim的代码位于containerd的/containerd-shim目录下,独立编译成二进制。containerd-shim是一个常驻进程,负责容器中进程的启动,是容器进程的父进程。有了containerd-shim之后,容器进程与containerd有了父子联系。
main
先来看下containerd-shim的main()函数,定义在/containerd-shim/main.go中:
main()的流程如下:
- 调用os.Getwd()获取进程目录,即containerd中设置好的进程目录,其中每个容器的init进程称为”init”;
- 打开”shim-log.json”,如果出错,则往该文件中记录日志;
- 调用start()执行进一步的操作。
start()定义如下:
start()的流程如下:
- 调用newProcess()生成process;
- 调用p.create()创建并执行runc命令;
- 监听进程目录下control中的指令,其中0为关闭进程输入,1为调整窗口大小(还不清楚为什么这么设计);
- 监听进程的事件,并作出处理。
所以可以看出,一般来说,containerd-shim不会主动退出。
Process
conainerd-shim的process定义在/containerd-shim/process.go中:
其中processState对应进程的process.json。
newProcess()
newProcess()调用loadProcess()从process.json中读取processState,并打开进程的标准输入输出,然后生成process并返回。
create()
create的流程如下:
- 根据processState,即containerd生成的process.json,生成runc命令;
- 执行runc命令,等待runc命令执行完成;
- 获取pid文件中的pid,更新process的containerPid(pid由runc写入)。
步骤1中的runc命令生成流程如下:
- 如果exec为true,则runc命令为”runc exec”,如,”runc –log /run/docker/libcontainerd/containerd/nginx/4067/log.json –log-format json exec -d –process /run/docker/libcontainerd/containerd/nginx/4067/process.json –console /dev/pts/6 –pid-file /run/docker/libcontainerd/containerd/nginx/4067/pid nginx”
- 否则,如果checkpoint不为空,则生成””(checkpoint不是研究重点,所以现在命令还不大熟,以后补充);
- 否则,runc命令为”runc create”,如,”runc –log /run/docker/libcontainerd/containerd/nginx/init/log.json –log-format json create –bundle /home/fankang/mycontainer –console /dev/pts/5 –pid-file /run/docker/libcontainerd/containerd/nginx/init/pid nginx”。12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697//***转换成runc命令,并执行***//func (p *process) create() error {cwd, err := os.Getwd()if err != nil {return err}logPath := filepath.Join(cwd, "log.json")args := append([]string{"--log", logPath,"--log-format", "json",}, p.state.RuntimeArgs...)//***如果p.state.Exec为true,则调用runc exec***//if p.state.Exec {args = append(args, "exec","-d","--process", filepath.Join(cwd, "process.json"),"--console", p.consolePath,)} else if p.checkpoint != nil {args = append(args, "restore","-d","--image-path", p.checkpointPath,"--work-path", filepath.Join(p.checkpointPath, "criu.work", "restore-"+time.Now().Format(time.RFC3339)),)add := func(flags ...string) {args = append(args, flags...)}if p.checkpoint.Shell {add("--shell-job")}if p.checkpoint.TCP {add("--tcp-established")}if p.checkpoint.UnixSockets {add("--ext-unix-sk")}if p.state.NoPivotRoot {add("--no-pivot")}for _, ns := range p.checkpoint.EmptyNS {add("--empty-ns", ns)}} else {args = append(args, "create","--bundle", p.bundle,"--console", p.consolePath,)if p.state.NoPivotRoot {args = append(args, "--no-pivot")}}args = append(args,"--pid-file", filepath.Join(cwd, "pid"),p.id,)//***生成runc命令***////***runc --log /run/docker/libcontainerd/containerd/nginx/init/log.json --log-format json create --bundle /home/fankang/mycontainer --console /dev/pts/5 --pid-file /run/docker/libcontainerd/containerd/nginx/init/pid nginx***//cmd := exec.Command(p.runtime, args...)cmd.Dir = p.bundlecmd.Stdin = p.stdio.stdincmd.Stdout = p.stdio.stdoutcmd.Stderr = p.stdio.stderr// Call out to setPDeathSig to set SysProcAttr as elements are platform specificcmd.SysProcAttr = setPDeathSig()//***启动runc命令***//if err := cmd.Start(); err != nil {if exErr, ok := err.(*exec.Error); ok {if exErr.Err == exec.ErrNotFound || exErr.Err == os.ErrNotExist {return fmt.Errorf("%s not installed on system", p.runtime)}}return err}p.stdio.stdout.Close()p.stdio.stderr.Close()//***等待runc命令运行完成***//if err := cmd.Wait(); err != nil {if _, ok := err.(*exec.ExitError); ok {return errRuntime}return err}//***pid中存储有容器内进程的pid号,如9947***//data, err := ioutil.ReadFile("pid")if err != nil {return err}//***strconv.Atoi把string转换成int***//pid, err := strconv.Atoi(string(data))if err != nil {return err}p.containerPid = pidreturn nil}
openIO()
openIO()处理进程的标准输入输出。