一、shim

containerd下的shim是充当containerd和runc之间的中间件,用来组装runc命令的参数,负责容器中进程的启动。
containerd中调用shim时执行的命令如下:

/root/lib-containerd/containerd/bin/containerd-shim {containerID} {bundleDirPath} runc

其中三个参数如下:
1、Arg0:容器Id
2、Arg1:路径
3、Arg2:执行时间

二、主函数main()

func main() {flag.Parse()cwd, err := os.Getwd() //获取当前进程目录cwd, /run/docker/libcontainerd/containerd/{containerID}/initif err != nil {panic(err)}//打开shim的日志文件:/var/run/docker/libcontainerd/containerd/{containerID}/init/shim-log.jsonf, err := os.OpenFile(filepath.Join(cwd, "shim-log.json"), os.O_CREATE|os.O_WRONLY|os.O_APPEND|os.O_SYNC, 0666)if err != nil {panic(err)}//启动容器if err := start(f); err != nil {//启动失败时,把错误信息err记录到shim的log文件f中if err == errRuntime {f.Close()return}//记录错误,而不是写入到stderr,因为垫片将有/dev/null,因为它是stdio,因为它应该被重新创建到系统init,并没有任何人从它读取writeMessage(f, "error", err)f.Close()os.Exit(1)}
}

三、start()函数

核心步骤是基于containerID,bundle,runtimeName 构建一个进程对象,即type process struct。
然后p.create()调用runc命令启动进程。

func start(log *os.File) error {// 尽快开始处理信号,以便正确地获取信息,或者在我们执行处理程序之前运行时退出signals := make(chan os.Signal, 2048)signal.Notify(signals)// 将shim设置为容器创建的所有孤立进程的子进程回收if err := osutils.SetSubreaper(1); err != nil {return err}// 打开exit管道(p),用来发送退出信号f, err := os.OpenFile("exit", syscall.O_WRONLY, 0)if err != nil {return err}defer f.Close()//打开control管道,可发送和接收容器控制信息:/var/run/docker/libcontainerd/containerd/redis/init/controlcontrol, err := os.OpenFile("control", syscall.O_RDWR, 0)if err != nil {return err}defer control.Close()//基于containerID,bundle,runtimeName 构建一个进程对象,即process构造函数p, err := newProcess(flag.Arg(0), flag.Arg(1), flag.Arg(2))if err != nil {return err}defer func() {if err := p.Close(); err != nil {writeMessage(log, "warn", err)}}()//创建进程,调用`runc`命令if err := p.create(); err != nil {p.delete()return err}//创建控制信息通道msgC := make(chan controlMessage, 32)go func() {//一个死循环,用于接收信号:将control管道的信息格式化写入channel controlMessagefor {var m controlMessageif _, err := fmt.Fscanf(control, "%d %d %d\n", &m.Type, &m.Width, &m.Height); err != nil {continue}msgC <- m}}()var exitShim boolfor {select {case s := <-signals: //监听信号通道signalsswitch s {case syscall.SIGCHLD:exits, _ := osutils.Reap()for _, e := range exits {// check to see if runtime is one of the processes that has exitedif e.Pid == p.pid() {exitShim = truewriteInt("exitStatus", e.Status)}}}// 运行时已经退出,所以垫片也可以退出if exitShim {f.Close()p.Wait()return nil}case msg := <-msgC://监听控制信息通道 controlMessage:0为关闭进程输入,1为调整窗口大小switch msg.Type {case 0:// 关闭标准输入输出if p.stdinCloser != nil {p.stdinCloser.Close()}case 1:if p.console == nil {continue}ws := term.Winsize{Width:  uint16(msg.Width),Height: uint16(msg.Height),}term.SetWinsize(p.console.Fd(), &ws)}}}return nil
}

四、process构造方法

type process struct {sync.WaitGroupid             string //进程编号bundle         stringstdio          *stdio //标准输入输出exec           boolcontainerPid   int //容器进程IDcheckpoint     *checkpointcheckpointPath stringshimIO         *IO stdinCloser    io.Closerconsole        *os.FileconsolePath    stringstate          *processStateruntime        string
}

1、newProcess()函数

func newProcess(id, bundle, runtimeName string) (*process, error) {p := &process{id:      id,bundle:  bundle,runtime: runtimeName,}//读取该进程的process.json文件,得到一个type processState struct对象s, err := loadProcess()if err != nil {return nil, err}p.state = sif s.CheckpointPath != "" {cpt, err := loadCheckpoint(s.CheckpointPath)if err != nil {return nil, err}p.checkpoint = cptp.checkpointPath = s.CheckpointPath}//打开进程的输入输出端if err := p.openIO(); err != nil {return nil, err}return p, nil
}

2、create()函数

