docker源码解析(一)dockerd服务的启动

本文源码基于docker20.10.8。文中机器安装的docker代码版本为docker20.10.0。
本文的计算机环境是centos-8。因此,不介绍Windows平台的docker。
docker源码地址

参考文献
1. docker的初始化——by lovenashbest-简书
Docker - NewDaemon源码分析——by ETIN-知乎
docker 源码分析NewDaemon 函数——by arwen_spy-博客园

部分参数默认值

Config.Root				/var/lib/docker
Config.ExecRoot			/var/run/docker
Config.Pidfile			/var/run/docker.pid
imageRoot				/var/lib/docker/image/文件系统(linux为overlay2)/

main函数入口

代码位置:/moby/cmd/dockerd/docker.go:

func main() {
	if reexec.Init() {
		return
	}

	// initial log formatting; this setting is updated after the daemon configuration is loaded.
	logrus.SetFormatter(&logrus.TextFormatter{
		TimestampFormat: jsonmessage.RFC3339NanoFixed,
		FullTimestamp:   true,
	})

	// Set terminal emulation based on platform as required.
	_, stdout, stderr := term.StdStreams()

	initLogging(stdout, stderr)

	onError := func(err error) {
		fmt.Fprintf(stderr, "%s\n", err)
		os.Exit(1)
	}

	cmd, err := newDaemonCommand()
	if err != nil {
		onError(err)
	}
	cmd.SetOutput(stdout)
	if err := cmd.Execute(); err != nil {
		onError(err)
	}
}

首先判断reexec.Init()方法的返回值,如果是真,则直接退出运行,否则继续执行。由于在docker运行之前,没有任何initializer注册,所以这段代码执行的返回值为假。

随后调用newDaemonCommand()函数,启动Daemon。

newDaemonCommand()函数

代码路径:/moby/cmd/dockerd/docker.go

func newDaemonCommand() (*cobra.Command, error) {
	opts := newDaemonOptions(config.New())

	cmd := &cobra.Command{
		Use:           "dockerd [OPTIONS]",
		Short:         "A self-sufficient runtime for containers.",
		SilenceUsage:  true,
		SilenceErrors: true,
		Args:          cli.NoArgs,
		RunE: func(cmd *cobra.Command, args []string) error {
			opts.flags = cmd.Flags()
			return runDaemon(opts)
		},
		DisableFlagsInUseLine: true,
		Version:               fmt.Sprintf("%s, build %s", dockerversion.Version, dockerversion.GitCommit),
	}
	cli.SetupRootCommand(cmd)

	flags := cmd.Flags()
	flags.BoolP("version", "v", false, "Print version information and quit")
	defaultDaemonConfigFile, err := getDefaultDaemonConfigFile()
	if err != nil {
		return nil, err
	}
	flags.StringVar(&opts.configFile, "config-file", defaultDaemonConfigFile, "Daemon configuration file")
	opts.InstallFlags(flags)
	if err := installConfigFlags(opts.daemonConfig, flags); err != nil {
		return nil, err
	}
	installServiceFlags(flags)

	return cmd, nil
}

首先调用newDaemonOptions()创建flag参数。

然后调用runDaemon()函数。

runDaemon()函数

Windows平台

Windows平台,代码路径:/moby/cmd/dockerd/docker_windows.go

func runDaemon(opts *daemonOptions) error {
	daemonCli := NewDaemonCli()

	// On Windows, this may be launching as a service or with an option to
	// register the service.
	stop, runAsService, err := initService(daemonCli)
	if err != nil {
		logrus.Fatal(err)
	}

	if stop {
		return nil
	}

	// Windows specific settings as these are not defaulted.
	if opts.configFile == "" {
		opts.configFile = filepath.Join(opts.daemonConfig.Root, `config\daemon.json`)
	}
	if runAsService {
		// If Windows SCM manages the service - no need for PID files
		opts.daemonConfig.Pidfile = ""
	} else if opts.daemonConfig.Pidfile == "" {
		opts.daemonConfig.Pidfile = filepath.Join(opts.daemonConfig.Root, "docker.pid")
	}

	err = daemonCli.start(opts)
	notifyShutdown(err)
	return err
}

linux平台

代码路径:/moby/cmd/dockerd/docker_unix.go

func runDaemon(opts *daemonOptions) error {
	daemonCli := NewDaemonCli()
	return daemonCli.start(opts)
}

