本文源码基于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)/
代码位置:/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。
代码路径:/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()函数。
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
}
代码路径:/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()函数。
代码路径:/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实例。
代码路径:moby/daemon/daemon.go:Line:738
调用位置:
注意事项:
- 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
}
github.com/containerd/containerd/client.go:Line-79:New()
调用位置:docker/docker/daemon/daemon.go:Line-926:d.containerdCli, err = containerd.New
docker/docker/daemon/daemon.go:Line-207:restore()
调用位置:docker/docker/daemon/daemon.go:Line-1063:d.restore()
docker/docker/volume/service/service.go
调用位置:docker/docker/daemon/daemon.go:Line-972:volumesservice.NewVolumeService()