docker启动Container进程之启动流程

来源:互联网 发布:怎么出售淘宝店铺页面 编辑:程序博客网 时间:2024/05/21 09:40

docker启动Container进程之启动流程


代码分析起点:daemon/container.go/Start
docker启动containter的起始点在daemon/container.go/Start方法。
这个方法做了三大方面的事情:

  • 配置启动container所需要的环境
  • 启动container进程
  • 如果container启动失败,做资源回收

1 配置启动container需要的环境

此部分为启动container进程做准备。包括一下几个方面:
1. 构建container的DNS
使用container的setupContainerDns方法构建container的DNS。 –dns
2. 挂在文件系统
使用Mount方法配置container的basefs路径。
3. 初始化网络
使用initialize方法配置container网络信息,详细分析见docker如果构建bridge网络 。–net
4. 更新parent hosts
使用container的updateParentHosts方法更新与当前container相关container的etc/hosts
5. 验证container资源设置
使用container的verifyDaemonSetting方法校验container的Me moryLimitSwapLimitIPv4ForwardingDisable设置。
6. 卷准备
使用container的prepareVolumes方法为container启动配置挂载数据卷。 –volume
7. 链接其他container
使用container的setupLinkedContainers方法配置container需要连接的其他container。 –link
8. 构建工作路径
使用container的setupWorkingDirectory方法配置container的工作路径。
9. 创建container进程环境
使用container的createDaemonEnvironment方法配置container内的环境变量。 –env
10. populateCommand
根据步骤9收集到的环境变量,使用populateCommand构建execdriver命令execdriver.Command
11. 构建mount点
使用container的setupMounts方法设置container内部的mount配置。

2 启动container进程

在准备好启动container进程的配置后,docker开始创建container进程,使用container的waitForStart方法完成此项任务。
通过为启动的container创建一个monitor来监控container进程的启动情况,这个地方使用了golang的一个特性:channel
如果container进程正常启动,那么会在container.monitor.startSigbal通道受到一个信号;如果在promise.Go(container.monitor.Start)通道收到错误消息,说明container启动失败并返回错误信息。
也就是说container monitor监控container主进程的执行情况。如果container被指定了一种重启策略,那么monitor保证进程按重启策略重启。当container被stop,monitor会reset并cleanup container占用的资源。

2.1 为container创建monitor

newContinerMonitor方法中根据为container内第一个进程配置的重启策略创建一个monitor对象。

func newContainerMonitor(container *Container, policy runconfig.RestartPolicy) *containerMonitor {    return &containerMonitor{        container:     container,        restartPolicy: policy,        timeIncrement: defaultTimeIncrement,        stopChan:      make(chan struct{}),        startSignal:   make(chan struct{}),    }}

containerMonitor结构字段

字段名 含义 mux sync.Mutex monitor锁 container *Container 被监控的container restartPolicy runconfig.RestartPolicy monitor使用的监控策略 failureCount int contianer启动失败计算器 shouldStop bool 告诉monitor,下次退出container的原因是由于docker或用户要求 startSignal chan struct{} 通过此通道,container告诉docker启动完成,container最初的启动完成后被关闭 stopChan chan struct{} 告诉monitor在下次重启之前有一个等待时间 timeIncrement int 两次重启的时间间隔,单位:毫秒 lastStartTime time.Time monitor最后执行container进程的时间

2.2 启动container进程

docker daemon启动一个goroutine执行container.monitor.Start,然后监听两个通道,如果捕获了startSignal,说明container进行正常启动;如果从promise.Go(container.monitor.Start)通道捕获到了err,说明container进程启动失败。其中contianer.monitor.Start为启动container进程的入口函数。

    // block until we either receive an error from the initial start of the container's    // process or until the process is running in the container    select {    case <-container.monitor.startSignal:    case err := <-promise.Go(container.monitor.Start):        return err    }

下面看看monitor.Start做了什么事情:

首先为container创建标准输入、输出和错误管道。

pipes := execdriver.NewPipes(m.container.stdin, m.container.stdout, m.container.stderr, m.container.Config.OpenStdin)

然后启动container进程

if exitStatus, err = m.container.daemon.Run(m.container, pipes, m.callback); err != nil {            // if we receive an internal error from the initial start of a container then lets            // return it instead of entering the restart loop            if m.container.RestartCount == 0 {                m.container.ExitCode = -1                m.resetContainer(false)                return err            }            log.Errorf("Error running container: %s", err)        }

daemon/Run –> execdriver/Run –> native/driver.go/Run
在native/driver.go/Run中使用namespace包中的方法Exec创建容器进程。

go func() {        exitCode, err := namespaces.Exec(container, c.ProcessConfig.Stdin, c.ProcessConfig.Stdout, c.ProcessConfig.Stderr, c.ProcessConfig.Console, dataPath, args, func(container *libcontainer.Config, console, dataPath, init string, child *os.File, args []string) *exec.Cmd {            c.ProcessConfig.Path = d.initPath            c.ProcessConfig.Args = append([]string{                DriverName,                "-console", console,                "-pipe", "3",                "-root", filepath.Join(d.root, c.ID),                "--",            }, args...)            // set this to nil so that when we set the clone flags anything else is reset            c.ProcessConfig.SysProcAttr = &syscall.SysProcAttr{                Cloneflags: uintptr(namespaces.GetNamespaceFlags(container.Namespaces)),            }            c.ProcessConfig.ExtraFiles = []*os.File{child}            c.ProcessConfig.Env = container.Env            c.ProcessConfig.Dir = container.RootFs            return &c.ProcessConfig.Cmd        }, func() {            close(waitForStart)            if startCallback != nil {                c.ContainerPid = c.ProcessConfig.Process.Pid                startCallback(&c.ProcessConfig, c.ContainerPid)            }        })        execOutputChan <- execOutput{exitCode, err}    }()

函数func(container *libcontainer.Config, console, dataPath, init string, child *os.File, args []string)构建了container进程的属性相关字段,例如 :dockerinit路径、进程参数、进程namespace flags、进程环境、进程根目录等信息。

3 启动失败后资源的回收

如果container进行启动失败,那么通过

defer func() {        if err != nil {            container.setError(err)            // if no one else has set it, make sure we don't leave it at zero            if container.ExitCode == 0 {                container.ExitCode = 128            }            container.toDisk()            container.cleanup()        }    }()

回收已经为container分配的资源。

0 0
原创粉丝点击