Skip to content

Latest commit

 

History

History
230 lines (169 loc) · 9.15 KB

charter 1.md

File metadata and controls

230 lines (169 loc) · 9.15 KB

chapter 1:docker daemon启动过程


00 序

docker daemon 的功能是创建守护进程,保障docker服务正常运行。由两部分逻辑组成:第一,创建docker运行环境并启动守护进程;第二,服务docker client,接收和处理请求。

01 目录

序号 标题
第一节 daemon 配置初始化
第二节 创建daemon所需的其他服务
第三节 创建核心守护进程
第四节 创建Middlewares
第五节 创建Router

02 内容

第一节 daemon 配置初始化

这部分功能在main.init()函数中实现,作用是初始化docker daemon的参数列表,解析用户启动参数。

1.1 首先定位main.NewDaemonCli(),功能是创建daemon支持的配置参数信息,形同“dackerd --help”所示数据。

func NewDaemonCli() *DaemonCli {
	// TODO(tiborvass): remove InstallFlags?
	daemonConfig := new(daemon.Config)
	daemonConfig.LogConfig.Config = make(map[string]string)
	daemonConfig.ClusterOpts = make(map[string]string)

	if runtime.GOOS != "linux" {
		daemonConfig.V2Only = true
	}

	daemonConfig.InstallFlags(flag.CommandLine, presentInHelp)
	configFile := flag.CommandLine.String([]string{daemonConfigFileFlag}, defaultDaemonConfigFile, "Daemon configuration file")
	flag.CommandLine.Require(flag.Exact, 0)

	return &DaemonCli{
		Config:      daemonConfig,
		commonFlags: cliflags.InitCommonFlags(),
		configFile:  configFile,
	}
}

