DOCKER源码分析5 daemon端对container start的处理

上一篇介绍了daemon端对container create的处理,这一章将详细介绍daemon端对container start的处理,也就是r.postContainersStart函数

源码阅读基于docker 19.03


1. r.postContainersStart()

1.1 源码

r.postContainersCreate()的实现位于moby/api/server/router/container/container_routes.go,代码的主要内容是:

func (s *containerRouter) postContainersStart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
	//获取hostConfig配置信息
	var hostConfig *container.HostConfig
	checkpoint := r.Form.Get("checkpoint")
	checkpointDir := r.Form.Get("checkpoint-dir")
	//调用ContainerStart进一步启动容器,2.详细分析
	err := s.backend.ContainerStart(vars["name"], hostConfig, checkpoint, checkpointDir)
}

1.2 流程图

DOCKER源码分析5 daemon端对container start的处理

下面2.分析ContainerStart()函数。


2. ContainerStart()

ContainerStart()的实现位于,代码的主要内容是:

func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.HostConfig, checkpoint string, checkpointDir string) error {
	//可以根据全容器ID、容器名、容器ID前缀获取容器对象
	container, err := daemon.GetContainer(name)
	//调用containerStart进一步启动容器,3.详细分析
	daemon.containerStart(container, checkpoint, checkpointDir, true)
}

下面分析daemon.containerStart()函数。


3. daemon.containerStart()

daemon.containerStart()的实现位于moby/daemon/start.go,代码的主要内容是:

func (daemon *Daemon) containerStart(container *container.Container, checkpoint string, checkpointDir string, resetRestartManager bool) (err error) {
	//在创建的时候调用了一次,这次start又mount一次?
	err := daemon.conditionalMountOnStart(container)
	//3.1对网络进行初始化,后面再详细分析
	err := daemon.initializeNetworking(container)
	//调用daemon.containerd.Create()进一步启动,后面4.再详细分析
	err = daemon.containerd.Create(ctx, container.ID, spec, createOptions)
        // TODO(mlaventure): we need to specify checkpoint options here
        //为指定的容器id创建并启动一个任务
        pid, err := daemon.containerd.Start(context.Background(), container.ID, checkpointDir,
        container.StreamConfig.Stdin() != nil || container.Config.Tty,
        container.InitializeStdio)

}

下面3.1分析daemon.initializeNetworking()函数,4.分析daemon.containerd.Create()


3.1 daemon.initializeNetworking()

daemon.initializeNetworking()的实现位于moby/daemon/container_operations.go。initalizeNetworking()对网络进行初始化,docker网络有三种,bridge模式(默认值)、host模式和contaier 模式,根据confighostConfig中的参数来确定容器的网络模式,然后调libnetwork包来建立网络。代码的主要内容是:

func (daemon *Daemon) initializeNetworking(container *container.Container) error {
	//如果是container模式
	if container.HostConfig.NetworkMode.IsContainer() {
		//获取所要共享网络的container的hosts文件
		nc, err := daemon.getNetworkedContainer(container.ID, container.HostConfig.NetworkMode.ConnectedContainer())
		//将所要共享网络的container的HostnamePath、HostsPath和ResolvConfPath赋值给新container
		initializeNetworkingPaths(container, nc)
		//将所要共享网络的container的Hostname、Domainname赋值给新container
		container.Config.Hostname = nc.Config.Hostname
		container.Config.Domainname = nc.Config.Domainname
		//container模式从这里就返回了,不执行后面的操作
		return nil
	}
	//如果是host模式
	if container.HostConfig.NetworkMode.IsHost() {
		if container.Config.Hostname == "" {
			//将宿主机的hostname赋值给新建container
			container.Config.Hostname, err = os.Hostname()
			//这里host没有返回,回往下继续执行
		}
	}
	//下面3.1.1再详细分析
	err := daemon.allocateNetwork(container)
	//生成container的hosts文件
	return container.BuildHostnameFile()
}

下面3.1.1分析daemon.allocateNetwork()


3.1.1 daemon.allocateNetwork()到daemon.connectToNetwork()

daemon.allocateNetwork()的实现位于moby/daemon/container_operations.go,其主要实现为:

func (daemon *Daemon) allocateNetwork(container *container.Container) error {
	//如果没有使用网络,或者是container网络,则直接返回
	if container.Config.NetworkDisabled || container.HostConfig.NetworkMode.IsContainer() {
		return nil
	}
	
	networks := make(map[string]*network.EndpointSettings)
	for n, epConf := range container.NetworkSettings.Networks {
		if n == defaultNetName {
			continue
		}

		networks[n] = epConf
	}		
	for netName, epConf := range networks {
		cleanOperationalData(epConf)
		//下面再详细分析
		if err := daemon.connectToNetwork(container, netName, epConf.EndpointSettings, updateSettings); err != nil {
			return err
		}
	}
	//将hosts配置信息存到disk中
	err := container.WriteHostConfig()
}

接着分析daemon.connectToNetwork()

func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName string, endpointConfig *networktypes.EndpointSettings, updateSettings bool) (err error) {
	//查找需要连接的网络,n为libnetwork.Network类型,config为*networktypes.NetworkingConfig
	n, config, err := daemon.findAndAttachNetwork(container, idOrName, endpointConfig)
	//获取controller
	controller := daemon.netController
	//获得sandbox
	sb := daemon.getNetworkSandbox(container)
	//从给定的network,也就是n,创建一个endpoint options
	createOptions, err := container.BuildCreateEndpointOptions(n, endpointConfig, sb, daemon.configStore.DNS)
	//创建endpoin
	ep, err := n.CreateEndpoint(endpointName, createOptions...)
	//由network n 创建endpoint Join options
	joinOptions, err := container.BuildJoinOptions(n)
	//将endpoint接入sandbox
	err := ep.Join(sb, joinOptions...)
}

对网络创建的分析先到此,后续文章再分析

  • 创建network
  • connect到network

这两部分。下面先接着把container的start过程分析完。


4. daemon.containerd.Create()

daemon.containerd.Create()的实现位于moby\libcontainerd\remote\client.go,其主要代码为:

func (c *client) Create(ctx context.Context, id string, ociSpec *specs.Spec, runtimeOptions interface{}) error {
//我们通过 NewContainer 方法可以使用默认的 OCI runtime spec 直接创建容器对象。当然,也可以通过 Opts 模式的参数修改默认值:
        _, err := c.client.NewContainer(ctx, id,
            containerd.WithSpec(ociSpec),
            containerd.WithRuntime(runtimeName, runtimeOptions),
            WithBundle(bdir, ociSpec),
}

目前为止,算是分析完了daemon部分的start过程,接下来的内容应该是交给docker-containerd来继续start,但是目前对containerd还不了解,需要去了解containerd之后,才能继续后面的源码分析。

结语

本文分析了daemon对container start的处理做了一个简单分析,很多细节部分没有得到很好的分析,比如网络的具体创建、从daemon到containerd的传递,这些内容将在后续的博客中分析。

参考

上一篇:docker 设置镜像加速加速访问


下一篇:Ceph 日常监视