func initRouter(s *apiserver.Server, d *daemon.Daemon, c *cluster.Cluster) { decoder := runconfig.ContainerDecoder{} routers := []router.Router{ // we need to add the checkpoint router before the container router or the DELETE gets masked checkpointrouter.NewRouter(d, decoder), container.NewRouter(d, decoder), image.NewRouter(d, decoder), systemrouter.NewRouter(d, c), volume.NewRouter(d), build.NewRouter(dockerfile.NewBuildManager(d)), swarmrouter.NewRouter(c), pluginrouter.NewRouter(plugin.GetManager()), } if d.NetworkControllerEnabled() { routers = append(routers, network.NewRouter(d, c)) } if d.HasExperimental() { for _, r := range routers { for _, route := range r.Routes() { if experimental, ok := route.(router.ExperimentalRoute); ok { experimental.Enable() } } } } s.InitRouter(utils.IsDebugEnabled(), routers...) }
func (cli *DaemonCli) reloadConfig() { reload := func(config *daemon.Config) { // Reload the authorization plugin cli.authzMiddleware.SetPlugins(config.AuthorizationPlugins) if err := cli.d.Reload(config); err != nil { logrus.Errorf("Error reconfiguring the daemon: %v", err) return } if config.IsValueSet("debug") { debugEnabled := utils.IsDebugEnabled() switch { case debugEnabled && !config.Debug: // disable debug utils.DisableDebug() cli.api.DisableProfiler() case config.Debug && !debugEnabled: // enable debug utils.EnableDebug() cli.api.EnableProfiler() } } } if err := daemon.ReloadConfiguration(*cli.configFile, cli.flags, reload); err != nil { logrus.Error(err) } }
func initRouter(s *apiserver.Server, d *daemon.Daemon) { s.InitRouter(utils.IsDebugEnabled(), container.NewRouter(d), image.NewRouter(d), network.NewRouter(d), systemrouter.NewRouter(d), volume.NewRouter(d), build.NewRouter(dockerfile.NewBuildManager(d))) }
// Reload reads configuration changes and modifies the // server according to those changes. // Currently, only the --debug configuration is taken into account. func (s *Server) Reload(config *daemon.Config) { debugEnabled := utils.IsDebugEnabled() switch { case debugEnabled && !config.Debug: // disable debug utils.DisableDebug() s.routerSwapper.Swap(s.createMux()) case config.Debug && !debugEnabled: // enable debug utils.EnableDebug() s.routerSwapper.Swap(s.createMux()) } }
// Reload reads configuration changes and modifies the // server according to those changes. // Currently, only the --debug configuration is taken into account. func (s *Server) Reload(debug bool) { debugEnabled := utils.IsDebugEnabled() switch { case debugEnabled && !debug: // disable debug utils.DisableDebug() s.routerSwapper.Swap(s.createMux()) case debug && !debugEnabled: // enable debug utils.EnableDebug() s.routerSwapper.Swap(s.createMux()) } }
func initRouter(s *apiserver.Server, d *daemon.Daemon) { routers := []router.Router{ container.NewRouter(d), image.NewRouter(d), 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...) }
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(d), } if d.NetworkControllerEnabled() { routers = append(routers, network.NewRouter(d)) } s.InitRouter(utils.IsDebugEnabled(), routers...) }
func initRouter(s *apiserver.Server, d *daemon.Daemon, c *cluster.Cluster) { decoder := runconfig.ContainerDecoder{} routers := []router.Router{ container.NewRouter(d, decoder), image.NewRouter(d, decoder), systemrouter.NewRouter(d, c), volume.NewRouter(d), build.NewRouter(dockerfile.NewBuildManager(d)), swarmrouter.NewRouter(c), } if d.NetworkControllerEnabled() { routers = append(routers, network.NewRouter(d, c)) } routers = addExperimentalRouters(routers) s.InitRouter(utils.IsDebugEnabled(), routers...) }
// createMux initializes the main router the server uses. func (s *Server) createMux() *mux.Router { m := mux.NewRouter() if utils.IsDebugEnabled() { profilerSetup(m, "/debug/") } logrus.Debugf("Registering routers") for _, apiRouter := range s.routers { for _, r := range apiRouter.Routes() { f := s.makeHTTPHandler(r.Handler()) logrus.Debugf("Registering %s, %s", r.Method(), r.Path()) m.Path(versionMatcher + r.Path()).Methods(r.Method()).Handler(f) m.Path(r.Path()).Methods(r.Method()).Handler(f) } } return m }
func initRouter(s *apiserver.Server, d *daemon.Daemon, c *cluster.Cluster) { decoder := runconfig.ContainerDecoder{} routers := []router.Router{} // we need to add the checkpoint router before the container router or the DELETE gets masked routers = addExperimentalRouters(routers, d, decoder) routers = append(routers, []router.Router{ container.NewRouter(d, decoder), image.NewRouter(d, decoder), systemrouter.NewRouter(d, c), volume.NewRouter(d), build.NewRouter(dockerfile.NewBuildManager(d)), swarmrouter.NewRouter(c), }...) if d.NetworkControllerEnabled() { routers = append(routers, network.NewRouter(d, c)) } s.InitRouter(utils.IsDebugEnabled(), routers...) }
func (cli *DaemonCli) reloadConfig() { reload := func(config *daemon.Config) { if err := cli.d.Reload(config); err != nil { logrus.Errorf("Error reconfiguring the daemon: %v", err) return } if config.IsValueSet("debug") { debugEnabled := utils.IsDebugEnabled() switch { case debugEnabled && !config.Debug: // disable debug utils.DisableDebug() cli.api.DisableProfiler() case config.Debug && !debugEnabled: // enable debug utils.EnableDebug() cli.api.EnableProfiler() } } } if err := daemon.ReloadConfiguration(*cli.configFile, flag.CommandLine, reload); err != nil { logrus.Error(err) } }
func (daemon *Daemon) restore() error { var ( debug = utils.IsDebugEnabled() currentDriver = daemon.GraphDriverName() containers = make(map[string]*container.Container) ) if !debug { logrus.Info("Loading containers: start.") } dir, err := ioutil.ReadDir(daemon.repository) if err != nil { return err } containerCount := 0 for _, v := range dir { id := v.Name() container, err := daemon.load(id) if !debug && logrus.GetLevel() == logrus.InfoLevel { fmt.Print(".") containerCount++ } if err != nil { logrus.Errorf("Failed to load container %v: %v", id, err) continue } // Ignore the container if it does not support the current driver being used by the graph if (container.Driver == "" && currentDriver == "aufs") || container.Driver == currentDriver { rwlayer, err := daemon.layerStore.GetRWLayer(container.ID) if err != nil { logrus.Errorf("Failed to load container mount %v: %v", id, err) continue } container.RWLayer = rwlayer logrus.Debugf("Loaded container %v", container.ID) containers[container.ID] = container } else { logrus.Debugf("Cannot load container %s because it was created with another graph driver.", container.ID) } } var migrateLegacyLinks bool restartContainers := make(map[*container.Container]chan struct{}) activeSandboxes := make(map[string]interface{}) for _, c := range containers { if err := daemon.registerName(c); err != nil { logrus.Errorf("Failed to register container %s: %s", c.ID, err) continue } if err := daemon.Register(c); err != nil { logrus.Errorf("Failed to register container %s: %s", c.ID, err) continue } // The LogConfig.Type is empty if the container was created before docker 1.12 with default log driver. // We should rewrite it to use the daemon defaults. // Fixes https://github.com/docker/docker/issues/22536 if c.HostConfig.LogConfig.Type == "" { if err := daemon.mergeAndVerifyLogConfig(&c.HostConfig.LogConfig); err != nil { logrus.Errorf("Failed to verify log config for container %s: %q", c.ID, err) continue } } } var wg sync.WaitGroup var mapLock sync.Mutex for _, c := range containers { wg.Add(1) go func(c *container.Container) { defer wg.Done() rm := c.RestartManager(false) if c.IsRunning() || c.IsPaused() { if err := daemon.containerd.Restore(c.ID, libcontainerd.WithRestartManager(rm)); err != nil { logrus.Errorf("Failed to restore %s with containerd: %s", c.ID, err) return } if !c.HostConfig.NetworkMode.IsContainer() && c.IsRunning() { options, err := daemon.buildSandboxOptions(c) if err != nil { logrus.Warnf("Failed build sandbox option to restore container %s: %v", c.ID, err) } mapLock.Lock() activeSandboxes[c.NetworkSettings.SandboxID] = options mapLock.Unlock() } } // fixme: only if not running // get list of containers we need to restart if daemon.configStore.AutoRestart && !c.IsRunning() && !c.IsPaused() && c.ShouldRestart() { mapLock.Lock() restartContainers[c] = make(chan struct{}) mapLock.Unlock() } if c.RemovalInProgress { // We probably crashed in the middle of a removal, reset // the flag. // // We DO NOT remove the container here as we do not // know if the user had requested for either the // associated volumes, network links or both to also // be removed. So we put the container in the "dead" // state and leave further processing up to them. logrus.Debugf("Resetting RemovalInProgress flag from %v", c.ID) c.ResetRemovalInProgress() c.SetDead() c.ToDisk() } // if c.hostConfig.Links is nil (not just empty), then it is using the old sqlite links and needs to be migrated if c.HostConfig != nil && c.HostConfig.Links == nil { migrateLegacyLinks = true } }(c) } wg.Wait() daemon.netController, err = daemon.initNetworkController(daemon.configStore, activeSandboxes) if err != nil { return fmt.Errorf("Error initializing network controller: %v", err) } // migrate any legacy links from sqlite linkdbFile := filepath.Join(daemon.root, "linkgraph.db") var legacyLinkDB *graphdb.Database if migrateLegacyLinks { legacyLinkDB, err = graphdb.NewSqliteConn(linkdbFile) if err != nil { return fmt.Errorf("error connecting to legacy link graph DB %s, container links may be lost: %v", linkdbFile, err) } defer legacyLinkDB.Close() } // Now that all the containers are registered, register the links for _, c := range containers { if migrateLegacyLinks { if err := daemon.migrateLegacySqliteLinks(legacyLinkDB, c); err != nil { return err } } if err := daemon.registerLinks(c, c.HostConfig); err != nil { logrus.Errorf("failed to register link for container %s: %v", c.ID, err) } } group := sync.WaitGroup{} for c, notifier := range restartContainers { group.Add(1) go func(c *container.Container, chNotify chan struct{}) { defer group.Done() logrus.Debugf("Starting container %s", c.ID) // ignore errors here as this is a best effort to wait for children to be // running before we try to start the container children := daemon.children(c) timeout := time.After(5 * time.Second) for _, child := range children { if notifier, exists := restartContainers[child]; exists { select { case <-notifier: case <-timeout: } } } // Make sure networks are available before starting daemon.waitForNetworks(c) if err := daemon.containerStart(c); err != nil { logrus.Errorf("Failed to start container %s: %s", c.ID, err) } close(chNotify) }(c, notifier) } group.Wait() // any containers that were started above would already have had this done, // however we need to now prepare the mountpoints for the rest of the containers as well. // This shouldn't cause any issue running on the containers that already had this run. // This must be run after any containers with a restart policy so that containerized plugins // can have a chance to be running before we try to initialize them. for _, c := range containers { // if the container has restart policy, do not // prepare the mountpoints since it has been done on restarting. // This is to speed up the daemon start when a restart container // has a volume and the volume dirver is not available. if _, ok := restartContainers[c]; ok { continue } group.Add(1) go func(c *container.Container) { defer group.Done() if err := daemon.prepareMountPoints(c); err != nil { logrus.Error(err) } }(c) } group.Wait() if !debug { if logrus.GetLevel() == logrus.InfoLevel && containerCount > 0 { fmt.Println() } logrus.Info("Loading containers: done.") } return nil }
func (cli *DaemonCli) start() { // warn from uuid package when running the daemon uuid.Loggerf = logrus.Warnf flags := flag.CommandLine cli.commonFlags.PostParse() if cli.commonFlags.TrustKey == "" { cli.commonFlags.TrustKey = filepath.Join(getDaemonConfDir(), cliflags.DefaultTrustKeyFile) } cliConfig, err := loadDaemonCliConfig(cli.Config, flags, cli.commonFlags, *cli.configFile) if err != nil { fmt.Fprint(os.Stderr, err) os.Exit(1) } cli.Config = cliConfig if cli.Config.Debug { utils.EnableDebug() } if utils.ExperimentalBuild() { logrus.Warn("Running experimental build") } logrus.SetFormatter(&logrus.TextFormatter{ TimestampFormat: jsonlog.RFC3339NanoFixed, DisableColors: cli.Config.RawLogs, }) if err := setDefaultUmask(); err != nil { logrus.Fatalf("Failed to set umask: %v", err) } if len(cli.LogConfig.Config) > 0 { if err := logger.ValidateLogOpts(cli.LogConfig.Type, cli.LogConfig.Config); err != nil { logrus.Fatalf("Failed to set log opts: %v", err) } } var pfile *pidfile.PIDFile if cli.Pidfile != "" { pf, err := pidfile.New(cli.Pidfile) if err != nil { logrus.Fatalf("Error starting daemon: %v", err) } pfile = pf defer func() { if err := pfile.Remove(); err != nil { logrus.Error(err) } }() } serverConfig := &apiserver.Config{ Logging: true, SocketGroup: cli.Config.SocketGroup, Version: dockerversion.Version, } serverConfig = setPlatformServerConfig(serverConfig, cli.Config) if cli.Config.TLS { tlsOptions := tlsconfig.Options{ CAFile: cli.Config.CommonTLSOptions.CAFile, CertFile: cli.Config.CommonTLSOptions.CertFile, KeyFile: cli.Config.CommonTLSOptions.KeyFile, } if cli.Config.TLSVerify { // server requires and verifies client's certificate tlsOptions.ClientAuth = tls.RequireAndVerifyClientCert } tlsConfig, err := tlsconfig.Server(tlsOptions) if err != nil { logrus.Fatal(err) } serverConfig.TLSConfig = tlsConfig } if len(cli.Config.Hosts) == 0 { cli.Config.Hosts = make([]string, 1) } 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.Fatalf("error parsing -H %s : %v", cli.Config.Hosts[i], err) } protoAddr := cli.Config.Hosts[i] protoAddrParts := strings.SplitN(protoAddr, "://", 2) if len(protoAddrParts) != 2 { logrus.Fatalf("bad format %s, expected PROTO://ADDR", protoAddr) } proto := protoAddrParts[0] addr := protoAddrParts[1] // It's a bad idea to bind to TCP without tlsverify. if proto == "tcp" && (serverConfig.TLSConfig == nil || serverConfig.TLSConfig.ClientAuth != tls.RequireAndVerifyClientCert) { logrus.Warn("[!] DON'T BIND ON ANY IP ADDRESS WITHOUT setting -tlsverify IF YOU DON'T KNOW WHAT YOU'RE DOING [!]") } l, err := listeners.Init(proto, addr, serverConfig.SocketGroup, serverConfig.TLSConfig) if err != nil { logrus.Fatal(err) } // If we're binding to a TCP port, make sure that a container doesn't try to use it. if proto == "tcp" { if err := allocateDaemonPort(addr); err != nil { logrus.Fatal(err) } } logrus.Debugf("Listener created for HTTP on %s (%s)", protoAddrParts[0], protoAddrParts[1]) api.Accept(protoAddrParts[1], l...) } if err := migrateKey(); err != nil { logrus.Fatal(err) } cli.TrustKeyPath = cli.commonFlags.TrustKey registryService := registry.NewService(cli.Config.ServiceOptions) containerdRemote, err := libcontainerd.New(cli.getLibcontainerdRoot(), cli.getPlatformRemoteOptions()...) if err != nil { logrus.Fatal(err) } d, err := daemon.NewDaemon(cli.Config, registryService, containerdRemote) if err != nil { if pfile != nil { if err := pfile.Remove(); err != nil { logrus.Error(err) } } logrus.Fatalf("Error starting daemon: %v", err) } logrus.Info("Daemon has completed initialization") logrus.WithFields(logrus.Fields{ "version": dockerversion.Version, "commit": dockerversion.GitCommit, "graphdriver": d.GraphDriverName(), }).Info("Docker daemon") cli.initMiddlewares(api, serverConfig) initRouter(api, d) reload := func(config *daemon.Config) { if err := d.Reload(config); err != nil { logrus.Errorf("Error reconfiguring the daemon: %v", err) return } if config.IsValueSet("debug") { debugEnabled := utils.IsDebugEnabled() switch { case debugEnabled && !config.Debug: // disable debug utils.DisableDebug() api.DisableProfiler() case config.Debug && !debugEnabled: // enable debug utils.EnableDebug() api.EnableProfiler() } } } setupConfigReloadTrap(*cli.configFile, flags, reload) // 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 api.Wait(serveAPIWait) signal.Trap(func() { api.Close() <-serveAPIWait shutdownDaemon(d, 15) if pfile != nil { if err := pfile.Remove(); err != nil { logrus.Error(err) } } }) // 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 shutdownDaemon(d, 15) containerdRemote.Cleanup() if errAPI != nil { if pfile != nil { if err := pfile.Remove(); err != nil { logrus.Error(err) } } logrus.Fatalf("Shutting down due to ServeAPI error: %v", errAPI) } }
// SystemInfo returns information about the host server the daemon is running on. func (daemon *Daemon) SystemInfo() (*types.Info, error) { kernelVersion := "<unknown>" if kv, err := kernel.GetKernelVersion(); err == nil { kernelVersion = kv.String() } operatingSystem := "<unknown>" if s, err := operatingsystem.GetOperatingSystem(); err == nil { operatingSystem = s } // Don't do containerized check on Windows if runtime.GOOS != "windows" { if inContainer, err := operatingsystem.IsContainerized(); err != nil { logrus.Errorf("Could not determine if daemon is containerized: %v", err) operatingSystem += " (error determining if containerized)" } else if inContainer { operatingSystem += " (containerized)" } } meminfo, err := system.ReadMemInfo() if err != nil { logrus.Errorf("Could not read system memory info: %v", err) } // if we still have the original dockerinit binary from before // we copied it locally, let's return the path to that, since // that's more intuitive (the copied path is trivial to derive // by hand given VERSION) initPath := utils.DockerInitPath("") sysInfo := sysinfo.New(true) var cRunning, cPaused, cStopped int for _, c := range daemon.List() { switch c.StateString() { case "paused": cPaused++ case "running": cRunning++ default: cStopped++ } } v := &types.Info{ ID: daemon.ID, Containers: len(daemon.List()), ContainersRunning: cRunning, ContainersPaused: cPaused, ContainersStopped: cStopped, Images: len(daemon.imageStore.Map()), Driver: daemon.GraphDriverName(), DriverStatus: daemon.layerStore.DriverStatus(), Plugins: daemon.showPluginsInfo(), IPv4Forwarding: !sysInfo.IPv4ForwardingDisabled, BridgeNfIptables: !sysInfo.BridgeNfCallIptablesDisabled, BridgeNfIP6tables: !sysInfo.BridgeNfCallIP6tablesDisabled, Debug: utils.IsDebugEnabled(), NFd: fileutils.GetTotalUsedFds(), NGoroutines: runtime.NumGoroutine(), SystemTime: time.Now().Format(time.RFC3339Nano), ExecutionDriver: daemon.ExecutionDriver().Name(), LoggingDriver: daemon.defaultLogConfig.Type, NEventsListener: daemon.EventsService.SubscribersCount(), KernelVersion: kernelVersion, OperatingSystem: operatingSystem, IndexServerAddress: registry.IndexServer, OSType: platform.OSType, Architecture: platform.Architecture, RegistryConfig: daemon.RegistryService.Config, InitSha1: dockerversion.InitSHA1, InitPath: initPath, NCPU: runtime.NumCPU(), MemTotal: meminfo.MemTotal, DockerRootDir: daemon.configStore.Root, Labels: daemon.configStore.Labels, ExperimentalBuild: utils.ExperimentalBuild(), ServerVersion: dockerversion.Version, ClusterStore: daemon.configStore.ClusterStore, ClusterAdvertise: daemon.configStore.ClusterAdvertise, HTTPProxy: getProxyEnv("http_proxy"), HTTPSProxy: getProxyEnv("https_proxy"), NoProxy: getProxyEnv("no_proxy"), } // TODO Windows. Refactor this more once sysinfo is refactored into // platform specific code. On Windows, sysinfo.cgroupMemInfo and // sysinfo.cgroupCpuInfo will be nil otherwise and cause a SIGSEGV if // an attempt is made to access through them. if runtime.GOOS != "windows" { v.MemoryLimit = sysInfo.MemoryLimit v.SwapLimit = sysInfo.SwapLimit v.OomKillDisable = sysInfo.OomKillDisable v.CPUCfsPeriod = sysInfo.CPUCfsPeriod v.CPUCfsQuota = sysInfo.CPUCfsQuota v.CPUShares = sysInfo.CPUShares v.CPUSet = sysInfo.Cpuset } if hostname, err := os.Hostname(); err == nil { v.Name = hostname } return v, nil }
func (daemon *Daemon) restore() error { var ( debug = utils.IsDebugEnabled() currentDriver = daemon.GraphDriverName() containers = make(map[string]*container.Container) ) if !debug { logrus.Info("Loading containers: start.") } dir, err := ioutil.ReadDir(daemon.repository) if err != nil { return err } for _, v := range dir { id := v.Name() container, err := daemon.load(id) if !debug && logrus.GetLevel() == logrus.InfoLevel { fmt.Print(".") } if err != nil { logrus.Errorf("Failed to load container %v: %v", id, err) continue } // Ignore the container if it does not support the current driver being used by the graph if (container.Driver == "" && currentDriver == "aufs") || container.Driver == currentDriver { rwlayer, err := daemon.layerStore.GetRWLayer(container.ID) if err != nil { logrus.Errorf("Failed to load container mount %v: %v", id, err) continue } container.RWLayer = rwlayer logrus.Debugf("Loaded container %v", container.ID) containers[container.ID] = container } else { logrus.Debugf("Cannot load container %s because it was created with another graph driver.", container.ID) } } var migrateLegacyLinks bool restartContainers := make(map[*container.Container]chan struct{}) for _, c := range containers { if err := daemon.registerName(c); err != nil { logrus.Errorf("Failed to register container %s: %s", c.ID, err) continue } if err := daemon.Register(c); err != nil { logrus.Errorf("Failed to register container %s: %s", c.ID, err) continue } } var wg sync.WaitGroup var mapLock sync.Mutex for _, c := range containers { wg.Add(1) go func(c *container.Container) { defer wg.Done() rm := c.RestartManager(false) if c.IsRunning() || c.IsPaused() { // Fix activityCount such that graph mounts can be unmounted later if err := daemon.layerStore.ReinitRWLayer(c.RWLayer); err != nil { logrus.Errorf("Failed to ReinitRWLayer for %s due to %s", c.ID, err) return } if err := daemon.containerd.Restore(c.ID, libcontainerd.WithRestartManager(rm)); err != nil { logrus.Errorf("Failed to restore with containerd: %q", err) return } } // fixme: only if not running // get list of containers we need to restart if daemon.configStore.AutoRestart && !c.IsRunning() && !c.IsPaused() && c.ShouldRestart() { mapLock.Lock() restartContainers[c] = make(chan struct{}) mapLock.Unlock() } // if c.hostConfig.Links is nil (not just empty), then it is using the old sqlite links and needs to be migrated if c.HostConfig != nil && c.HostConfig.Links == nil { migrateLegacyLinks = true } }(c) } wg.Wait() // migrate any legacy links from sqlite linkdbFile := filepath.Join(daemon.root, "linkgraph.db") var legacyLinkDB *graphdb.Database if migrateLegacyLinks { legacyLinkDB, err = graphdb.NewSqliteConn(linkdbFile) if err != nil { return fmt.Errorf("error connecting to legacy link graph DB %s, container links may be lost: %v", linkdbFile, err) } defer legacyLinkDB.Close() } // Now that all the containers are registered, register the links for _, c := range containers { if migrateLegacyLinks { if err := daemon.migrateLegacySqliteLinks(legacyLinkDB, c); err != nil { return err } } if err := daemon.registerLinks(c, c.HostConfig); err != nil { logrus.Errorf("failed to register link for container %s: %v", c.ID, err) } } group := sync.WaitGroup{} for c, notifier := range restartContainers { group.Add(1) go func(c *container.Container, chNotify chan struct{}) { defer group.Done() logrus.Debugf("Starting container %s", c.ID) // ignore errors here as this is a best effort to wait for children to be // running before we try to start the container children := daemon.children(c) timeout := time.After(5 * time.Second) for _, child := range children { if notifier, exists := restartContainers[child]; exists { select { case <-notifier: case <-timeout: } } } if err := daemon.containerStart(c); err != nil { logrus.Errorf("Failed to start container %s: %s", c.ID, err) } close(chNotify) }(c, notifier) } group.Wait() // any containers that were started above would already have had this done, // however we need to now prepare the mountpoints for the rest of the containers as well. // This shouldn't cause any issue running on the containers that already had this run. // This must be run after any containers with a restart policy so that containerized plugins // can have a chance to be running before we try to initialize them. for _, c := range containers { // if the container has restart policy, do not // prepare the mountpoints since it has been done on restarting. // This is to speed up the daemon start when a restart container // has a volume and the volume dirver is not available. if _, ok := restartContainers[c]; ok { continue } group.Add(1) go func(c *container.Container) { defer group.Done() if err := daemon.prepareMountPoints(c); err != nil { logrus.Error(err) } }(c) } group.Wait() if !debug { if logrus.GetLevel() == logrus.InfoLevel { fmt.Println() } logrus.Info("Loading containers: done.") } return nil }
// CmdDaemon is the daemon command, called the raw arguments after `docker daemon`. func (cli *DaemonCli) CmdDaemon(args ...string) error { // warn from uuid package when running the daemon uuid.Loggerf = logrus.Warnf if !commonFlags.FlagSet.IsEmpty() || !clientFlags.FlagSet.IsEmpty() { // deny `docker -D daemon` illegalFlag := getGlobalFlag() fmt.Fprintf(os.Stderr, "invalid flag '-%s'.\nSee 'docker daemon --help'.\n", illegalFlag.Names[0]) os.Exit(1) } else { // allow new form `docker daemon -D` flag.Merge(cli.flags, commonFlags.FlagSet) } configFile := cli.flags.String([]string{daemonConfigFileFlag}, defaultDaemonConfigFile, "Daemon configuration file") cli.flags.ParseFlags(args, true) commonFlags.PostParse() if commonFlags.TrustKey == "" { commonFlags.TrustKey = filepath.Join(getDaemonConfDir(), defaultTrustKeyFile) } cliConfig, err := loadDaemonCliConfig(cli.Config, cli.flags, commonFlags, *configFile) if err != nil { fmt.Fprint(os.Stderr, err) os.Exit(1) } cli.Config = cliConfig if cli.Config.Debug { utils.EnableDebug() } if utils.ExperimentalBuild() { logrus.Warn("Running experimental build") } logrus.SetFormatter(&logrus.TextFormatter{ TimestampFormat: jsonlog.RFC3339NanoFixed, DisableColors: cli.Config.RawLogs, }) if err := setDefaultUmask(); err != nil { logrus.Fatalf("Failed to set umask: %v", err) } if len(cli.LogConfig.Config) > 0 { if err := logger.ValidateLogOpts(cli.LogConfig.Type, cli.LogConfig.Config); err != nil { logrus.Fatalf("Failed to set log opts: %v", err) } } var pfile *pidfile.PIDFile if cli.Pidfile != "" { pf, err := pidfile.New(cli.Pidfile) if err != nil { logrus.Fatalf("Error starting daemon: %v", err) } pfile = pf defer func() { if err := pfile.Remove(); err != nil { logrus.Error(err) } }() } serverConfig := &apiserver.Config{ AuthorizationPluginNames: cli.Config.AuthorizationPlugins, Logging: true, SocketGroup: cli.Config.SocketGroup, Version: dockerversion.Version, } serverConfig = setPlatformServerConfig(serverConfig, cli.Config) if cli.Config.TLS { tlsOptions := tlsconfig.Options{ CAFile: cli.Config.CommonTLSOptions.CAFile, CertFile: cli.Config.CommonTLSOptions.CertFile, KeyFile: cli.Config.CommonTLSOptions.KeyFile, } if cli.Config.TLSVerify { // server requires and verifies client's certificate tlsOptions.ClientAuth = tls.RequireAndVerifyClientCert } tlsConfig, err := tlsconfig.Server(tlsOptions) if err != nil { logrus.Fatal(err) } serverConfig.TLSConfig = tlsConfig } if len(cli.Config.Hosts) == 0 { cli.Config.Hosts = make([]string, 1) } 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.Fatalf("error parsing -H %s : %v", cli.Config.Hosts[i], err) } protoAddr := cli.Config.Hosts[i] protoAddrParts := strings.SplitN(protoAddr, "://", 2) if len(protoAddrParts) != 2 { logrus.Fatalf("bad format %s, expected PROTO://ADDR", protoAddr) } l, err := listeners.Init(protoAddrParts[0], protoAddrParts[1], serverConfig.SocketGroup, serverConfig.TLSConfig) if err != nil { logrus.Fatal(err) } logrus.Debugf("Listener created for HTTP on %s (%s)", protoAddrParts[0], protoAddrParts[1]) api.Accept(protoAddrParts[1], l...) } if err := migrateKey(); err != nil { logrus.Fatal(err) } cli.TrustKeyPath = commonFlags.TrustKey registryService := registry.NewService(cli.Config.ServiceOptions) d, err := daemon.NewDaemon(cli.Config, registryService) if err != nil { if pfile != nil { if err := pfile.Remove(); err != nil { logrus.Error(err) } } logrus.Fatalf("Error starting daemon: %v", err) } logrus.Info("Daemon has completed initialization") logrus.WithFields(logrus.Fields{ "version": dockerversion.Version, "commit": dockerversion.GitCommit, "execdriver": d.ExecutionDriver().Name(), "graphdriver": d.GraphDriverName(), }).Info("Docker daemon") initRouter(api, d) reload := func(config *daemon.Config) { if err := d.Reload(config); err != nil { logrus.Errorf("Error reconfiguring the daemon: %v", err) return } if config.IsValueSet("debug") { debugEnabled := utils.IsDebugEnabled() switch { case debugEnabled && !config.Debug: // disable debug utils.DisableDebug() api.DisableProfiler() case config.Debug && !debugEnabled: // enable debug utils.EnableDebug() api.EnableProfiler() } } } setupConfigReloadTrap(*configFile, cli.flags, reload) // 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 api.Wait(serveAPIWait) signal.Trap(func() { api.Close() <-serveAPIWait shutdownDaemon(d, 15) if pfile != nil { if err := pfile.Remove(); err != nil { logrus.Error(err) } } }) // 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 shutdownDaemon(d, 15) if errAPI != nil { if pfile != nil { if err := pfile.Remove(); err != nil { logrus.Error(err) } } logrus.Fatalf("Shutting down due to ServeAPI error: %v", errAPI) } return nil }
// SystemInfo returns information about the host server the daemon is running on. func (daemon *Daemon) SystemInfo() (*types.Info, error) { kernelVersion := "<unknown>" if kv, err := kernel.GetKernelVersion(); err != nil { logrus.Warnf("Could not get kernel version: %v", err) } else { kernelVersion = kv.String() } operatingSystem := "<unknown>" if s, err := operatingsystem.GetOperatingSystem(); err != nil { logrus.Warnf("Could not get operating system name: %v", err) } else { operatingSystem = s } // Don't do containerized check on Windows if runtime.GOOS != "windows" { if inContainer, err := operatingsystem.IsContainerized(); err != nil { logrus.Errorf("Could not determine if daemon is containerized: %v", err) operatingSystem += " (error determining if containerized)" } else if inContainer { operatingSystem += " (containerized)" } } meminfo, err := system.ReadMemInfo() if err != nil { logrus.Errorf("Could not read system memory info: %v", err) meminfo = &system.MemInfo{} } sysInfo := sysinfo.New(true) var cRunning, cPaused, cStopped int32 daemon.containers.ApplyAll(func(c *container.Container) { switch c.StateString() { case "paused": atomic.AddInt32(&cPaused, 1) case "running": atomic.AddInt32(&cRunning, 1) default: atomic.AddInt32(&cStopped, 1) } }) securityOptions := []string{} if sysInfo.AppArmor { securityOptions = append(securityOptions, "name=apparmor") } if sysInfo.Seccomp && supportsSeccomp { profile := daemon.seccompProfilePath if profile == "" { profile = "default" } securityOptions = append(securityOptions, fmt.Sprintf("name=seccomp,profile=%s", profile)) } if selinuxEnabled() { securityOptions = append(securityOptions, "name=selinux") } uid, gid := daemon.GetRemappedUIDGID() if uid != 0 || gid != 0 { securityOptions = append(securityOptions, "name=userns") } v := &types.Info{ ID: daemon.ID, Containers: int(cRunning + cPaused + cStopped), ContainersRunning: int(cRunning), ContainersPaused: int(cPaused), ContainersStopped: int(cStopped), Images: len(daemon.imageStore.Map()), Driver: daemon.GraphDriverName(), DriverStatus: daemon.layerStore.DriverStatus(), Plugins: daemon.showPluginsInfo(), IPv4Forwarding: !sysInfo.IPv4ForwardingDisabled, BridgeNfIptables: !sysInfo.BridgeNFCallIPTablesDisabled, BridgeNfIP6tables: !sysInfo.BridgeNFCallIP6TablesDisabled, Debug: utils.IsDebugEnabled(), NFd: fileutils.GetTotalUsedFds(), NGoroutines: runtime.NumGoroutine(), SystemTime: time.Now().Format(time.RFC3339Nano), LoggingDriver: daemon.defaultLogConfig.Type, CgroupDriver: daemon.getCgroupDriver(), NEventsListener: daemon.EventsService.SubscribersCount(), KernelVersion: kernelVersion, OperatingSystem: operatingSystem, IndexServerAddress: registry.IndexServer, OSType: platform.OSType, Architecture: platform.Architecture, RegistryConfig: daemon.RegistryService.ServiceConfig(), NCPU: sysinfo.NumCPU(), MemTotal: meminfo.MemTotal, DockerRootDir: daemon.configStore.Root, Labels: daemon.configStore.Labels, ExperimentalBuild: daemon.configStore.Experimental, ServerVersion: dockerversion.Version, ClusterStore: daemon.configStore.ClusterStore, ClusterAdvertise: daemon.configStore.ClusterAdvertise, HTTPProxy: sockets.GetProxyEnv("http_proxy"), HTTPSProxy: sockets.GetProxyEnv("https_proxy"), NoProxy: sockets.GetProxyEnv("no_proxy"), LiveRestoreEnabled: daemon.configStore.LiveRestoreEnabled, SecurityOptions: securityOptions, Isolation: daemon.defaultIsolation, } // Retrieve platform specific info daemon.FillPlatformInfo(v, sysInfo) hostname := "" if hn, err := os.Hostname(); err != nil { logrus.Warnf("Could not get hostname: %v", err) } else { hostname = hn } v.Name = hostname return v, nil }
// CmdInfo displays system-wide information. // // Usage: docker info func (cli *DockerCli) CmdInfo(args ...string) error { cmd := Cli.Subcmd("info", nil, Cli.DockerCommands["info"].Description, true) cmd.Require(flag.Exact, 0) cmd.ParseFlags(args, true) info, err := cli.client.Info(context.Background()) if err != nil { return err } fmt.Fprintf(cli.out, "Containers: %d\n", info.Containers) fmt.Fprintf(cli.out, " Running: %d\n", info.ContainersRunning) fmt.Fprintf(cli.out, " Paused: %d\n", info.ContainersPaused) fmt.Fprintf(cli.out, " Stopped: %d\n", info.ContainersStopped) fmt.Fprintf(cli.out, "Images: %d\n", info.Images) ioutils.FprintfIfNotEmpty(cli.out, "Server Version: %s\n", info.ServerVersion) ioutils.FprintfIfNotEmpty(cli.out, "Storage Driver: %s\n", info.Driver) if info.DriverStatus != nil { for _, pair := range info.DriverStatus { fmt.Fprintf(cli.out, " %s: %s\n", pair[0], pair[1]) // print a warning if devicemapper is using a loopback file if pair[0] == "Data loop file" { fmt.Fprintln(cli.err, " WARNING: Usage of loopback devices is strongly discouraged for production use. Either use `--storage-opt dm.thinpooldev` or use `--storage-opt dm.no_warn_on_loop_devices=true` to suppress this warning.") } } } if info.SystemStatus != nil { for _, pair := range info.SystemStatus { fmt.Fprintf(cli.out, "%s: %s\n", pair[0], pair[1]) } } ioutils.FprintfIfNotEmpty(cli.out, "Execution Driver: %s\n", info.ExecutionDriver) ioutils.FprintfIfNotEmpty(cli.out, "Logging Driver: %s\n", info.LoggingDriver) ioutils.FprintfIfNotEmpty(cli.out, "Cgroup Driver: %s\n", info.CgroupDriver) fmt.Fprintf(cli.out, "Plugins: \n") fmt.Fprintf(cli.out, " Volume:") fmt.Fprintf(cli.out, " %s", strings.Join(info.Plugins.Volume, " ")) fmt.Fprintf(cli.out, "\n") fmt.Fprintf(cli.out, " Network:") fmt.Fprintf(cli.out, " %s", strings.Join(info.Plugins.Network, " ")) fmt.Fprintf(cli.out, "\n") if len(info.Plugins.Authorization) != 0 { fmt.Fprintf(cli.out, " Authorization:") fmt.Fprintf(cli.out, " %s", strings.Join(info.Plugins.Authorization, " ")) fmt.Fprintf(cli.out, "\n") } ioutils.FprintfIfNotEmpty(cli.out, "Kernel Version: %s\n", info.KernelVersion) ioutils.FprintfIfNotEmpty(cli.out, "Operating System: %s\n", info.OperatingSystem) ioutils.FprintfIfNotEmpty(cli.out, "OSType: %s\n", info.OSType) ioutils.FprintfIfNotEmpty(cli.out, "Architecture: %s\n", info.Architecture) fmt.Fprintf(cli.out, "CPUs: %d\n", info.NCPU) fmt.Fprintf(cli.out, "Total Memory: %s\n", units.BytesSize(float64(info.MemTotal))) ioutils.FprintfIfNotEmpty(cli.out, "Name: %s\n", info.Name) ioutils.FprintfIfNotEmpty(cli.out, "ID: %s\n", info.ID) fmt.Fprintf(cli.out, "Docker Root Dir: %s\n", info.DockerRootDir) fmt.Fprintf(cli.out, "Debug mode (client): %v\n", utils.IsDebugEnabled()) fmt.Fprintf(cli.out, "Debug mode (server): %v\n", info.Debug) if info.Debug { fmt.Fprintf(cli.out, " File Descriptors: %d\n", info.NFd) fmt.Fprintf(cli.out, " Goroutines: %d\n", info.NGoroutines) fmt.Fprintf(cli.out, " System Time: %s\n", info.SystemTime) fmt.Fprintf(cli.out, " EventsListeners: %d\n", info.NEventsListener) } ioutils.FprintfIfNotEmpty(cli.out, "Http Proxy: %s\n", info.HTTPProxy) ioutils.FprintfIfNotEmpty(cli.out, "Https Proxy: %s\n", info.HTTPSProxy) ioutils.FprintfIfNotEmpty(cli.out, "No Proxy: %s\n", info.NoProxy) if info.IndexServerAddress != "" { u := cli.configFile.AuthConfigs[info.IndexServerAddress].Username if len(u) > 0 { fmt.Fprintf(cli.out, "Username: %v\n", u) } fmt.Fprintf(cli.out, "Registry: %v\n", info.IndexServerAddress) } // Only output these warnings if the server does not support these features if info.OSType != "windows" { if !info.MemoryLimit { fmt.Fprintln(cli.err, "WARNING: No memory limit support") } if !info.SwapLimit { fmt.Fprintln(cli.err, "WARNING: No swap limit support") } if !info.KernelMemory { fmt.Fprintln(cli.err, "WARNING: No kernel memory limit support") } if !info.OomKillDisable { fmt.Fprintln(cli.err, "WARNING: No oom kill disable support") } if !info.CPUCfsQuota { fmt.Fprintln(cli.err, "WARNING: No cpu cfs quota support") } if !info.CPUCfsPeriod { fmt.Fprintln(cli.err, "WARNING: No cpu cfs period support") } if !info.CPUShares { fmt.Fprintln(cli.err, "WARNING: No cpu shares support") } if !info.CPUSet { fmt.Fprintln(cli.err, "WARNING: No cpuset support") } if !info.IPv4Forwarding { fmt.Fprintln(cli.err, "WARNING: IPv4 forwarding is disabled") } if !info.BridgeNfIptables { fmt.Fprintln(cli.err, "WARNING: bridge-nf-call-iptables is disabled") } if !info.BridgeNfIP6tables { fmt.Fprintln(cli.err, "WARNING: bridge-nf-call-ip6tables is disabled") } } if info.Labels != nil { fmt.Fprintln(cli.out, "Labels:") for _, attribute := range info.Labels { fmt.Fprintf(cli.out, " %s\n", attribute) } } ioutils.FprintfIfTrue(cli.out, "Experimental: %v\n", info.ExperimentalBuild) if info.ClusterStore != "" { fmt.Fprintf(cli.out, "Cluster store: %s\n", info.ClusterStore) } if info.ClusterAdvertise != "" { fmt.Fprintf(cli.out, "Cluster advertise: %s\n", info.ClusterAdvertise) } return nil }
func prettyPrintInfo(dockerCli *client.DockerCli, info types.Info) error { fmt.Fprintf(dockerCli.Out(), "Containers: %d\n", info.Containers) fmt.Fprintf(dockerCli.Out(), " Running: %d\n", info.ContainersRunning) fmt.Fprintf(dockerCli.Out(), " Paused: %d\n", info.ContainersPaused) fmt.Fprintf(dockerCli.Out(), " Stopped: %d\n", info.ContainersStopped) fmt.Fprintf(dockerCli.Out(), "Images: %d\n", info.Images) ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Server Version: %s\n", info.ServerVersion) ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Storage Driver: %s\n", info.Driver) if info.DriverStatus != nil { for _, pair := range info.DriverStatus { fmt.Fprintf(dockerCli.Out(), " %s: %s\n", pair[0], pair[1]) // print a warning if devicemapper is using a loopback file if pair[0] == "Data loop file" { fmt.Fprintln(dockerCli.Err(), " WARNING: Usage of loopback devices is strongly discouraged for production use. Use `--storage-opt dm.thinpooldev` to specify a custom block storage device.") } } } if info.SystemStatus != nil { for _, pair := range info.SystemStatus { fmt.Fprintf(dockerCli.Out(), "%s: %s\n", pair[0], pair[1]) } } ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Logging Driver: %s\n", info.LoggingDriver) ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Cgroup Driver: %s\n", info.CgroupDriver) fmt.Fprintf(dockerCli.Out(), "Plugins: \n") fmt.Fprintf(dockerCli.Out(), " Volume:") fmt.Fprintf(dockerCli.Out(), " %s", strings.Join(info.Plugins.Volume, " ")) fmt.Fprintf(dockerCli.Out(), "\n") fmt.Fprintf(dockerCli.Out(), " Network:") fmt.Fprintf(dockerCli.Out(), " %s", strings.Join(info.Plugins.Network, " ")) fmt.Fprintf(dockerCli.Out(), "\n") if len(info.Plugins.Authorization) != 0 { fmt.Fprintf(dockerCli.Out(), " Authorization:") fmt.Fprintf(dockerCli.Out(), " %s", strings.Join(info.Plugins.Authorization, " ")) fmt.Fprintf(dockerCli.Out(), "\n") } fmt.Fprintf(dockerCli.Out(), "Swarm: %v\n", info.Swarm.LocalNodeState) if info.Swarm.LocalNodeState != swarm.LocalNodeStateInactive { fmt.Fprintf(dockerCli.Out(), " NodeID: %s\n", info.Swarm.NodeID) if info.Swarm.Error != "" { fmt.Fprintf(dockerCli.Out(), " Error: %v\n", info.Swarm.Error) } fmt.Fprintf(dockerCli.Out(), " Is Manager: %v\n", info.Swarm.ControlAvailable) if info.Swarm.ControlAvailable { fmt.Fprintf(dockerCli.Out(), " ClusterID: %s\n", info.Swarm.Cluster.ID) fmt.Fprintf(dockerCli.Out(), " Managers: %d\n", info.Swarm.Managers) fmt.Fprintf(dockerCli.Out(), " Nodes: %d\n", info.Swarm.Nodes) fmt.Fprintf(dockerCli.Out(), " Orchestration:\n") fmt.Fprintf(dockerCli.Out(), " Task History Retention Limit: %d\n", info.Swarm.Cluster.Spec.Orchestration.TaskHistoryRetentionLimit) fmt.Fprintf(dockerCli.Out(), " Raft:\n") fmt.Fprintf(dockerCli.Out(), " Snapshot Interval: %d\n", info.Swarm.Cluster.Spec.Raft.SnapshotInterval) fmt.Fprintf(dockerCli.Out(), " Heartbeat Tick: %d\n", info.Swarm.Cluster.Spec.Raft.HeartbeatTick) fmt.Fprintf(dockerCli.Out(), " Election Tick: %d\n", info.Swarm.Cluster.Spec.Raft.ElectionTick) fmt.Fprintf(dockerCli.Out(), " Dispatcher:\n") fmt.Fprintf(dockerCli.Out(), " Heartbeat Period: %s\n", units.HumanDuration(time.Duration(info.Swarm.Cluster.Spec.Dispatcher.HeartbeatPeriod))) fmt.Fprintf(dockerCli.Out(), " CA Configuration:\n") fmt.Fprintf(dockerCli.Out(), " Expiry Duration: %s\n", units.HumanDuration(info.Swarm.Cluster.Spec.CAConfig.NodeCertExpiry)) if len(info.Swarm.Cluster.Spec.CAConfig.ExternalCAs) > 0 { fmt.Fprintf(dockerCli.Out(), " External CAs:\n") for _, entry := range info.Swarm.Cluster.Spec.CAConfig.ExternalCAs { fmt.Fprintf(dockerCli.Out(), " %s: %s\n", entry.Protocol, entry.URL) } } } fmt.Fprintf(dockerCli.Out(), " Node Address: %s\n", info.Swarm.NodeAddr) } if len(info.Runtimes) > 0 { fmt.Fprintf(dockerCli.Out(), "Runtimes:") for name := range info.Runtimes { fmt.Fprintf(dockerCli.Out(), " %s", name) } fmt.Fprint(dockerCli.Out(), "\n") fmt.Fprintf(dockerCli.Out(), "Default Runtime: %s\n", info.DefaultRuntime) } fmt.Fprintf(dockerCli.Out(), "Security Options:") ioutils.FprintfIfNotEmpty(dockerCli.Out(), " %s", strings.Join(info.SecurityOptions, " ")) fmt.Fprintf(dockerCli.Out(), "\n") ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Kernel Version: %s\n", info.KernelVersion) ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Operating System: %s\n", info.OperatingSystem) ioutils.FprintfIfNotEmpty(dockerCli.Out(), "OSType: %s\n", info.OSType) ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Architecture: %s\n", info.Architecture) fmt.Fprintf(dockerCli.Out(), "CPUs: %d\n", info.NCPU) fmt.Fprintf(dockerCli.Out(), "Total Memory: %s\n", units.BytesSize(float64(info.MemTotal))) ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Name: %s\n", info.Name) ioutils.FprintfIfNotEmpty(dockerCli.Out(), "ID: %s\n", info.ID) fmt.Fprintf(dockerCli.Out(), "Docker Root Dir: %s\n", info.DockerRootDir) fmt.Fprintf(dockerCli.Out(), "Debug Mode (client): %v\n", utils.IsDebugEnabled()) fmt.Fprintf(dockerCli.Out(), "Debug Mode (server): %v\n", info.Debug) if info.Debug { fmt.Fprintf(dockerCli.Out(), " File Descriptors: %d\n", info.NFd) fmt.Fprintf(dockerCli.Out(), " Goroutines: %d\n", info.NGoroutines) fmt.Fprintf(dockerCli.Out(), " System Time: %s\n", info.SystemTime) fmt.Fprintf(dockerCli.Out(), " EventsListeners: %d\n", info.NEventsListener) } ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Http Proxy: %s\n", info.HTTPProxy) ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Https Proxy: %s\n", info.HTTPSProxy) ioutils.FprintfIfNotEmpty(dockerCli.Out(), "No Proxy: %s\n", info.NoProxy) if info.IndexServerAddress != "" { u := dockerCli.ConfigFile().AuthConfigs[info.IndexServerAddress].Username if len(u) > 0 { fmt.Fprintf(dockerCli.Out(), "Username: %v\n", u) } fmt.Fprintf(dockerCli.Out(), "Registry: %v\n", info.IndexServerAddress) } // Only output these warnings if the server does not support these features if info.OSType != "windows" { if !info.MemoryLimit { fmt.Fprintln(dockerCli.Err(), "WARNING: No memory limit support") } if !info.SwapLimit { fmt.Fprintln(dockerCli.Err(), "WARNING: No swap limit support") } if !info.KernelMemory { fmt.Fprintln(dockerCli.Err(), "WARNING: No kernel memory limit support") } if !info.OomKillDisable { fmt.Fprintln(dockerCli.Err(), "WARNING: No oom kill disable support") } if !info.CPUCfsQuota { fmt.Fprintln(dockerCli.Err(), "WARNING: No cpu cfs quota support") } if !info.CPUCfsPeriod { fmt.Fprintln(dockerCli.Err(), "WARNING: No cpu cfs period support") } if !info.CPUShares { fmt.Fprintln(dockerCli.Err(), "WARNING: No cpu shares support") } if !info.CPUSet { fmt.Fprintln(dockerCli.Err(), "WARNING: No cpuset support") } if !info.IPv4Forwarding { fmt.Fprintln(dockerCli.Err(), "WARNING: IPv4 forwarding is disabled") } if !info.BridgeNfIptables { fmt.Fprintln(dockerCli.Err(), "WARNING: bridge-nf-call-iptables is disabled") } if !info.BridgeNfIP6tables { fmt.Fprintln(dockerCli.Err(), "WARNING: bridge-nf-call-ip6tables is disabled") } } if info.Labels != nil { fmt.Fprintln(dockerCli.Out(), "Labels:") for _, attribute := range info.Labels { fmt.Fprintf(dockerCli.Out(), " %s\n", attribute) } } ioutils.FprintfIfTrue(dockerCli.Out(), "Experimental: %v\n", info.ExperimentalBuild) if info.ClusterStore != "" { fmt.Fprintf(dockerCli.Out(), "Cluster Store: %s\n", info.ClusterStore) } if info.ClusterAdvertise != "" { fmt.Fprintf(dockerCli.Out(), "Cluster Advertise: %s\n", info.ClusterAdvertise) } if info.RegistryConfig != nil && (len(info.RegistryConfig.InsecureRegistryCIDRs) > 0 || len(info.RegistryConfig.IndexConfigs) > 0) { fmt.Fprintln(dockerCli.Out(), "Insecure Registries:") for _, registry := range info.RegistryConfig.IndexConfigs { if registry.Secure == false { fmt.Fprintf(dockerCli.Out(), " %s\n", registry.Name) } } for _, registry := range info.RegistryConfig.InsecureRegistryCIDRs { mask, _ := registry.Mask.Size() fmt.Fprintf(dockerCli.Out(), " %s/%d\n", registry.IP.String(), mask) } } if info.RegistryConfig != nil && len(info.RegistryConfig.Mirrors) > 0 { fmt.Fprintln(dockerCli.Out(), "Registry Mirrors:") for _, mirror := range info.RegistryConfig.Mirrors { fmt.Fprintf(dockerCli.Out(), " %s\n", mirror) } } fmt.Fprintf(dockerCli.Out(), "Live Restore Enabled: %v\n", info.LiveRestoreEnabled) return nil }
func (daemon *Daemon) restore() error { var ( debug = utils.IsDebugEnabled() currentDriver = daemon.GraphDriverName() containers = make(map[string]*container.Container) ) if !debug { logrus.Info("Loading containers: start.") } dir, err := ioutil.ReadDir(daemon.repository) if err != nil { return err } for _, v := range dir { id := v.Name() container, err := daemon.load(id) if !debug && logrus.GetLevel() == logrus.InfoLevel { fmt.Print(".") } if err != nil { logrus.Errorf("Failed to load container %v: %v", id, err) continue } rwlayer, err := daemon.layerStore.GetRWLayer(container.ID) if err != nil { logrus.Errorf("Failed to load container mount %v: %v", id, err) continue } container.RWLayer = rwlayer // Ignore the container if it does not support the current driver being used by the graph if (container.Driver == "" && currentDriver == "aufs") || container.Driver == currentDriver { logrus.Debugf("Loaded container %v", container.ID) containers[container.ID] = container } else { logrus.Debugf("Cannot load container %s because it was created with another graph driver.", container.ID) } } var migrateLegacyLinks bool restartContainers := make(map[*container.Container]chan struct{}) for _, c := range containers { if err := daemon.registerName(c); err != nil { logrus.Errorf("Failed to register container %s: %s", c.ID, err) continue } if err := daemon.Register(c); err != nil { logrus.Errorf("Failed to register container %s: %s", c.ID, err) continue } // get list of containers we need to restart if daemon.configStore.AutoRestart && c.ShouldRestart() { restartContainers[c] = make(chan struct{}) } // if c.hostConfig.Links is nil (not just empty), then it is using the old sqlite links and needs to be migrated if c.HostConfig != nil && c.HostConfig.Links == nil { migrateLegacyLinks = true } } // migrate any legacy links from sqlite linkdbFile := filepath.Join(daemon.root, "linkgraph.db") var legacyLinkDB *graphdb.Database if migrateLegacyLinks { legacyLinkDB, err = graphdb.NewSqliteConn(linkdbFile) if err != nil { return fmt.Errorf("error connecting to legacy link graph DB %s, container links may be lost: %v", linkdbFile, err) } defer legacyLinkDB.Close() } // Now that all the containers are registered, register the links for _, c := range containers { if migrateLegacyLinks { if err := daemon.migrateLegacySqliteLinks(legacyLinkDB, c); err != nil { return err } } if err := daemon.registerLinks(c, c.HostConfig); err != nil { logrus.Errorf("failed to register link for container %s: %v", c.ID, err) } } group := sync.WaitGroup{} for c, notifier := range restartContainers { group.Add(1) go func(c *container.Container, chNotify chan struct{}) { defer group.Done() logrus.Debugf("Starting container %s", c.ID) // ignore errors here as this is a best effort to wait for children to be // running before we try to start the container children := daemon.children(c) timeout := time.After(5 * time.Second) for _, child := range children { if notifier, exists := restartContainers[child]; exists { select { case <-notifier: case <-timeout: } } } if err := daemon.containerStart(c); err != nil { logrus.Errorf("Failed to start container %s: %s", c.ID, err) } close(chNotify) }(c, notifier) } group.Wait() if !debug { if logrus.GetLevel() == logrus.InfoLevel { fmt.Println() } logrus.Info("Loading containers: done.") } return nil }
// SystemInfo returns information about the host server the daemon is running on. func (daemon *Daemon) SystemInfo() (*types.Info, error) { kernelVersion := "<unknown>" if kv, err := kernel.GetKernelVersion(); err != nil { logrus.Warnf("Could not get kernel version: %v", err) } else { kernelVersion = kv.String() } operatingSystem := "<unknown>" if s, err := operatingsystem.GetOperatingSystem(); err != nil { logrus.Warnf("Could not get operating system name: %v", err) } else { operatingSystem = s } // Don't do containerized check on Windows if runtime.GOOS != "windows" { if inContainer, err := operatingsystem.IsContainerized(); err != nil { logrus.Errorf("Could not determine if daemon is containerized: %v", err) operatingSystem += " (error determining if containerized)" } else if inContainer { operatingSystem += " (containerized)" } } meminfo, err := system.ReadMemInfo() if err != nil { logrus.Errorf("Could not read system memory info: %v", err) meminfo = &system.MemInfo{} } sysInfo := sysinfo.New(true) var cRunning, cPaused, cStopped int32 daemon.containers.ApplyAll(func(c *container.Container) { switch c.StateString() { case "paused": atomic.AddInt32(&cPaused, 1) case "running": atomic.AddInt32(&cRunning, 1) default: atomic.AddInt32(&cStopped, 1) } }) securityOptions := []types.SecurityOpt{} if sysInfo.AppArmor { securityOptions = append(securityOptions, types.SecurityOpt{Key: "Name", Value: "apparmor"}) } if sysInfo.Seccomp && supportsSeccomp { profile := daemon.seccompProfilePath if profile == "" { profile = "default" } securityOptions = append(securityOptions, types.SecurityOpt{Key: "Name", Value: "seccomp"}, types.SecurityOpt{Key: "Profile", Value: profile}, ) } if selinuxEnabled() { securityOptions = append(securityOptions, types.SecurityOpt{Key: "Name", Value: "selinux"}) } uid, gid := daemon.GetRemappedUIDGID() if uid != 0 || gid != 0 { securityOptions = append(securityOptions, types.SecurityOpt{Key: "Name", Value: "userns"}) } v := &types.InfoBase{ ID: daemon.ID, Containers: int(cRunning + cPaused + cStopped), ContainersRunning: int(cRunning), ContainersPaused: int(cPaused), ContainersStopped: int(cStopped), Images: len(daemon.imageStore.Map()), Driver: daemon.GraphDriverName(), DriverStatus: daemon.layerStore.DriverStatus(), Plugins: daemon.showPluginsInfo(), IPv4Forwarding: !sysInfo.IPv4ForwardingDisabled, BridgeNfIptables: !sysInfo.BridgeNFCallIPTablesDisabled, BridgeNfIP6tables: !sysInfo.BridgeNFCallIP6TablesDisabled, Debug: utils.IsDebugEnabled(), NFd: fileutils.GetTotalUsedFds(), NGoroutines: runtime.NumGoroutine(), SystemTime: time.Now().Format(time.RFC3339Nano), LoggingDriver: daemon.defaultLogConfig.Type, CgroupDriver: daemon.getCgroupDriver(), NEventsListener: daemon.EventsService.SubscribersCount(), KernelVersion: kernelVersion, OperatingSystem: operatingSystem, IndexServerAddress: registry.IndexServer, OSType: platform.OSType, Architecture: platform.Architecture, RegistryConfig: daemon.RegistryService.ServiceConfig(), NCPU: sysinfo.NumCPU(), MemTotal: meminfo.MemTotal, DockerRootDir: daemon.configStore.Root, Labels: daemon.configStore.Labels, ExperimentalBuild: daemon.configStore.Experimental, ServerVersion: dockerversion.Version, ClusterStore: daemon.configStore.ClusterStore, ClusterAdvertise: daemon.configStore.ClusterAdvertise, HTTPProxy: sockets.GetProxyEnv("http_proxy"), HTTPSProxy: sockets.GetProxyEnv("https_proxy"), NoProxy: sockets.GetProxyEnv("no_proxy"), LiveRestoreEnabled: daemon.configStore.LiveRestoreEnabled, Isolation: daemon.defaultIsolation, } // TODO Windows. Refactor this more once sysinfo is refactored into // platform specific code. On Windows, sysinfo.cgroupMemInfo and // sysinfo.cgroupCpuInfo will be nil otherwise and cause a SIGSEGV if // an attempt is made to access through them. if runtime.GOOS != "windows" { v.MemoryLimit = sysInfo.MemoryLimit v.SwapLimit = sysInfo.SwapLimit v.KernelMemory = sysInfo.KernelMemory v.OomKillDisable = sysInfo.OomKillDisable v.CPUCfsPeriod = sysInfo.CPUCfsPeriod v.CPUCfsQuota = sysInfo.CPUCfsQuota v.CPUShares = sysInfo.CPUShares v.CPUSet = sysInfo.Cpuset v.Runtimes = daemon.configStore.GetAllRuntimes() v.DefaultRuntime = daemon.configStore.GetDefaultRuntimeName() v.InitBinary = daemon.configStore.GetInitPath() v.ContainerdCommit.Expected = dockerversion.ContainerdCommitID if sv, err := daemon.containerd.GetServerVersion(context.Background()); err == nil { v.ContainerdCommit.ID = sv.Revision } else { logrus.Warnf("failed to retrieve containerd version: %v", err) v.ContainerdCommit.ID = "N/A" } v.RuncCommit.Expected = dockerversion.RuncCommitID if rv, err := exec.Command(DefaultRuntimeBinary, "--version").Output(); err == nil { parts := strings.Split(strings.TrimSpace(string(rv)), "\n") if len(parts) == 3 { parts = strings.Split(parts[1], ": ") if len(parts) == 2 { v.RuncCommit.ID = strings.TrimSpace(parts[1]) } } } else { logrus.Warnf("failed to retrieve %s version: %v", DefaultRuntimeBinary, err) v.RuncCommit.ID = "N/A" } if v.RuncCommit.ID == "" { logrus.Warnf("failed to retrieve %s version: unknown output format", DefaultRuntimeBinary) v.RuncCommit.ID = "N/A" } v.InitCommit.Expected = dockerversion.InitCommitID if rv, err := exec.Command(DefaultInitBinary, "--version").Output(); err == nil { parts := strings.Split(string(rv), " ") if len(parts) == 3 { v.InitCommit.ID = strings.TrimSpace(parts[2]) } else { logrus.Warnf("failed to retrieve %s version: unknown output format", DefaultInitBinary) v.InitCommit.ID = "N/A" } } else { logrus.Warnf("failed to retrieve %s version", DefaultInitBinary) v.InitCommit.ID = "N/A" } } hostname := "" if hn, err := os.Hostname(); err != nil { logrus.Warnf("Could not get hostname: %v", err) } else { hostname = hn } v.Name = hostname i := &types.Info{ InfoBase: v, SecurityOptions: securityOptions, } return i, nil }
// SystemInfo returns information about the host server the daemon is running on. func (daemon *Daemon) SystemInfo() (*types.Info, error) { kernelVersion := "<unknown>" if kv, err := kernel.GetKernelVersion(); err != nil { logrus.Warnf("Could not get kernel version: %v", err) } else { kernelVersion = kv.String() } operatingSystem := "<unknown>" if s, err := operatingsystem.GetOperatingSystem(); err != nil { logrus.Warnf("Could not get operating system name: %v", err) } else { operatingSystem = s } // Don't do containerized check on Windows if runtime.GOOS != "windows" { if inContainer, err := operatingsystem.IsContainerized(); err != nil { logrus.Errorf("Could not determine if daemon is containerized: %v", err) operatingSystem += " (error determining if containerized)" } else if inContainer { operatingSystem += " (containerized)" } } meminfo, err := system.ReadMemInfo() if err != nil { logrus.Errorf("Could not read system memory info: %v", err) } sysInfo := sysinfo.New(true) var cRunning, cPaused, cStopped int32 daemon.containers.ApplyAll(func(c *container.Container) { switch c.StateString() { case "paused": atomic.AddInt32(&cPaused, 1) case "running": atomic.AddInt32(&cRunning, 1) default: atomic.AddInt32(&cStopped, 1) } }) var securityOptions []string if sysInfo.AppArmor { securityOptions = append(securityOptions, "apparmor") } if sysInfo.Seccomp { securityOptions = append(securityOptions, "seccomp") } if selinuxEnabled() { securityOptions = append(securityOptions, "selinux") } v := &types.Info{ ID: daemon.ID, Containers: int(cRunning + cPaused + cStopped), ContainersRunning: int(cRunning), ContainersPaused: int(cPaused), ContainersStopped: int(cStopped), Images: len(daemon.imageStore.Map()), Driver: daemon.GraphDriverName(), DriverStatus: daemon.layerStore.DriverStatus(), Plugins: daemon.showPluginsInfo(), IPv4Forwarding: !sysInfo.IPv4ForwardingDisabled, BridgeNfIptables: !sysInfo.BridgeNFCallIPTablesDisabled, BridgeNfIP6tables: !sysInfo.BridgeNFCallIP6TablesDisabled, Debug: utils.IsDebugEnabled(), NFd: fileutils.GetTotalUsedFds(), NGoroutines: runtime.NumGoroutine(), SystemTime: time.Now().Format(time.RFC3339Nano), LoggingDriver: daemon.defaultLogConfig.Type, CgroupDriver: daemon.getCgroupDriver(), NEventsListener: daemon.EventsService.SubscribersCount(), KernelVersion: kernelVersion, OperatingSystem: operatingSystem, IndexServerAddress: registry.IndexServer, OSType: platform.OSType, Architecture: platform.Architecture, RegistryConfig: daemon.RegistryService.ServiceConfig(), NCPU: sysinfo.NumCPU(), MemTotal: meminfo.MemTotal, DockerRootDir: daemon.configStore.Root, Labels: daemon.configStore.Labels, ExperimentalBuild: utils.ExperimentalBuild(), ServerVersion: dockerversion.Version, ClusterStore: daemon.configStore.ClusterStore, ClusterAdvertise: daemon.configStore.ClusterAdvertise, HTTPProxy: sockets.GetProxyEnv("http_proxy"), HTTPSProxy: sockets.GetProxyEnv("https_proxy"), NoProxy: sockets.GetProxyEnv("no_proxy"), SecurityOptions: securityOptions, } // TODO Windows. Refactor this more once sysinfo is refactored into // platform specific code. On Windows, sysinfo.cgroupMemInfo and // sysinfo.cgroupCpuInfo will be nil otherwise and cause a SIGSEGV if // an attempt is made to access through them. if runtime.GOOS != "windows" { v.MemoryLimit = sysInfo.MemoryLimit v.SwapLimit = sysInfo.SwapLimit v.KernelMemory = sysInfo.KernelMemory v.OomKillDisable = sysInfo.OomKillDisable v.CPUCfsPeriod = sysInfo.CPUCfsPeriod v.CPUCfsQuota = sysInfo.CPUCfsQuota v.CPUShares = sysInfo.CPUShares v.CPUSet = sysInfo.Cpuset v.Runtimes = daemon.configStore.GetAllRuntimes() v.DefaultRuntime = daemon.configStore.GetDefaultRuntimeName() } hostname := "" if hn, err := os.Hostname(); err != nil { logrus.Warnf("Could not get hostname: %v", err) } else { hostname = hn } v.Name = hostname return v, nil }
func prettyPrintInfo(dockerCli *command.DockerCli, info types.Info) error { fmt.Fprintf(dockerCli.Out(), "Containers: %d\n", info.Containers) fmt.Fprintf(dockerCli.Out(), " Running: %d\n", info.ContainersRunning) fmt.Fprintf(dockerCli.Out(), " Paused: %d\n", info.ContainersPaused) fmt.Fprintf(dockerCli.Out(), " Stopped: %d\n", info.ContainersStopped) fmt.Fprintf(dockerCli.Out(), "Images: %d\n", info.Images) ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Server Version: %s\n", info.ServerVersion) ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Storage Driver: %s\n", info.Driver) if info.DriverStatus != nil { for _, pair := range info.DriverStatus { fmt.Fprintf(dockerCli.Out(), " %s: %s\n", pair[0], pair[1]) // print a warning if devicemapper is using a loopback file if pair[0] == "Data loop file" { fmt.Fprintln(dockerCli.Err(), " WARNING: Usage of loopback devices is strongly discouraged for production use. Use `--storage-opt dm.thinpooldev` to specify a custom block storage device.") } } } if info.SystemStatus != nil { for _, pair := range info.SystemStatus { fmt.Fprintf(dockerCli.Out(), "%s: %s\n", pair[0], pair[1]) } } ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Logging Driver: %s\n", info.LoggingDriver) ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Cgroup Driver: %s\n", info.CgroupDriver) fmt.Fprintf(dockerCli.Out(), "Plugins: \n") fmt.Fprintf(dockerCli.Out(), " Volume:") fmt.Fprintf(dockerCli.Out(), " %s", strings.Join(info.Plugins.Volume, " ")) fmt.Fprintf(dockerCli.Out(), "\n") fmt.Fprintf(dockerCli.Out(), " Network:") fmt.Fprintf(dockerCli.Out(), " %s", strings.Join(info.Plugins.Network, " ")) fmt.Fprintf(dockerCli.Out(), "\n") if len(info.Plugins.Authorization) != 0 { fmt.Fprintf(dockerCli.Out(), " Authorization:") fmt.Fprintf(dockerCli.Out(), " %s", strings.Join(info.Plugins.Authorization, " ")) fmt.Fprintf(dockerCli.Out(), "\n") } fmt.Fprintf(dockerCli.Out(), "Swarm: %v\n", info.Swarm.LocalNodeState) if info.Swarm.LocalNodeState != swarm.LocalNodeStateInactive && info.Swarm.LocalNodeState != swarm.LocalNodeStateLocked { fmt.Fprintf(dockerCli.Out(), " NodeID: %s\n", info.Swarm.NodeID) if info.Swarm.Error != "" { fmt.Fprintf(dockerCli.Out(), " Error: %v\n", info.Swarm.Error) } fmt.Fprintf(dockerCli.Out(), " Is Manager: %v\n", info.Swarm.ControlAvailable) if info.Swarm.ControlAvailable { fmt.Fprintf(dockerCli.Out(), " ClusterID: %s\n", info.Swarm.Cluster.ID) fmt.Fprintf(dockerCli.Out(), " Managers: %d\n", info.Swarm.Managers) fmt.Fprintf(dockerCli.Out(), " Nodes: %d\n", info.Swarm.Nodes) fmt.Fprintf(dockerCli.Out(), " Orchestration:\n") taskHistoryRetentionLimit := int64(0) if info.Swarm.Cluster.Spec.Orchestration.TaskHistoryRetentionLimit != nil { taskHistoryRetentionLimit = *info.Swarm.Cluster.Spec.Orchestration.TaskHistoryRetentionLimit } fmt.Fprintf(dockerCli.Out(), " Task History Retention Limit: %d\n", taskHistoryRetentionLimit) fmt.Fprintf(dockerCli.Out(), " Raft:\n") fmt.Fprintf(dockerCli.Out(), " Snapshot Interval: %d\n", info.Swarm.Cluster.Spec.Raft.SnapshotInterval) if info.Swarm.Cluster.Spec.Raft.KeepOldSnapshots != nil { fmt.Fprintf(dockerCli.Out(), " Number of Old Snapshots to Retain: %d\n", *info.Swarm.Cluster.Spec.Raft.KeepOldSnapshots) } fmt.Fprintf(dockerCli.Out(), " Heartbeat Tick: %d\n", info.Swarm.Cluster.Spec.Raft.HeartbeatTick) fmt.Fprintf(dockerCli.Out(), " Election Tick: %d\n", info.Swarm.Cluster.Spec.Raft.ElectionTick) fmt.Fprintf(dockerCli.Out(), " Dispatcher:\n") fmt.Fprintf(dockerCli.Out(), " Heartbeat Period: %s\n", units.HumanDuration(time.Duration(info.Swarm.Cluster.Spec.Dispatcher.HeartbeatPeriod))) fmt.Fprintf(dockerCli.Out(), " CA Configuration:\n") fmt.Fprintf(dockerCli.Out(), " Expiry Duration: %s\n", units.HumanDuration(info.Swarm.Cluster.Spec.CAConfig.NodeCertExpiry)) if len(info.Swarm.Cluster.Spec.CAConfig.ExternalCAs) > 0 { fmt.Fprintf(dockerCli.Out(), " External CAs:\n") for _, entry := range info.Swarm.Cluster.Spec.CAConfig.ExternalCAs { fmt.Fprintf(dockerCli.Out(), " %s: %s\n", entry.Protocol, entry.URL) } } } fmt.Fprintf(dockerCli.Out(), " Node Address: %s\n", info.Swarm.NodeAddr) managers := []string{} for _, entry := range info.Swarm.RemoteManagers { managers = append(managers, entry.Addr) } if len(managers) > 0 { sort.Strings(managers) fmt.Fprintf(dockerCli.Out(), " Manager Addresses:\n") for _, entry := range managers { fmt.Fprintf(dockerCli.Out(), " %s\n", entry) } } } if len(info.Runtimes) > 0 { fmt.Fprintf(dockerCli.Out(), "Runtimes:") for name := range info.Runtimes { fmt.Fprintf(dockerCli.Out(), " %s", name) } fmt.Fprint(dockerCli.Out(), "\n") fmt.Fprintf(dockerCli.Out(), "Default Runtime: %s\n", info.DefaultRuntime) } if info.OSType == "linux" { fmt.Fprintf(dockerCli.Out(), "Init Binary: %v\n", info.InitBinary) for _, ci := range []struct { Name string Commit types.Commit }{ {"containerd", info.ContainerdCommit}, {"runc", info.RuncCommit}, {"init", info.InitCommit}, } { fmt.Fprintf(dockerCli.Out(), "%s version: %s", ci.Name, ci.Commit.ID) if ci.Commit.ID != ci.Commit.Expected { fmt.Fprintf(dockerCli.Out(), " (expected: %s)", ci.Commit.Expected) } fmt.Fprintf(dockerCli.Out(), "\n") } if len(info.SecurityOptions) != 0 { fmt.Fprintf(dockerCli.Out(), "Security Options:\n") for _, o := range info.SecurityOptions { switch o.Key { case "Name": fmt.Fprintf(dockerCli.Out(), " %s\n", o.Value) case "Profile": if o.Value != "default" { fmt.Fprintf(dockerCli.Err(), " WARNING: You're not using the default seccomp profile\n") } fmt.Fprintf(dockerCli.Out(), " %s: %s\n", o.Key, o.Value) } } } } // Isolation only has meaning on a Windows daemon. if info.OSType == "windows" { fmt.Fprintf(dockerCli.Out(), "Default Isolation: %v\n", info.Isolation) } ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Kernel Version: %s\n", info.KernelVersion) ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Operating System: %s\n", info.OperatingSystem) ioutils.FprintfIfNotEmpty(dockerCli.Out(), "OSType: %s\n", info.OSType) ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Architecture: %s\n", info.Architecture) fmt.Fprintf(dockerCli.Out(), "CPUs: %d\n", info.NCPU) fmt.Fprintf(dockerCli.Out(), "Total Memory: %s\n", units.BytesSize(float64(info.MemTotal))) ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Name: %s\n", info.Name) ioutils.FprintfIfNotEmpty(dockerCli.Out(), "ID: %s\n", info.ID) fmt.Fprintf(dockerCli.Out(), "Docker Root Dir: %s\n", info.DockerRootDir) fmt.Fprintf(dockerCli.Out(), "Debug Mode (client): %v\n", utils.IsDebugEnabled()) fmt.Fprintf(dockerCli.Out(), "Debug Mode (server): %v\n", info.Debug) if info.Debug { fmt.Fprintf(dockerCli.Out(), " File Descriptors: %d\n", info.NFd) fmt.Fprintf(dockerCli.Out(), " Goroutines: %d\n", info.NGoroutines) fmt.Fprintf(dockerCli.Out(), " System Time: %s\n", info.SystemTime) fmt.Fprintf(dockerCli.Out(), " EventsListeners: %d\n", info.NEventsListener) } ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Http Proxy: %s\n", info.HTTPProxy) ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Https Proxy: %s\n", info.HTTPSProxy) ioutils.FprintfIfNotEmpty(dockerCli.Out(), "No Proxy: %s\n", info.NoProxy) if info.IndexServerAddress != "" { u := dockerCli.ConfigFile().AuthConfigs[info.IndexServerAddress].Username if len(u) > 0 { fmt.Fprintf(dockerCli.Out(), "Username: %v\n", u) } fmt.Fprintf(dockerCli.Out(), "Registry: %v\n", info.IndexServerAddress) } // Only output these warnings if the server does not support these features if info.OSType != "windows" { if !info.MemoryLimit { fmt.Fprintln(dockerCli.Err(), "WARNING: No memory limit support") } if !info.SwapLimit { fmt.Fprintln(dockerCli.Err(), "WARNING: No swap limit support") } if !info.KernelMemory { fmt.Fprintln(dockerCli.Err(), "WARNING: No kernel memory limit support") } if !info.OomKillDisable { fmt.Fprintln(dockerCli.Err(), "WARNING: No oom kill disable support") } if !info.CPUCfsQuota { fmt.Fprintln(dockerCli.Err(), "WARNING: No cpu cfs quota support") } if !info.CPUCfsPeriod { fmt.Fprintln(dockerCli.Err(), "WARNING: No cpu cfs period support") } if !info.CPUShares { fmt.Fprintln(dockerCli.Err(), "WARNING: No cpu shares support") } if !info.CPUSet { fmt.Fprintln(dockerCli.Err(), "WARNING: No cpuset support") } if !info.IPv4Forwarding { fmt.Fprintln(dockerCli.Err(), "WARNING: IPv4 forwarding is disabled") } if !info.BridgeNfIptables { fmt.Fprintln(dockerCli.Err(), "WARNING: bridge-nf-call-iptables is disabled") } if !info.BridgeNfIP6tables { fmt.Fprintln(dockerCli.Err(), "WARNING: bridge-nf-call-ip6tables is disabled") } } if info.Labels != nil { fmt.Fprintln(dockerCli.Out(), "Labels:") for _, attribute := range info.Labels { fmt.Fprintf(dockerCli.Out(), " %s\n", attribute) } // TODO: Engine labels with duplicate keys has been deprecated in 1.13 and will be error out // after 3 release cycles (1.16). For now, a WARNING will be generated. The following will // be removed eventually. labelMap := map[string]string{} for _, label := range info.Labels { stringSlice := strings.SplitN(label, "=", 2) if len(stringSlice) > 1 { // If there is a conflict we will throw out an warning if v, ok := labelMap[stringSlice[0]]; ok && v != stringSlice[1] { fmt.Fprintln(dockerCli.Err(), "WARNING: labels with duplicate keys and conflicting values have been deprecated") break } labelMap[stringSlice[0]] = stringSlice[1] } } } fmt.Fprintf(dockerCli.Out(), "Experimental: %v\n", info.ExperimentalBuild) if info.ClusterStore != "" { fmt.Fprintf(dockerCli.Out(), "Cluster Store: %s\n", info.ClusterStore) } if info.ClusterAdvertise != "" { fmt.Fprintf(dockerCli.Out(), "Cluster Advertise: %s\n", info.ClusterAdvertise) } if info.RegistryConfig != nil && (len(info.RegistryConfig.InsecureRegistryCIDRs) > 0 || len(info.RegistryConfig.IndexConfigs) > 0) { fmt.Fprintln(dockerCli.Out(), "Insecure Registries:") for _, registry := range info.RegistryConfig.IndexConfigs { if registry.Secure == false { fmt.Fprintf(dockerCli.Out(), " %s\n", registry.Name) } } for _, registry := range info.RegistryConfig.InsecureRegistryCIDRs { mask, _ := registry.Mask.Size() fmt.Fprintf(dockerCli.Out(), " %s/%d\n", registry.IP.String(), mask) } } if info.RegistryConfig != nil && len(info.RegistryConfig.Mirrors) > 0 { fmt.Fprintln(dockerCli.Out(), "Registry Mirrors:") for _, mirror := range info.RegistryConfig.Mirrors { fmt.Fprintf(dockerCli.Out(), " %s\n", mirror) } } fmt.Fprintf(dockerCli.Out(), "Live Restore Enabled: %v\n", info.LiveRestoreEnabled) return nil }
func runInfo(dockerCli *client.DockerCli) error { info, err := dockerCli.Client().Info(context.Background()) if err != nil { return err } fmt.Fprintf(dockerCli.Out(), "Containers: %d\n", info.Containers) fmt.Fprintf(dockerCli.Out(), " Running: %d\n", info.ContainersRunning) fmt.Fprintf(dockerCli.Out(), " Paused: %d\n", info.ContainersPaused) fmt.Fprintf(dockerCli.Out(), " Stopped: %d\n", info.ContainersStopped) fmt.Fprintf(dockerCli.Out(), "Images: %d\n", info.Images) ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Server Version: %s\n", info.ServerVersion) ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Storage Driver: %s\n", info.Driver) if info.DriverStatus != nil { for _, pair := range info.DriverStatus { fmt.Fprintf(dockerCli.Out(), " %s: %s\n", pair[0], pair[1]) // print a warning if devicemapper is using a loopback file if pair[0] == "Data loop file" { fmt.Fprintln(dockerCli.Err(), " WARNING: Usage of loopback devices is strongly discouraged for production use. Either use `--storage-opt dm.thinpooldev` or use `--storage-opt dm.no_warn_on_loop_devices=true` to suppress this warning.") } } } if info.SystemStatus != nil { for _, pair := range info.SystemStatus { fmt.Fprintf(dockerCli.Out(), "%s: %s\n", pair[0], pair[1]) } } ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Execution Driver: %s\n", info.ExecutionDriver) ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Logging Driver: %s\n", info.LoggingDriver) ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Cgroup Driver: %s\n", info.CgroupDriver) fmt.Fprintf(dockerCli.Out(), "Plugins: \n") fmt.Fprintf(dockerCli.Out(), " Volume:") fmt.Fprintf(dockerCli.Out(), " %s", strings.Join(info.Plugins.Volume, " ")) fmt.Fprintf(dockerCli.Out(), "\n") fmt.Fprintf(dockerCli.Out(), " Network:") fmt.Fprintf(dockerCli.Out(), " %s", strings.Join(info.Plugins.Network, " ")) fmt.Fprintf(dockerCli.Out(), "\n") if len(info.Plugins.Authorization) != 0 { fmt.Fprintf(dockerCli.Out(), " Authorization:") fmt.Fprintf(dockerCli.Out(), " %s", strings.Join(info.Plugins.Authorization, " ")) fmt.Fprintf(dockerCli.Out(), "\n") } fmt.Fprintf(dockerCli.Out(), "Swarm: %v\n", info.Swarm.LocalNodeState) if info.Swarm.LocalNodeState != swarm.LocalNodeStateInactive { fmt.Fprintf(dockerCli.Out(), " NodeID: %s\n", info.Swarm.NodeID) if info.Swarm.Error != "" { fmt.Fprintf(dockerCli.Out(), " Error: %v\n", info.Swarm.Error) } if info.Swarm.ControlAvailable { fmt.Fprintf(dockerCli.Out(), " IsManager: Yes\n") fmt.Fprintf(dockerCli.Out(), " Managers: %d\n", info.Swarm.Managers) fmt.Fprintf(dockerCli.Out(), " Nodes: %d\n", info.Swarm.Nodes) ioutils.FprintfIfNotEmpty(dockerCli.Out(), " CACertHash: %s\n", info.Swarm.CACertHash) } else { fmt.Fprintf(dockerCli.Out(), " IsManager: No\n") } } if len(info.Runtimes) > 0 { fmt.Fprintf(dockerCli.Out(), "Runtimes:") for name := range info.Runtimes { fmt.Fprintf(dockerCli.Out(), " %s", name) } fmt.Fprint(dockerCli.Out(), "\n") fmt.Fprintf(dockerCli.Out(), "Default Runtime: %s\n", info.DefaultRuntime) } fmt.Fprintf(dockerCli.Out(), "Security Options:") ioutils.FprintfIfNotEmpty(dockerCli.Out(), " %s", strings.Join(info.SecurityOptions, " ")) fmt.Fprintf(dockerCli.Out(), "\n") ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Kernel Version: %s\n", info.KernelVersion) ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Operating System: %s\n", info.OperatingSystem) ioutils.FprintfIfNotEmpty(dockerCli.Out(), "OSType: %s\n", info.OSType) ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Architecture: %s\n", info.Architecture) fmt.Fprintf(dockerCli.Out(), "CPUs: %d\n", info.NCPU) fmt.Fprintf(dockerCli.Out(), "Total Memory: %s\n", units.BytesSize(float64(info.MemTotal))) ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Name: %s\n", info.Name) ioutils.FprintfIfNotEmpty(dockerCli.Out(), "ID: %s\n", info.ID) fmt.Fprintf(dockerCli.Out(), "Docker Root Dir: %s\n", info.DockerRootDir) fmt.Fprintf(dockerCli.Out(), "Debug Mode (client): %v\n", utils.IsDebugEnabled()) fmt.Fprintf(dockerCli.Out(), "Debug Mode (server): %v\n", info.Debug) if info.Debug { fmt.Fprintf(dockerCli.Out(), " File Descriptors: %d\n", info.NFd) fmt.Fprintf(dockerCli.Out(), " Goroutines: %d\n", info.NGoroutines) fmt.Fprintf(dockerCli.Out(), " System Time: %s\n", info.SystemTime) fmt.Fprintf(dockerCli.Out(), " EventsListeners: %d\n", info.NEventsListener) } ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Http Proxy: %s\n", info.HTTPProxy) ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Https Proxy: %s\n", info.HTTPSProxy) ioutils.FprintfIfNotEmpty(dockerCli.Out(), "No Proxy: %s\n", info.NoProxy) if info.IndexServerAddress != "" { u := dockerCli.ConfigFile().AuthConfigs[info.IndexServerAddress].Username if len(u) > 0 { fmt.Fprintf(dockerCli.Out(), "Username: %v\n", u) } fmt.Fprintf(dockerCli.Out(), "Registry: %v\n", info.IndexServerAddress) } // Only output these warnings if the server does not support these features if info.OSType != "windows" { if !info.MemoryLimit { fmt.Fprintln(dockerCli.Err(), "WARNING: No memory limit support") } if !info.SwapLimit { fmt.Fprintln(dockerCli.Err(), "WARNING: No swap limit support") } if !info.KernelMemory { fmt.Fprintln(dockerCli.Err(), "WARNING: No kernel memory limit support") } if !info.OomKillDisable { fmt.Fprintln(dockerCli.Err(), "WARNING: No oom kill disable support") } if !info.CPUCfsQuota { fmt.Fprintln(dockerCli.Err(), "WARNING: No cpu cfs quota support") } if !info.CPUCfsPeriod { fmt.Fprintln(dockerCli.Err(), "WARNING: No cpu cfs period support") } if !info.CPUShares { fmt.Fprintln(dockerCli.Err(), "WARNING: No cpu shares support") } if !info.CPUSet { fmt.Fprintln(dockerCli.Err(), "WARNING: No cpuset support") } if !info.IPv4Forwarding { fmt.Fprintln(dockerCli.Err(), "WARNING: IPv4 forwarding is disabled") } if !info.BridgeNfIptables { fmt.Fprintln(dockerCli.Err(), "WARNING: bridge-nf-call-iptables is disabled") } if !info.BridgeNfIP6tables { fmt.Fprintln(dockerCli.Err(), "WARNING: bridge-nf-call-ip6tables is disabled") } } if info.Labels != nil { fmt.Fprintln(dockerCli.Out(), "Labels:") for _, attribute := range info.Labels { fmt.Fprintf(dockerCli.Out(), " %s\n", attribute) } } ioutils.FprintfIfTrue(dockerCli.Out(), "Experimental: %v\n", info.ExperimentalBuild) if info.ClusterStore != "" { fmt.Fprintf(dockerCli.Out(), "Cluster Store: %s\n", info.ClusterStore) } if info.ClusterAdvertise != "" { fmt.Fprintf(dockerCli.Out(), "Cluster Advertise: %s\n", info.ClusterAdvertise) } if info.RegistryConfig != nil && (len(info.RegistryConfig.InsecureRegistryCIDRs) > 0 || len(info.RegistryConfig.IndexConfigs) > 0) { fmt.Fprintln(dockerCli.Out(), "Insecure Registries:") for _, registry := range info.RegistryConfig.IndexConfigs { if registry.Secure == false { fmt.Fprintf(dockerCli.Out(), " %s\n", registry.Name) } } for _, registry := range info.RegistryConfig.InsecureRegistryCIDRs { mask, _ := registry.Mask.Size() fmt.Fprintf(dockerCli.Out(), " %s/%d\n", registry.IP.String(), mask) } } return nil }
// CmdDaemon is the daemon command, called the raw arguments after `docker daemon`. func (cli *DaemonCli) CmdDaemon(args ...string) error { // warn from uuid package when running the daemon uuid.Loggerf = logrus.Warnf //调整一下daemon的启动方式 if !commonFlags.FlagSet.IsEmpty() || !clientFlags.FlagSet.IsEmpty() { // deny `docker -D daemon` illegalFlag := getGlobalFlag() fmt.Fprintf(os.Stderr, "invalid flag '-%s'.\nSee 'docker daemon --help'.\n", illegalFlag.Names[0]) os.Exit(1) } else { // allow new form `docker daemon -D` flag.Merge(cli.flags, commonFlags.FlagSet) } configFile := cli.flags.String([]string{daemonConfigFileFlag}, defaultDaemonConfigFile, "Daemon configuration file") //匹配配置参数 cli.flags.ParseFlags(args, true) //配置参数生效 commonFlags.PostParse() if commonFlags.TrustKey == "" { commonFlags.TrustKey = filepath.Join(getDaemonConfDir(), defaultTrustKeyFile) } cliConfig, err := loadDaemonCliConfig(cli.Config, cli.flags, commonFlags, *configFile) if err != nil { fmt.Fprint(os.Stderr, err) os.Exit(1) } cli.Config = cliConfig if cli.Config.Debug { utils.EnableDebug() } if utils.ExperimentalBuild() { logrus.Warn("Running experimental build") } logrus.SetFormatter(&logrus.TextFormatter{ TimestampFormat: jsonlog.RFC3339NanoFixed, DisableColors: cli.Config.RawLogs, }) if err := setDefaultUmask(); err != nil { logrus.Fatalf("Failed to set umask: %v", err) } if len(cli.LogConfig.Config) > 0 { if err := logger.ValidateLogOpts(cli.LogConfig.Type, cli.LogConfig.Config); err != nil { logrus.Fatalf("Failed to set log opts: %v", err) } } var pfile *pidfile.PIDFile if cli.Pidfile != "" { pf, err := pidfile.New(cli.Pidfile) if err != nil { logrus.Fatalf("Error starting daemon: %v", err) } pfile = pf defer func() { if err := pfile.Remove(); err != nil { logrus.Error(err) } }() } //定义apiserver的配置,包括认证、日志输出、版本等。 serverConfig := &apiserver.Config{ AuthorizationPluginNames: cli.Config.AuthorizationPlugins, Logging: true, SocketGroup: cli.Config.SocketGroup, Version: dockerversion.Version, } serverConfig = setPlatformServerConfig(serverConfig, cli.Config) if cli.Config.TLS { tlsOptions := tlsconfig.Options{ CAFile: cli.Config.CommonTLSOptions.CAFile, CertFile: cli.Config.CommonTLSOptions.CertFile, KeyFile: cli.Config.CommonTLSOptions.KeyFile, } if cli.Config.TLSVerify { // server requires and verifies client's certificate tlsOptions.ClientAuth = tls.RequireAndVerifyClientCert } tlsConfig, err := tlsconfig.Server(tlsOptions) if err != nil { logrus.Fatal(err) } serverConfig.TLSConfig = tlsConfig } if len(cli.Config.Hosts) == 0 { cli.Config.Hosts = make([]string, 1) } //定义一个新的apiserver。 //apiServer是一个这样的结构(api/server/server.go): /* type Server struct { cfg *Config servers []*HTTPServer routers []router.Router authZPlugins []authorization.Plugin routerSwapper *routerSwapper } */ 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.Fatalf("error parsin g -H %s : %v", cli.Config.Hosts[i], err) } protoAddr := cli.Config.Hosts[i] protoAddrParts := strings.SplitN(protoAddr, "://", 2) if len(protoAddrParts) != 2 { logrus.Fatalf("bad format %s, expected PROTO://ADDR", protoAddr) } l, err := listeners.Init(protoAddrParts[0], protoAddrParts[1], serverConfig.SocketGroup, serverConfig.TLSConfig) if err != nil { logrus.Fatal(err) } logrus.Debugf("Listener created for HTTP on %s (%s)", protoAddrParts[0], protoAddrParts[1]) //初始化api的servers数组,里面放着的都是httpserver类型。此时也没有具体的运行什么 api.Accept(protoAddrParts[1], l...) } if err := migrateKey(); err != nil { logrus.Fatal(err) } cli.TrustKeyPath = commonFlags.TrustKey //创建镜像仓库服务 registryService := registry.NewService(cli.Config.ServiceOptions) //初始化libcontainer。比如在linux中,就会调用libcontainerd/remote_linux.go中的New方法。 containerdRemote, err := libcontainerd.New(filepath.Join(cli.Config.ExecRoot, "libcontainerd"), cli.getPlatformRemoteOptions()...) if err != nil { logrus.Fatal(err) } //初始化守护进程使得能够服务。需要输入仓库服务和libcontainerd服务的参数。 //返回的d是Daemon类型: /* type Daemon struct { ID string repository string containers container.Store execCommands *exec.Store referenceStore reference.Store downloadManager *xfer.LayerDownloadManager uploadManager *xfer.LayerUploadManager distributionMetadataStore dmetadata.Store trustKey libtrust.PrivateKey idIndex *truncindex.TruncIndex configStore *Config statsCollector *statsCollector defaultLogConfig containertypes.LogConfig RegistryService *registry.Service EventsService *events.Events netController libnetwork.NetworkController volumes *store.VolumeStore discoveryWatcher discoveryReloader root string seccompEnabled bool shutdown bool uidMaps []idtools.IDMap gidMaps []idtools.IDMap layerStore layer.Store imageStore image.Store nameIndex *registrar.Registrar linkIndex *linkIndex containerd libcontainerd.Client defaultIsolation containertypes.Isolation // Default isolation mode on Windows } */ d, err := daemon.NewDaemon(cli.Config, registryService, containerdRemote) if err != nil { if pfile != nil { if err := pfile.Remove(); err != nil { logrus.Error(err) } } logrus.Fatalf("Error starting daemon: %v", err) } logrus.Info("Daemon has completed initialization") logrus.WithFields(logrus.Fields{ "version": dockerversion.Version, "commit": dockerversion.GitCommit, "graphdriver": d.GraphDriverName(), }).Info("Docker daemon") //初始化http的路由,这个路由设计的非常易懂,所有的路由及处理函数的映射关系 //请见api/server/router/文件夹中的内容。有类似这样的内容: //router.NewPostRoute("/containers/create", r.postContainersCreate), //其中,对应的处理函数postContainersCreate在api/server/router/container/container_routes.go //但是,实际上这个函数也不做具体的事情,他交给backend去做,就是daemon去做 /* ccr, err := s.backend.ContainerCreate(types.ContainerCreateConfig{ Name: name, Config: config, HostConfig: hostConfig, NetworkingConfig: networkingConfig, AdjustCPUShares: adjustCPUShares, }) */ //其中的ContainerCreate在 initRouter(api, d) reload := func(config *daemon.Config) { if err := d.Reload(config); err != nil { logrus.Errorf("Error reconfiguring the daemon: %v", err) return } if config.IsValueSet("debug") { debugEnabled := utils.IsDebugEnabled() switch { case debugEnabled && !config.Debug: // disable debug utils.DisableDebug() api.DisableProfiler() case config.Debug && !debugEnabled: // enable debug utils.EnableDebug() api.EnableProfiler() } } } setupConfigReloadTrap(*configFile, cli.flags, reload) // 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 //设置一个传输apiServer状态的通道 serveAPIWait := make(chan error) //重新开启一个goroutine作为httpServer。 //具体的请查看api/server/server.go中的方法func (s *Server) serveAPI() error go api.Wait(serveAPIWait) signal.Trap(func() { api.Close() <-serveAPIWait shutdownDaemon(d, 15) if pfile != nil { if err := pfile.Remove(); err != nil { logrus.Error(err) } } }) // 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 //<-表示接受通道值,只有当通道中有值的时候,才会返回。 //也就是说主线程一直在等待api.wait的goroutine启动apiServer之后的返回才会进行。 errAPI := <-serveAPIWait //当接收到返回(返回就是错误了),开始清理进程。 shutdownDaemon(d, 15) containerdRemote.Cleanup() if errAPI != nil { if pfile != nil { if err := pfile.Remove(); err != nil { logrus.Error(err) } } logrus.Fatalf("Shutting down due to ServeAPI error: %v", errAPI) } return nil }