1.2 定位main.main(),先合并docker daemon的全部配置参数,然后解析用户启动daemon时的命令行参数,若是“--help”则调用flag.Usage()后结束,或是“--version”则调用showVersion()后结束,其他则调用核心函数daemonCli.start()进入主流程。

	flag.Merge(flag.CommandLine, daemonCli.commonFlags.FlagSet)
	//set flag Usage
	flag.Usage = func() {
        ......
	}
	//set flag shortUsage
	flag.CommandLine.ShortUsage = func() {
		fmt.Fprint(stderr, "\nUsage:\tdockerd [OPTIONS]\n")
	}
	//analysize cmd option
	if err := flag.CommandLine.ParseFlags(os.Args[1:], false); err != nil {
		os.Exit(1)
	}

	if *flVersion {
		showVersion()
		return
	}

	if *flHelp {
		flag.Usage()
		return
	}

	if !stop {
		err = daemonCli.start()

第二节 daemonCli.start():创建daemon所需的其他服务

2.1 创建daemon启动时的配置信息,通过合并用户的命令行参数和配置文件的参数实现,检查配置文件中的参数是否与命令行参数有重合,如有则退出,无则将两者融合成最终的配置参数。

2.2 创建apiserver,用于监听并接收客户端请求数据,默认情况下的监听接口是unix:///var/run/docker.sock,如果存在TCP连接则进行严格的TLS检测。

2.3 创建registryservice,用于配置注册服务器的信息,包括公有服务器和私有服务器信息。

2.4 创建libcontainer,用于管理容器的各项具体操作事例。

	cliConfig, err := loadDaemonCliConfig(cli.Config, flags, cli.commonFlags, *cli.configFile)
    ......
	serverConfig := &apiserver.Config{
		Logging:     true,
		SocketGroup: cli.Config.SocketGroup,
		Version:     dockerversion.Version,
		EnableCors:  cli.Config.EnableCors,
		CorsHeaders: cli.Config.CorsHeaders,
	}
    ......
	api := apiserver.New(serverConfig)
	for i := 0; i < len(cli.Config.Hosts); i++ {
		var err error
		if cli.Config.Hosts[i], err = opts.ParseHost(cli.Config.TLS, cli.Config.Hosts[i]); err != nil {
        ......
		logrus.Debugf("Listener created for HTTP on %s (%s)", protoAddrParts[0], protoAddrParts[1])
		api.Accept(protoAddrParts[1], l...)
	}
	......
	registryService := registry.NewService(cli.Config.ServiceOptions)
	containerdRemote, err := libcontainerd.New(cli.getLibcontainerdRoot(), cli.getPlatformRemoteOptions()...)
	if err != nil {
		return err
	}

第三节 daemon.Newdaemon():创建核心守护进程


3.1 设置MTU的值:setDefaultMtu()

如果config选项中存在此值则无需改动,否则设置为默认值1500。

3.2 检测网桥和CGROUP驱动:verifyDaemonSettings()

首先,网卡和IP不能同时设置,因为网卡已经设置了IP,不能重复定义;其次,检测控制容器是否设置互联,如需设置容器连通性(不管连通或不连通),则必须开启iptable功能;再者,对runtimeoptions中的cgroup driver判断,以及croupparent目录和cgroup driver判断;

3.3 检测是否设置禁用网桥的选项

3.4 检测dockerd的启动用户是否root,目前版本dockerd必须以root权限启动。

3.5 检测RemappedRoot属性:setupRemappedRoot()

RemappedRoot属性是指映射docker host上的用户到container中作Root用户,该属性由dockerd启动时的"userns-remap"选项指定。例如,"./dockerd --uers-remap 1000:1000"意味着uid和gid为1000的用户会被重新映射,映射记录存储在/etc/subuid和/etc/subgid文件中,dockerd就是在该文件中查找特定用户映射后的结果,比如"thebeeman:100000:65535",uid 1000对应thebeeman用户,thebeeman hostID范围是[100000,165535];container ID范围是[0,65535],其中container Root ID=0,此时host ID=100000。

3.6 通过获取rootUID和rootGID

rootUID和rootGID其实就是Host ID,上步已详细讲解,从uidMaps和gidMaps中取出即可。

3.7 获取docker ROOT USER的主目录

如果"--users-remap"参数不为空,创建目录"/var/lib/docker/rootUID:rootGID",并设置config.root为该值;否则为空,则主目录位于"/var/lib/docker/rootUID:rootGID"。不设置该项时主目录就是"/var/lib/docker/rootUID:rootGID"。例如:

启动命令为dockerd --raws-log,客户端执行docker images -a则是获取"/var/lib/docker/"目录下iamges的数据。

启动命令为dockerd --raws-log  --users-remap=hostUID:hostGID, 客户端执行docker images -a则是获取"/var/lib/docker/rootUID:rootGID"目录下iamges的数据。

3.8 docker 主目录下创建tmp目录

设置环境变量 "TMPDIR=cofig.root/tmp"

3.9 初始化AppArmor配置

首先检测系统是否支持apparmor选项,通过读取"/sys/module/apparmor/parameters/enabled"获知。然后,创建默认的AppArmor的配置文件"/etc/apparmor.d/docker"文件,并填充数据在里面。

3.10 docker 主作目录下创建containers目录

创建containers目录,用于记录和存储容器。

3.11 创建存储驱动的目录

 先通过"DOCKER_DRIVER"获取,否则通过config.GraphDriver项获得。然后调用graphdriver.New创建aufs目录下子目录diff、layers、mnt,再调用NewFSMetadataStore创建layerdb数据库文件,最后调用NewStoreFromGraphDriver()重新存储layerdb中的镜像层,加载image/aufs/layerdb目录下面的数据。

3.12 检测和配置EnableSelinuxSupport

 检测是否设置Selinux选项,是则进行系统配置;否则关闭系统配置。

3.13 创建LayerDownloader和LayerUploader

3.14 创建image/aufs/imagedb目录

 调用NewFSStoreBackend创建imagedb,重载imagedb记录的数据。

3.15 创建卷驱动目录

 创建volumes目录,然后加载卷驱动,具体怎么实现没搞懂。

3.16 加载信任的key

 受信任的key存储在/etc/docker/key.json

3.17 创建distribution目录

3.18 创建events

3.19 创建image/aufs/repositories.json

 标签存储仓库,就是docker images的数据。

3.20 文件系统中数据迁移

3.21 检测和配置advertise

3.22 初始化网络控制器

第四节 initMiddlewares():创建Middlewares

func (cli *DaemonCli) initMiddlewares(s *apiserver.Server, cfg *apiserver.Config) {
	v := cfg.Version

	vm := middleware.NewVersionMiddleware(v, api.DefaultVersion, api.MinVersion)
	s.UseMiddleware(vm)

	if cfg.EnableCors {
		c := middleware.NewCORSMiddleware(cfg.CorsHeaders)
		s.UseMiddleware(c)
	}

	u := middleware.NewUserAgentMiddleware(v)
	s.UseMiddleware(u)

	if len(cli.Config.AuthorizationPlugins) > 0 {
		authZPlugins := authorization.NewPlugins(cli.Config.AuthorizationPlugins)
		handleAuthorization := authorization.NewMiddleware(authZPlugins)
		s.UseMiddleware(handleAuthorization)
	}
}

第五节 initRouter():创建HTTP请求分发路由

router用于匹配事件的处理函数,并进行事件分发。那么初始化router就是创建事件与其处理函数的maps,包括container、images、volume、build、systemrouter等。由于docker是基于网络服务提供对外接口,c/s采用Http协议进行通信,下面枚举客户端请求与处理函数的记录,客户端请求的数据都会经过router处理后分发到相应的处理流程中去。

func initRouter(s *apiserver.Server, d *daemon.Daemon) {
	decoder := runconfig.ContainerDecoder{}

	routers := []router.Router{
		container.NewRouter(d, decoder),
		image.NewRouter(d, decoder),
		systemrouter.NewRouter(d),
		volume.NewRouter(d),
		build.NewRouter(dockerfile.NewBuildManager(d)),
	}
	if d.NetworkControllerEnabled() {
		routers = append(routers, network.NewRouter(d))
	}

	s.InitRouter(utils.IsDebugEnabled(), routers...)
}