func (p *process) create() error {cwd, err := os.Getwd()if err != nil {return err}//设置runc日志路径logPath := filepath.Join(cwd, "log.json")//开始根据不同情况组装`runc`的参数args := append([]string{"--log", logPath,"--log-format", "json",}, p.state.RuntimeArgs...)//`docker exec`时标记为true了 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","--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` 命令cmd := exec.Command(p.runtime, args...)cmd.Dir = p.bundlecmd.Stdin = p.stdio.stdincmd.Stdout = p.stdio.stdoutcmd.Stderr = p.stdio.stderr//调用setPDeathSig来设置SysProcAttr作为特定平台的元素cmd.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()//等待命令执行完毕if err := cmd.Wait(); err != nil {if _, ok := err.(*exec.ExitError); ok {return errRuntime}return err}//读取pid文件信息,得到进程pid数据,pid文件中信息由runc写入data, err := ioutil.ReadFile("pid")if err != nil {return err}//string类型转int类型pid, err := strconv.Atoi(string(data))if err != nil {return err}//回填容器的属性pidp.containerPid = pidreturn nil
}

Docker基础之containerd的shim相关推荐

  1. Docker基础、进阶笔记,为k8s的学习预预热

    标题 Docker基础.进阶笔记,为k8s的学习预预热 笔记来源于视频: 狂神docker基础篇 狂神docker进阶篇 笔记中图片有些取自于:这位博主的两篇docker笔记中的一些图片 百度云笔记工 ...

  2. Docker之docker基础

    Docker之docker基础 docker架构 docker镜像与镜像仓库 为什么镜像仓库名字是Registry而不是repository?在docker中仓库的名字是以应用的名称取名的. 镜像是静 ...

  3. Docker基础(上)

    Docker基础(上) 链接:https://pan.baidu.com/s/1KQjKml2OZAReYwOvpWD9XQ 提取码:6vo8 复制这段内容后打开百度网盘手机App,操作更方便哦 1. ...

  4. Docker之旅(1)-Docker基础

    一.Docker 简介 1.1 Docker 概念 Docker 于 2013 年开源,其基于 go 语言开发,是一个开源的 PaaS 服务(Platform as a Service,平台即服务的缩 ...

  5. docker基础总结

    Docker基础使用总结 Author:onceday Date:2022年11月11日 漫漫长路,有人对你微笑过嘛- 目录 Docker基础使用总结 1.简介 2.docker 镜像加速 3.基础使 ...

  6. Docker基础入门(基本命令)

    Docker基础入门(基本命令) 一.Docker概述 1.Docker为什么会出现? 一款产品: 开发–上线 两套环境!应用环境,应用配置! 开发 - 运维. 问题:我在我的电脑上可以允许!版本更新 ...

  7. Docker 基础教程

    Docker 基础教程 一.安装 # 1. linux 安装 wget -qO- https://get.docker.com/ | sh sudo usermod -aG docker ${USER ...

  8. 【docker基础用法】

    docker基础用法 文章目录 docker基础用法 什么是docker OCI&OCF OCI OCF 断续器 docker架构 docker镜像与镜像仓库 docker对象 安装及使用do ...

  9. 4.1 Docker基础及安装

    1. 版本信息 Docker-CE指Docker社区版,由社区维护和提供技术支持,为免费版本,适合个人开发人员和小团队使用. Docker-EE指Docker企业版,为收费版本,由售后团队和技术团队提 ...

  10. Docker基础之容器(container)创建命令

    一.Docker基础之容器(container)创建命令的用法 docker container create --name myetcd etcd_cluster:gc4.0 create命令完成的 ...

最新文章

  1. mysql管理器源码_一个HelloWorld版的MySQL数据库管理器的设计与实现(源码)
  2. java怎么知道上传文件是否成功_文件包含漏洞之——tomcat CVE-2020-1938漏洞复现
  3. 搜狗浏览器收藏夹在哪_搜狗浏览器居然流氓到操作我的微博账号
  4. 高等数学(第七版)同济大学 总习题一 个人解答
  5. 关于计算机信息学奥赛视频,震惊!信息学奥赛好处竟然这么多
  6. 整蛊系列——使小伙伴的电脑自动关机
  7. Unity Shader 法线贴图原理解析
  8. android 黑白色主题、滤镜效果(公祭日、追悼日)
  9. 第六章 go 文件操作
  10. Docker 容器技术初探
  11. YYModel之字典/Json转模型
  12. nginx负载均衡之一致性Hash方式
  13. 火影忍者服务器最近不稳定吗,火影忍者手游:新区一个月后变死区,魔方合区的标准有点迷!...
  14. mysql删除列前判断_MySQL中,删除列的SQL语句是( )
  15. 4.四大类(DDL、DML、DQL、DCL)
  16. 信息系统开发与管理【四】之 总体规划
  17. C语言错误:expected declaration or statement at end of input 归纳总结
  18. python日记Ddy19——Pandas速查中文手册
  19. UTF8字符编码转换工具
  20. hp 1007打印机加粉

热门文章

  1. 导轨式串口服务器作用,什么是485串口服务器?有什么功能与作用?
  2. 为知笔记Linux一键安装版
  3. UY_ELI, EL社区app
  4. STM32F103RCT6Mini开发板搭建1.44寸TFT显示屏
  5. NT1000无线测温系统 方维监测
  6. 你的电脑能装化学绘图软件ChemDraw吗?
  7. java migration_JetPack知识点实战系列九:Room数据库Migration
  8. rust键位失灵_switch手柄按键失灵不响应怎么办 NS手柄按键没反应解决办法
  9. 苹果电脑写python体验好吗_苹果笔记本系统好用吗,浅谈Mac的优缺点
  10. DRSL: Deep Relational Similarity Learning for Cross-modal Retrieval-多模态学习总结