代码路径:/moby/cmd/dockerd/daemon.go

首先调用NewDaemonCli()申请了一个结构体实例。

// NewDaemonCli returns a daemon CLI
func NewDaemonCli() *DaemonCli {
	return &DaemonCli{}
}

随后调用start()函数。

daemonCli.start()函数

代码路径:/moby/cmd/dockerd/daemon.go

func (cli *DaemonCli) start(opts *daemonOptions) (err error) {
	opts.SetDefaultOptions(opts.flags)		//TLS加密配置

	loadDaemonCliConfig(opts)				//配置docker信息

	configureDaemonLogs(cli.Config)			//日志等级配置

	setDefaultUmask()						//设置umask默认权限0022,对应文件目录权限为0755->即用户具有读/写/执行权限,组用户和其它用户具有读写权限;
	
	// Create the daemon root before we create ANY other files (PID, or migrate keys)
	// to ensure the appropriate ACL is set (particularly relevant on Windows)
	daemon.CreateDaemonRoot(cli.Config)		//创建root路径

	system.MkdirAll(cli.Config.ExecRoot, 0700)// 创建exec进去的路径

	pf, err := pidfile.New(cli.Pidfile)		//process ID

	if cli.Config.IsRootless() {
		// Set sticky bit if XDG_RUNTIME_DIR is set && the file is actually under XDG_RUNTIME_DIR
	StickRuntimeDirContents(potentiallyUnderRuntimeDir)//设置XDR_runtime路径

	newAPIServerConfig(cli)								//设置APIserver配置
	
	apiserver.New(serverConfig)							//创建一个apiserver对象

	loadListeners(cli, serverConfig)					//配置监听hosts
	//默认的host
	//	tcp://127.0.0.1:2376					TLS
	//	tcp://127:0.0.1:2375					HTTP(没有使用)
	//	unix://runtime目录/var/run/docker.sock	linux如果不使用TLS,使用这个,需要获取runtime目录。
	//	unix:///var/run/docker.sock				Windows如果不使用TLS,则使用这个

	context.WithCancel(context.Background())			//

	cli.initContainerD(ctx)								//初始化containerd

	defer waitForContainerDShutdown(10 * time.Second)	//退出以后,10s时间内没有响应则强制结束。
	

	// Notify that the API is active, but before daemon is set up.
	preNotifySystem()									//函数空?

	pluginStore := plugin.NewStore()					//创建plugin仓库。插件仓库

	cli.initMiddlewares(cli.api, serverConfig, pluginStore)//
	//docker-experimental
	//docker version
	//配置为每个请求添加的cors头。
	//authorization middleware

	//重点!!!
	daemon.NewDaemon(ctx, cli.Config, pluginStore)		//通过config, registry, containerd, pluginStore来真正创建了daemon

	d.StoreHosts(hosts)									//127.0.0.1:2376等一系列。
	//把定义的host map数据结构中的value值设置为true,表示开始监听。

	// validate after NewDaemon has restored enabled plugins. Don't change order.
	validateAuthzPlugins(cli.Config.AuthorizationPlugins, pluginStore)//

	cli.d = d

	cli.startMetricsServer(cli.Config.MetricsAddress)	//metrics地址
	//对应daemon.json文件中的key值为:"metrics-addr"
	//需要在experiment模式下才能使用。
	//不懂这个地址是用来干什么的。

	createAndStartCluster(cli, d)						//创建并开始cluster
	//swarm

	// Restart all autostart containers which has a swarm endpoint
	// and is not yet running now that we have successfully
	// initialized the cluster.
	d.RestartSwarmContainers()							//重启swarm容器。
	//重新启动所有具有swarm端点并且在我们成功初始化集群后尚未运行的autostart容器。
	
	newRouterOptions(cli.Config, d)						//配置router
	//不懂

	initRouter(routerOptions)							//初始化router
	//不懂
	
	// ProcessClusterNotifications gets changes from store and add them to event list
	go d.ProcessClusterNotifications(ctx, c.GetWatchStream())//处理cluster集群信号。
	//ProcessClusterNotifications从存储中获取更改并将其添加到事件列表中

	cli.setupConfigReloadTrap()							//SIGHUP信号的时候,reload config。

	// The serve API routine never exits unless an error occurs
	// We need to start it as a goroutine and wait on it so
	// daemon doesn't exit
	serveAPIWait := make(chan error)
	go cli.api.Wait(serveAPIWait)						//API server 开始监听

	// after the daemon is done setting up we can notify systemd api
	notifySystem()										//空函数?

	// Daemon is fully initialized and handling API traffic
	// Wait for serve API to complete
	errAPI := <-serveAPIWait
	c.Cleanup()

	shutdownDaemon(d)

	// Stop notification processing and any background processes
	cancel()

	if errAPI != nil {
		return errors.Wrap(errAPI, "shutting down due to ServeAPI error")
	}

	logrus.Info("Daemon shutdown complete")
	return nil
}

在start()函数里,首先在函数loadDaemonCliConfig()中,(在此之前会调用config包,在该包中会调用func installConfigFlags()函数。),读取cli客户端发出来的设置信息,其中包括docker运行时所使用的的root目录(getDaemonConfDir()函数),root用户默认是/etc/docker。随后调用CreateDaemonRoot(),将docker运行时所使用的root目录设置为/etc/docker。
随后调用NewDaemon()函数创建daemon实例。

newDaemon()函数

代码路径:moby/daemon/daemon.go:Line:738
调用位置:

注意事项:

  1. daemon在与containerd的通信过程采用grpc。
// NewDaemon sets up everything for the daemon to be able to service
// requests from the webserver.
func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.Store) (daemon *Daemon, err error) {
	setDefaultMtu(config)						//设置默认的MTU。默认Mtu为1500

	registry.NewService(config.ServiceOptions)	//生成registry Service实例。

	// Ensure that we have a correct root key limit for launching containers.
	ModifyRootKeyLimit()						//和能启动的容器数量有关
	//ModifyRootKeyLimit检查根密钥限制是否设置为至少1000000,并将其与分配给密钥的maxbytes一起以25:1的倍率更改为该限制。
	//不懂

	// Ensure we have compatible and valid configuration options
	verifyDaemonSettings(config)				//配置信息校验
	
	// Do we have a disabled network?
	isBridgeNetworkDisabled(config)				//判断网桥是否禁用
	
	// Setup the resolv.conf
	//如果未指定,setupResolvConf将设置相应的resolv.conf文件。
	//当systemd resolved运行时,默认的/etc/resolv.conf指向localhost。在本例中,获取位于不同路径中的备用配置文件,以便容器可以使用它。
	//在所有其他情况下,返回默认值。
	setupResolvConf(config)						//设置resolv.conf配置文件位置,默认是/etc/resolv.conf

	// Verify the platform is supported as a daemon
	if !platformSupported {						//检查系统是否支持 - platformSupported 为系统常量。在代码中为常量true
		return nil, errSystemNotSupported
	}

	// Validate platform-specific requirements
	if err := checkSystem(); err != nil {		//系统平台校验,内核版本是否满足要求
												//docker 1.11以上不再支持3.4以下的内核版本。
		return nil, err
	}

	setupRemappedRoot(config)					//将容器内的用户映射为宿主机上的普通用户。
	idMapping.RootPair()						//创建根uid,gid
	//上面两个语句完成映射。没看懂源码
	
	setupDaemonProcess(config)					// daemon进程其他设置:设置OOM Killer和MayDetachMounts

	// set up the tmpDir to use a canonical path
	prepareTempDir(config.Root, rootIDs)		//准备临时文件夹
	//prepareTempDir准备并返回用于临时文件的默认目录。如果它不存在,则创建它。如果存在,则删除其内容。
	
	fileutils.ReadSymlinkedDirectory(tmp)		//给tmp临时文件夹创建一个符号链接。
	//ReadSymlinkedDirectory返回符号链接的目标目录。
	//符号链接的目标可能不是文件。
	
	if runtime.GOOS == "windows" {
		if _, err := os.Stat(realTmp); err != nil && os.IsNotExist(err) {
			if err := system.MkdirAll(realTmp, 0700); err != nil {
				return nil, fmt.Errorf("Unable to create the TempDir (%s): %s", realTmp, err)
			}
		}
		os.Setenv("TEMP", realTmp)
		os.Setenv("TMP", realTmp)
	} else {
		os.Setenv("TMPDIR", realTmp)
	}

	d := &Daemon{
		configStore: config,
		PluginStore: pluginStore,
		startupDone: make(chan struct{}),
	}
	// Ensure the daemon is properly shutdown if there is a failure during
	// initialization
	defer func() {
		if err != nil {
			if err := d.Shutdown(); err != nil {
				logrus.Error(err)
			}
		}
	}()

	if err := d.setGenericResources(config); err != nil {
		return nil, err
	}
	// set up SIGUSR1 handler on Unix-like systems, or a Win32 global event
	// on Windows to dump Go routine stacks
	stackDumpDir := config.Root
	if execRoot := config.GetExecRoot(); execRoot != "" {
		stackDumpDir = execRoot
	}
	//Root directory for execution state files
	//execRoot是执行状态文件的根目录
	
	d.setupDumpStackTrap(stackDumpDir)			//配置docker的goroutine堆栈输出目录。注册SIGUSR1信号。
	//在这个函数内注册的USR1信号,所以可以使用在命令行使用 kill -s USR1 $(pidof dockerd)来调取docker运行堆栈信息。
	//如果execRoot不为空,则在execRoot目录下。(默认execRoot是/var/run/docker)
	//如果execRoot为空,则在Root目录下。(默认Root是/var/lib/docker)

	d.setupSeccompProfile()						//设置linux内核的Seccomp,用于限制系统调用
	//没懂

	// Set the default isolation mode (only applicable on Windows)
	d.setDefaultIsolation()
	//设置默认隔离模式,仅windows下使用

	configureMaxThreads(config)					//linux下面设置最大线程 /proc/sys/kernel/threads-max
	
	// ensureDefaultAppArmorProfile does nothing if apparmor is disabled
	ensureDefaultAppArmorProfile()				//代码中直接return nil?
	// 如果linux内核支持安全模块AppArmor(AppArmor允许系统管理员将每个程序与一个安全配置文件关联,从而限制程序的功能。
	//简单的说,AppArmor是与SELinux类似的一个访问控制系统,通过它你可以指定程序可以读、写或运行哪些文件,是否可以打开网络端口等。),则判定是否加载,如加载则设置默认的profile

	daemonRepo := filepath.Join(config.Root, "containers")
	idtools.MkdirAllAndChown(daemonRepo, 0700, rootIDs)//创建/var/lib/docker/containers

	// Create the directory where we'll store the runtime scripts (i.e. in
	// order to support runtimeArgs)
	daemonRuntimes := filepath.Join(config.Root, "runtimes")
	system.MkdirAll(daemonRuntimes, 0700)		//创建/var/lib/docker/runtimes
	//创建存储运行时脚本的目录(即为了支持runtimeArgs)
	
	d.loadRuntimes()							//初始化runtime

	//windows系统中创建credentialspecs目录
	if runtime.GOOS == "windows" {
		if err := system.MkdirAll(filepath.Join(config.Root, "credentialspecs"), 0); err != nil {
			return nil, err
		}
	}

	// On Windows we don't support the environment variable, or a user supplied graphdriver
	// as Windows has no choice in terms of which graphdrivers to use. It's a case of
	// running Windows containers on Windows - windowsfilter, running Linux containers on Windows,
	// lcow. Unix platforms however run a single graphdriver for all containers, and it can
	// be set through an environment variable, a daemon start parameter, or chosen through
	// initialization of the layerstore through driver priority order for example.
	d.graphDrivers = make(map[string]string)
	layerStores := make(map[string]layer.Store)
	if runtime.GOOS == "windows" {
		d.graphDrivers[runtime.GOOS] = "windowsfilter"
		//判断windows是否支持LCOW容器
		if system.LCOWSupported() {
			d.graphDrivers["linux"] = "lcow"
		}
	} else {									//linux
		driverName := os.Getenv("DOCKER_DRIVER")//设置graphdriver
		if driverName == "" {
			driverName = config.GraphDriver
		} else {
			logrus.Infof("Setting the storage driver from the $DOCKER_DRIVER environment variable (%s)", driverName)
		}
		d.graphDrivers[runtime.GOOS] = driverName // May still be empty. Layerstore init determines instead.
	}

	d.RegistryService = registryService
	logger.RegisterPluginGetter(d.PluginStore)	//插件仓库
	//不懂

	d.listenMetricsSock()						//监听metrics.sock
	//位置在 execRoot/metrics.sock

	// 详见: mount.Mount(sockPath, pluginSockPath, "none", "bind,ro")
	registerMetricsPluginCallback(d.PluginStore, metricsSockPath)
	// 没看懂



	//以下为grpc
	gopts := []grpc.DialOption{
		grpc.WithInsecure(),
		grpc.WithBackoffMaxDelay(3 * time.Second),
		grpc.WithDialer(dialer.Dialer),

		// TODO(stevvooe): We may need to allow configuration of this on the client.
		grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(defaults.DefaultMaxRecvMsgSize)),
		grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(defaults.DefaultMaxSendMsgSize)),
	}


	//创建containerdCli
	if config.ContainerdAddr != "" {
		d.containerdCli, err = containerd.New(config.ContainerdAddr, containerd.WithDefaultNamespace(config.ContainerdNamespace), containerd.WithDialOpts(gopts), containerd.WithTimeout(60*time.Second))
		if err != nil {
			return nil, errors.Wrapf(err, "failed to dial %q", config.ContainerdAddr)
		}
	}

	//创建pluginExec
	createPluginExec := func(m *plugin.Manager) (plugin.Executor, error) {
		var pluginCli *containerd.Client

		// Windows is not currently using containerd, keep the
		// client as nil
		if config.ContainerdAddr != "" {
			pluginCli, err = containerd.New(config.ContainerdAddr, containerd.WithDefaultNamespace(config.ContainerdPluginNamespace), containerd.WithDialOpts(gopts), containerd.WithTimeout(60*time.Second))
			if err != nil {
				return nil, errors.Wrapf(err, "failed to dial %q", config.ContainerdAddr)
			}
		}

		return pluginexec.New(ctx, getPluginExecRoot(config.Root), pluginCli, config.ContainerdPluginNamespace, m)
	}

	//创建插件管理器
	// Plugin system initialization should happen before restore. Do not change order.
	d.pluginManager, err = plugin.NewManager(plugin.ManagerConfig{
		Root:               filepath.Join(config.Root, "plugins"),
		ExecRoot:           getPluginExecRoot(config.Root),
		Store:              d.PluginStore,
		CreateExecutor:     createPluginExec,
		RegistryService:    registryService,
		LiveRestoreEnabled: config.LiveRestoreEnabled,
		LogPluginEvent:     d.LogPluginEvent, // todo: make private
		AuthzMiddleware:    config.AuthzMiddleware,
	})


	d.setupDefaultLogConfig()					//设置默认日志信息

	//下面一大段都是在创建graphDrivers
	//首先,从config中读取GraphDriver。默认配置文件中,GraphDriver为空,GraphOptions也为空。
	//其次,如果是空,则从环境变量DOCKER_DRIVER中读取grapDriver。
	//最后,如果用户没有配置环境变量的DOCKER_DRIVER。则遍历系统的优先级存储系统
	//linux中,priority = "btrfs,zfs,overlay2,aufs,overlay,devicemapper,vfs"
	//windows中,priority = "windowsfilter"
	//以上内容在newstoreFromOptions中的New函数中实现的。
	//从driverStore创建layerStore。
	for operatingSystem, gd := range d.graphDrivers {
		layerStores[operatingSystem], err = layer.NewStoreFromOptions(layer.StoreOptions{
			Root:                      config.Root,
			MetadataStorePathTemplate: filepath.Join(config.Root, "image", "%s", "layerdb"),
			GraphDriver:               gd,
			GraphDriverOptions:        config.GraphOptions,
			IDMapping:                 idMapping,
			PluginGetter:              d.PluginStore,
			ExperimentalEnabled:       config.Experimental,
			OS:                        operatingSystem,
		})
		if err != nil {
			return nil, err
		}

		// As layerstore initialization may set the driver
		d.graphDrivers[operatingSystem] = layerStores[operatingSystem].DriverName()
	}
	//不懂为什么是overlay2而不是btrfs。

	// Configure and validate the kernels security support. Note this is a Linux/FreeBSD
	// operation only, so it is safe to pass *just* the runtime OS graphdriver.
	configureKernelSecuritySupport(config, d.graphDrivers[runtime.GOOS])
	//配置并验证内核安全支持。注意,这只是一个Linux/FreeBSD操作,因此传递*只是*运行时OS graphdriver是安全的。

	//创建image根目录。/var/lib/docker/image
	imageRoot := filepath.Join(config.Root, "image", d.graphDrivers[runtime.GOOS])

	//创建imagedb目录。/var/lib/docker/image/imagedb
	//imagedb目录是镜像数据库
	//https://blog.csdn.net/weixin_39548163/article/details/118177663
	image.NewFSStoreBackend(filepath.Join(imageRoot, "imagedb"))	//创建仓库后端的文件系统

	lgrMap := make(map[string]image.LayerGetReleaser)
	for os, ls := range layerStores {
		lgrMap[os] = ls
	}

	//在 imagedb 目录下新建两个目录,content/sha256和metadata/sha256,存储镜像内容和镜像元数据
	image.NewImageStore(ifs, lgrMap)								//创建镜像仓库实例

	//配置Volume, local.New 创建目录 /var/lib/docker/volumes
	// Configure the volumes driver
	volumesservice.NewVolumeService(config.Root, d.PluginStore, rootIDs, d)	//创建volume drive实例
	//1.创建pluginstore实例。
	//2.创建/var/lib/docker/volumes
	//3.创建/var/lib/docker/volumes/metadata.db(使用go.etcd.io/bbolt库操作的db)这个是volumeStore。
	//4.返回volumeService实例。包含volumeStore和Store。volumeStore结构体中也有Store结构体。

	//LoadOrCreateTrustKey尝试在给定路径加载libtrust密钥,否则将生成一个新路径
	//TODO:这应该更多地使用libtrust.LoadOrCreateTrustKey,这可能需要重构或将此函数移动到libtrust中
	loadOrCreateTrustKey(config.TrustKeyPath)
	//加载或者创建key,/etc/docker/key.json

	//创建trustdir,/var/lib/docker/trust
	trustDir := filepath.Join(config.Root, "trust")
	system.MkdirAll(trustDir, 0700)

	// We have a single tag/reference store for the daemon globally. However, it's
	// stored under the graphdriver. On host platforms which only support a single
	// container OS, but multiple selectable graphdrivers, this means depending on which
	// graphdriver is chosen, the global reference store is under there. For
	// platforms which support multiple container operating systems, this is slightly
	// more problematic as where does the global ref store get located? Fortunately,
	// for Windows, which is currently the only daemon supporting multiple container
	// operating systems, the list of graphdrivers available isn't user configurable.
	// For backwards compatibility, we just put it under the windowsfilter
	// directory regardless.
	filepath.Join(imageRoot, `repositories.json`)		//创建refstore。位置。/var/lib/docker/image/overlay2/repositories.json
	//NewReferenceStore创建一个新的引用存储,绑定到一个文件路径,其中引用集以JSON格式序列化。
	refstore.NewReferenceStore(refStoreLocation)		//创建一个新的引用存储,绑定到一个文件路径上。
	//创建TagStore。用于管理存储镜像的仓库列表,Repositories记录了镜像仓库的映射,referencesByIDCache记录了镜像ID和镜像全名的映射


	//我们为守护进程全局提供了一个标记/引用存储。
	//然而,它储存在graphdriver下面。在只支持单个容器操作系统,但支持多个可选graphdriver的主机平台上,这意味着根据选择的graphdriver,全局参考存储在其中。
	//对于支持多个容器操作系统的平台,这稍微有点问题,因为全局ref存储在哪里?
	//幸运的是,对于目前唯一支持多容器操作系统的守护进程Windows,可用的GraphDriver列表不是用户可配置的。
	//为了向后兼容,我们只是将其放在windowsfilter目录下。

	// NewFSMetadataStore creates a new filesystem-based metadata store.
	dmetadata.NewFSMetadataStore(filepath.Join(imageRoot, "distribution"))//NewFSMetadataStore创建一个新的基于文件系统的元数据存储。
	//返回的是文件位置。文件位置在/var/lib/image/overlay2/distribution
	
	// Discovery is only enabled when the daemon is launched with an address to advertise.  When
	// initialized, the daemon is registered and we can store the discovery backend as it's read-only
	d.initDiscovery(config)							//创建discoveryWatcher实例
	//与cluster相关。需要cluster的advertiseaddress、clusterOpts和clusterStoure
	//只有在使用要公布的地址启动守护进程时,才会启用发现。初始化后,将注册守护进程,我们可以将发现后端存储为只读。
	//不懂

	sysInfo := sysinfo.New(false)					//获取系统信息。存储在sysInfo结构体中
	//获取系统信息, 包括cgroup、网络配置(网桥/iptables)和linux安全(AppArmor/Seccomp)等
	// Check if Devices cgroup is mounted, it is hard requirement for container security,
	// on Linux.
	//New返回一个新的SysInfo,使用文件系统检测内核支持哪些功能。
	//如果'quiet'为'false',则每当发生错误或存在错误配置时,日志中将打印警告。
	
	//确保linux系统下,支持Cgroup
	if runtime.GOOS == "linux" && !sysInfo.CgroupDevicesEnabled {
		return nil, errors.New("Devices cgroup isn't mounted")
	}

	//以下大段内容均为赋值和初始化结构体。
	d.ID = trustKey.PublicKey().KeyID()
	d.repository = daemonRepo
	d.containers = container.NewMemoryStore()
	if d.containersReplica, err = container.NewViewDB(); err != nil {
		return nil, err
	}
	d.execCommands = exec.NewStore()
	d.idIndex = truncindex.NewTruncIndex([]string{})
	d.statsCollector = d.newStatsCollector(1 * time.Second)

	d.EventsService = events.New()
	d.root = config.Root
	d.idMapping = idMapping
	d.seccompEnabled = sysInfo.Seccomp
	d.apparmorEnabled = sysInfo.AppArmor

	d.linkIndex = newLinkIndex()

	// TODO: imageStore, distributionMetadataStore, and ReferenceStore are only
	// used above to run migration. They could be initialized in ImageService
	// if migration is called from daemon/images. layerStore might move as well.
	d.imageService = images.NewImageService(images.ImageServiceConfig{
		ContainerStore:            d.containers,
		DistributionMetadataStore: distributionMetadataStore,
		EventsService:             d.EventsService,
		ImageStore:                imageStore,
		LayerStores:               layerStores,
		MaxConcurrentDownloads:    *config.MaxConcurrentDownloads,
		MaxConcurrentUploads:      *config.MaxConcurrentUploads,
		ReferenceStore:            rs,
		RegistryService:           registryService,
		TrustKey:                  trustKey,
	})

	
	go d.execCommandGC()			//新建协程清理容器不需要的命令
	//每5分钟清理一次。

	d.containerd, err = libcontainerd.NewClient(ctx, d.containerdCli, filepath.Join(config.ExecRoot, "containerd"), config.ContainerdNamespace, d)
	//创建和daemon相关的容器客户端libcontainerd
	//重点!!!
	//起一个新协程来监听事件。
	//目前看到的事件有:
	//Create,Start,Exit,OOM,ExecAdded,ExecStarted,Paused,Resumned
	//与GRPC通信获取的event

	// 从 /var/lib/docker/containers 读取所有目录,下面的文件夹的名字就是容器的id,
	//存入 containers map[string]*container.Container 信息并注册如 daemon 结构体中。
	//包括 start remove 操作 containers注册容器。注册信息都在内存中,启动daemon时需要重新进行注册
	d.restore()
	close(d.startupDone)

	// FIXME: this method never returns an error
	info, _ := d.SystemInfo()					//daemon运行的主机的配置信息。

	engineInfo.WithValues(						//配置信息
		dockerversion.Version,
		dockerversion.GitCommit,
		info.Architecture,
		info.Driver,
		info.KernelVersion,
		info.OperatingSystem,
		info.OSType,
		info.OSVersion,
		info.ID,
	).Set(1)
	engineCpus.Set(float64(info.NCPU))
	engineMemory.Set(float64(info.MemTotal))

	gd := ""
	for os, driver := range d.graphDrivers {
		if len(gd) > 0 {
			gd += ", "
		}
		gd += driver
		if len(d.graphDrivers) > 1 {
			gd = fmt.Sprintf("%s (%s)", gd, os)
		}
	}
	logrus.WithFields(logrus.Fields{
		"version":        dockerversion.Version,
		"commit":         dockerversion.GitCommit,
		"graphdriver(s)": gd,
	}).Info("Docker daemon")

	return d, nil
}

containerd.New()

github.com/containerd/containerd/client.go:Line-79:New()
调用位置:docker/docker/daemon/daemon.go:Line-926:d.containerdCli, err = containerd.New

d.restore()

docker/docker/daemon/daemon.go:Line-207:restore()
调用位置:docker/docker/daemon/daemon.go:Line-1063:d.restore()

volumesservice.NewVolumeService()

docker/docker/volume/service/service.go
调用位置:docker/docker/daemon/daemon.go:Line-972:volumesservice.NewVolumeService()

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值