• docker containerd 中的create 容器操作


    containerd的create container的API如下所示:

    type CreateContainerRequest struct {
    
      Id       string
      BundlePath  string
      Checkpoint   string
      Stdin     string
      Stdout     string
      Stderr     string
      Labels    []string
      NoPivotRoot  bool
      Runtime    string
      RuntimeArgs []string
      CheckpointDir  string
    }
    

      

    StartTask结构如下所示:

    type StartTask struct {
    
      baseTask
    
      ID      string
      BundlePath  string
      Stdout     string
      Stderr      string
      Stdin     string
      StartResponse   chan StartResponse
      Labels      []string
      NoPivotRoot  bool
      Checkpoint    *runtime.Checkpoint
      CheckpointDir   string
      Runtime     string
      RuntimeArgs   []string
    }
    

      

    1、containerd/api/grpc/server/server.go

    func (s *apiServer) CreateContainer(ctx context.Context, c *types.CreateContainerRequest) (*types.CreateContainerResponse, error)

    (1)、根据c填充获得e := &supervisor.StartTask{},并调用s.sv.SendTask(e)

    (2)、调用 r := <-e.StartResponse,再调用apiC ,err := createAPIContainer(r.Container, false)获取创建的容器实例

    (3)、return &types.CreateContainerResponse{Container: apiC,}

    2、containerd/supervisor/create.go

    func (s *Supervisor) start(t *StartTask) error

    (1)、根据t的内容创建容器,runtime.New主要根据ContainerOpts填充结构container获得容器实例,再将创建state并写入状态文件中。

    container, err := runtime.New(runtime.ContainerOpts{
    
      Root:    s.stateDir,
      ID:     t.ID,
      Bundle:   t.BundlePath,
      Runtime:   rt,
      RuntimeArgs: rtArgs,
    
      Shim:    s.shim,
    
      Labels:    t.Labels,
      NoPivotRoot:  t.NoPivotRoot,
      Timeout:   s.timeout
    })
    

      

    (2)、注册新增加的容器,调用s.containers[t.ID] = &containerInfo{container: container,}

    (3)、根据新获得的container实例和t的内容,填充获得startTask,再调用s.startTask <- task 交由worker处理

    task := &startTask{
    
      Err:      t.ErrorCh(),
      Container:   container,
      StartResponse: t.StartResponse,
      Stdin:       t.Stdin,
      Stdout:     t.Stdout,
      Stderr:     t.Stderr
    }
    

      

    3、containerd/supervisor/worker.go

    // Start runs a loop in charge of starting new containers

    func (w *Worker) Start()

    containerd在刚创建时,就启动了10个goroutine,用于处理startTasks,这里的Start函数就是从startTasks这个channel中获取任务,并处理

    (1)、调用process, err := t.Container.Start(t.checkpointPath, runtime.NewStdio(t.Stdin, t.Stdout, t.Stderr))启动容器,NewStdio()仅仅只是将参数封装到一个统一的Stdio结构中

    (2)、分别调用w.s.monitor.MonitorOOM(t.Container),w.s.monitorProcess(process)对容器进行监控,调用t.StartResponse <- StartResponse{Container: t.Container}返回创建成功的容器实例

    4、containerd/runtime/container.go

    func (c *container) Start(checkpointPath string, s Stdio) (Process, error)

    (1)、创建processRoot := filepath.Join(c.root, c.id, InitProcessID)目录

    (2)、容器创建命令 cmd := exec.Command(c.shim, c.id, c.bundle, c.runtime),配置cmd目录为processRoot,spec, err := c.readSpec()

    (3)、生成config如下:

    config := &processConfig {
    
      checkpoint:    checkpointPath,
      root:       processRoot,
      id:        InitProcessID,
      c:         c,
      stdio:        s,
      spec:        spec,
      processSpec:   specs.ProcessSpec(spec.Process),
    
    }
    

      

    (4)、根据config,调用p, err := newProcess(config),生成process实例

    (5)、最后调用c.createCmd(InitProcessID, cmd, p),并返回 return p, nil

    5、containerd/runtime/process.go

    func newProcess(config *processConfig) (*process, error)

    (1)、根据config生成process实例,p := &process{root: config.root, id: config.id, container: config.c, ....., cmdDoneCh: make(chan struct{}), state: Running,}

    (2)、创建状态文件os.Create(filepath.Join(config.root, "process.config")),生成ps := ProcessState{...},并将ps的内容写入状态文件中

    (3)、调用exit, err := getExitPipe(filepath.Join(config.root, ExitFile))和control ,err := getControlPipe(filepath.Join(config.root, ControlFile))生成两个pipe文件,并分别将exit和control赋值给p.exitPipe和p.controlPipe,最后 return p

    6、containerd/runtime/process.go

    getExitPipe和getControlPipe生成两个FIFO文件,其中Exit函数中的FIFO是只读的,而Control函数中的FIFO是读写的

    7、containerd/runtime/container.go

    func (c *container) createCmd(pid string, cmd *exec.Cmd, p *process)

    (1)、p.cmd = cmd, 再调用 cmd.Start()

    (2)、构建defer函数,其中生成一个goroutine,其中调用p.cmd.Wait(),再调用same, err := p.isSameProcess(),如果same为true并且p.pid > 0则再进行一些处理

    (3)、调用c.waitForCreate(p, cmd),c.processes[pid] = p

    8、containerd/runtime/container.go

    func (c *container) waitForCreate(p *process, cmd *exec.Cmd) error

    (1)、该函数先启动一个goroutine用于从pidfile中读取pid

    (2)、select,从(1)中的goroutine接收结果,或者超时,当超时时,调用cmd.Process.Kill()和cmd.Wait()

  • 相关阅读:
    【中山纪念中学六年级模拟赛】方格翻转 题解
    高斯消元
    net 控件开发资料
    使用自定义验证组件库扩展 Windows 窗体
    POJ 3032
    UVa 10878 Decode the tape
    C语言I博客作业03
    第十周助教总结
    第十二周助教总结
    C语言I博客作业06
  • 原文地址:https://www.cnblogs.com/YaoDD/p/6003783.html
Copyright © 2020-2023  润新知