"docker启动容器过程分析"

  "docker容器网络初始化"

Posted by Xu on July 27, 2017

Docker启动容器-网络初始化

这一章节接着容器启动分析第一章的内容继续研究深入,第一章中我们了解容器文件系统挂载的过程,这一章我们将就启动容器的第二个步骤网络初始化进行研究: 首先我们可以从宏观的角度来理解docker容器的网络模式,这里有一篇不错的博客可以帮助我们理解:Docker容器的五种网络模式

network daemon.initializeNetworking(container)

func (daemon *Daemon) initializeNetworking(container *container.Container) error {
	var err error
    //验证该容器的网络通信模式是否为“其他容器”模式
	if container.HostConfig.NetworkMode.IsContainer() {
		// we need to get the hosts files from the container to join
		nc, err := daemon.getNetworkedContainer(container.ID, container.HostConfig.NetworkMode.ConnectedContainer())//若是,则获取指定容器的网络空间,共用同一网络namespace
		if err != nil {
			return err
		}
		initializeNetworkingPaths(container, nc)
		container.Config.Hostname = nc.Config.Hostname
		container.Config.Domainname = nc.Config.Domainname
		return nil
	}
     //验证该容器的网络模式是否为“共享主机“的模式
     	if container.HostConfig.NetworkMode.IsHost() {
		if container.Config.Hostname == "" {
		//是共享主机的模式,则与宿主机共享IP地址
			container.Config.Hostname, err = os.Hostname()
			if err != nil {
				return err
			}
		}
	}
    //给该容器分配网络资源
	if err := daemon.allocateNetwork(container); err != nil {
		return err
	}

	return container.BuildHostnameFile()//写容器的hostname文件
}

给容器分配网络:关于沙盒及endpoint的一些概念可以参照下面这篇blogsandbox&endpoint

这部分实现根据容器网络配置信息,更新container.NetworkSettings.Networks网络名和endpoint映射关系,最后遍历该mao将所有的endpoint连接到网络

func (daemon *Daemon) allocateNetwork(container *container.Container) error {
	start := time.Now()
	controller := daemon.netController//获取daemon的网络控制器

	if daemon.netController == nil {
		return nil
	}

	// Cleanup any stale sandbox left over due to ungraceful daemon shutdown
	if err := controller.SandboxDestroy(container.ID); err != nil {
		logrus.Errorf("failed to cleanup up stale network sandbox for container %s", container.ID)
		//清理一些残留无用的沙盒,沙盒就是一个容器的网络栈,相当于一个网络命名空间
	}

	updateSettings := false
	if len(container.NetworkSettings.Networks) == 0 {
		if container.Config.NetworkDisabled || container.HostConfig.NetworkMode.IsContainer() {
			return nil
		}

		daemon.updateContainerNetworkSettings(container, nil)//更新容器的网络设置,其实就是根据容器网络模式来更新container.NetworkSettings.Networks的映射关系,就是网络名和endpoint的关系
		updateSettings = true
	}

	// always connect default network first since only default
	// network mode support link and we need do some setting
	// on sandbox initialize for link, but the sandbox only be initialized
	// on first network connecting.
	defaultNetName := runconfig.DefaultDaemonNetworkMode().NetworkName()
	if nConf, ok := container.NetworkSettings.Networks[defaultNetName]; ok {
		cleanOperationalData(nConf)
		if err := daemon.connectToNetwork(container, defaultNetName, nConf.EndpointSettings, updateSettings); err != nil {
			return err
			//第一次尝试连接默认的网络名,同时可以完成沙盒的初始化
		}

	}

	// the intermediate map is necessary because "connectToNetwork" modifies "container.NetworkSettings.Networks"
	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 {//将所有endpoint连接到网络
			return err
		}
	}

	if err := container.WriteHostConfig(); err != nil {
	//将主机配置信息保存在磁盘
		return err
	}
	networkActions.WithValues("allocate").UpdateSince(start)
	return nil
}