Docker 源码(一)1.1

270 阅读5分钟

newDaemonCommand

cmd.Flags()

// 返回设置一个FlagSet
func (c *Command) Flags() *flag.FlagSet {
   
   // 当 Command 的 pflgs-FlagSet为空
   if c.flags == nil {
      // 创建并初始化一个 FlagSet
      c.flags = flag.NewFlagSet(c.Name(), flag.ContinueOnError)
      if c.flagErrorBuf == nil {
         c.flagErrorBuf = new(bytes.Buffer)
      }
      c.flags.SetOutput(c.flagErrorBuf)
   }
   // 返回FlagSet,指针类型
   return c.flags
}
/**
 * 创建并初始化一个 FlagSet
 * @params name          设置 FlagSet name
 * @params errorHandling 如何处理标记解析错误 int 类型 (ContinueOnError ExitOnError PanicOnError)
 */
func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet {
   f := &FlagSet{
      name:          name,
      errorHandling: errorHandling,
      argsLenAtDash: -1,
      interspersed:  true,
      SortFlags:     true,
   }
   return f
}

// FlagSet(标志集): 一组已定义的标志
type FlagSet struct {}

runDaemon(opts)


func runDaemon(opts *daemonOptions) error {
   // NewDaemonCli: 返回 daemon CLI
   daemonCli := NewDaemonCli()
   // 调用start
   return daemonCli.start(opts)
}
// NewDaemonCli 返回 daemon CLI
func NewDaemonCli() *DaemonCli {
   return &DaemonCli{}
}
func (cli *DaemonCli) start(opts *daemonOptions) (err error) {
   // TLS 加密配置
   opts.setDefaultOptions()

   // Docker 信息配置 Debug Hosts LogLevel configFile(具体请看:1.1.1)
   if cli.Config, err = loadDaemonCliConfig(opts); err != nil {
      return err
   }
   
   // 检查是否存在以删除的属性 ClusterAdvertise ClusterOpts ClusterStore
   if err := checkDeprecatedOptions(cli.Config); err != nil {
      return err
   }

   // 返回 APIserver 配置 如果err != nil 返回err
   serverConfig, err := newAPIServerConfig(cli.Config)
   if err != nil {
      return err
   }

   // 校验 daemon 配置 bool 类型 false 退出
   if opts.Validate {
      // If config wasn't OK we wouldn't have made it this far.
      fmt.Fprintln(os.Stderr, "configuration OK")
      return nil
   }

   // 配置ProxyENV HTTP HTTPS NoProxy
   configureProxyEnv(cli.Config)
   
   // 判断是否设置输出等级,并设置输出log格式和等级
   configureDaemonLogs(cli.Config)

   logrus.Info("Starting up")

   // 配置文件,标志
   cli.configFile = &opts.configFile
   cli.flags = opts.flags

   // Debug bool 类型 如果为出 Debug env 设置为true 并设置log等级
   if cli.Config.Debug {
      debug.Enable()
   }

   // 实验性功能 警告日志 正在进行实验性构建
   if cli.Config.Experimental {
      logrus.Warn("Running experimental build")
   }

   // 是否是 RootLess 模式启动,此模式功能有限
   if cli.Config.IsRootless() {
      logrus.Warn("Running in rootless mode. This mode has feature limitations.")
   }
   
   // 是否是RootlessKit (非root用户安装、启动docker) 运行
   if rootless.RunningWithRootlessKit() {
      logrus.Info("Running with RootlessKit integration")
      // 只有再 Rootless 模式下 才能运行 RootlessKit模式
      if !cli.Config.IsRootless() {
         return fmt.Errorf("rootless mode needs to be enabled for running with RootlessKit")
      }
   }

   // return human-friendly error before creating files
   // 当前系统是否是linux,当前用户ID 是否是root 有效用户,不是则输出错误信息
   if runtime.GOOS == "linux" && os.Geteuid() != 0 {
      return fmt.Errorf("dockerd needs to be started with root privileges. To run dockerd in rootless mode as an unprivileged user, see https://docs.docker.com/go/rootless/")
   }

    //设置umask默认权限0022,对应文件目录权限为0755->即用户具有读/写/执行权限,组用户和其它用户具有读写权限
   if err := setDefaultUmask(); err != nil {
      return err
   }

   // 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 根路径并返回 docker 根路径
   if err := daemon.CreateDaemonRoot(cli.Config); err != nil {
      return err
   }

   // 创建exec路径,返回err != nil 返回错误
   if err := system.MkdirAll(cli.Config.ExecRoot, 0700); err != nil {
      return err
   }

   // 保存一些 Runtime 目录  初始化 slice 默认值是 cli.Config.ExecRoot
   potentiallyUnderRuntimeDir := []string{cli.Config.ExecRoot}

   // pid 文件路径是否为空
   if cli.Pidfile != "" {
      // 使用指定 Pidfile 创建 PIDfile err != nil 输出错误日志
      pf, err := pidfile.New(cli.Pidfile)
      if err != nil {
         return errors.Wrap(err, "failed to start daemon")
      }
      
      // PIDfile 路径保存在 potentiallyUnderRuntimeDir slice中
      potentiallyUnderRuntimeDir = append(potentiallyUnderRuntimeDir, cli.Pidfile)
      
      // func(外层) 声明周期结束之前 移除 pf err != nil 输出错误日志
      defer func() {
         if err := pf.Remove(); err != nil {
            logrus.Error(err)
         }
      }()
   }

   // 是否是 Rootless 模式运行
   if cli.Config.IsRootless() {
      // Set sticky bit if XDG_RUNTIME_DIR is set && the file is actually under XDG_RUNTIME_DIR
      // 非 linux 用户不支持 err != nil 并输出错误
      //go:build !linux 非 linux 才会build
      // +build !linux
      if _, err := homedir.StickRuntimeDirContents(potentiallyUnderRuntimeDir); err != nil {
         // StickRuntimeDirContents returns nil error if XDG_RUNTIME_DIR is just unset
         logrus.WithError(err).Warn("cannot set sticky bit on files under XDG_RUNTIME_DIR")
      }
   }

   // 根据 serverConfig 配置一个APIserver实例,端口、unix套接字
   cli.api = apiserver.New(serverConfig)

   // 根据 cli serverConfig 配置监听 hosts err != nil 输出错误信息
   hosts, err := loadListeners(cli, serverConfig)
   if err != nil {
      return errors.Wrap(err, "failed to load listeners")
   }

   // 初始化containerD waitForContainerDShutdown 不为空 退出后 10秒没有响应强制结束
   ctx, cancel := context.WithCancel(context.Background())
   waitForContainerDShutdown, err := cli.initContainerD(ctx)
   if waitForContainerDShutdown != nil {
      defer waitForContainerDShutdown(10 * time.Second)
   }
   if err != nil {
      cancel()
      return err
   }
   defer cancel()

   stopc := make(chan bool)
   defer close(stopc)

   trap.Trap(func() {
      cli.stop()
      <-stopc // wait for daemonCli.start() to return
   }, logrus.StandardLogger())

   // Notify that the API is active, but before daemon is set up.
   // preNotifyReady 在API处于活动状态时,在守护进程处于活动状态之前,向主机发送消息
   // 备注:再 linux 下是个空函数,再windows下 有这个实现 所以 why???
   preNotifyReady()

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

   // NewExperimentalMiddleware: 返回一个实验中间件
   // NewVersionMiddleware:      使用默认版本创建新的VersionMiddleware
   // 如果 CorsHeaders 不为空 增加 cors 请求头
   // 设置 cli.authorization 是否允许动态加载 已授权 插件
   if err := cli.initMiddlewares(cli.api, serverConfig, pluginStore); err != nil {
      logrus.Fatalf("Error creating middlewares: %v", err)
   }

   //通过containerd, config, pluginStore来真正创建了daemon
   d, err := daemon.NewDaemon(ctx, cli.Config, pluginStore)
   if err != nil {
      return errors.Wrap(err, "failed to start daemon")
   }

   // 监听 hosts 为 key 值设置为 true true 代表开始监听
   d.StoreHosts(hosts)

   // validate after NewDaemon has restored enabled plugins. Don't change order.
   // 验证使用--authorization plugin 标志请求的插件是否是权限的 plugin
   if err := validateAuthzPlugins(cli.Config.AuthorizationPlugins, pluginStore); err != nil {
      return errors.Wrap(err, "failed to validate authorization plugin")
   }
   
   // daemon 与 DaemonCLI 关联
   cli.d = d

   // daemon.json: metrics-addr,对当前 addr 监控,统计,测量
   if err := startMetricsServer(cli.Config.MetricsAddress); err != nil {
      return errors.Wrap(err, "failed to start metrics server")
   }

   // 创建并返回 cluster
   c, err := createAndStartCluster(cli, d)
   if err != nil {
      logrus.Fatalf("Error starting cluster component: %v", err)
   }

   // Restart all autostart containers which has a swarm endpoint
   // and is not yet running now that we have successfully
   // initialized the cluster.
   // 重新启动所有具有swarm端点且现在尚未运行的autostart容器,成功初始化集群
   d.RestartSwarmContainers()

   logrus.Info("Daemon has completed initialization")

   // 创建 routerOptions
   routerOptions, err := newRouterOptions(cli.Config, d)
   if err != nil {
      return err
   }
   
   // cli.api,cluster 与 routerOptions 关联
   routerOptions.api = cli.api
   routerOptions.cluster = c

   // 初始化路由
   initRouter(routerOptions)

   go d.ProcessClusterNotifications(ctx, c.GetWatchStream())

   cli.setupConfigReloadTrap()

   // 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)
   
   // 1. 监听 APIserver 执行情况 报错时 通过管道 serveAPIWait 传出
   go cli.api.Wait(serveAPIWait)

   // after the daemon is done setting up we can notify systemd api
   // 通知 Daemon 已经安装完成,api 服务可用
   notifyReady()

   // Daemon is fully initialized and handling API traffic
   // Wait for serve API to complete
   // 等待 APIserver 报错,未报错则阻塞,直到 APIserver 执行完成
   errAPI := <-serveAPIWait
   // 报错:调用 Cleanup 清理停止活动swarm节点
   c.Cleanup()

   // notify systemd that we're shutting down
   // 服务关闭时,通知系统
   notifyStopping()
   
   // 关闭 Daemon
   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
}