github.com/jiasir/docker@v1.3.3-0.20170609024000-252e610103e7/cmd/dockerd/daemon.go (about) 1 package main 2 3 import ( 4 "context" 5 "crypto/tls" 6 "fmt" 7 "os" 8 "path/filepath" 9 "strings" 10 "time" 11 12 "github.com/Sirupsen/logrus" 13 "github.com/docker/distribution/uuid" 14 "github.com/docker/docker/api" 15 apiserver "github.com/docker/docker/api/server" 16 buildbackend "github.com/docker/docker/api/server/backend/build" 17 "github.com/docker/docker/api/server/middleware" 18 "github.com/docker/docker/api/server/router" 19 "github.com/docker/docker/api/server/router/build" 20 checkpointrouter "github.com/docker/docker/api/server/router/checkpoint" 21 "github.com/docker/docker/api/server/router/container" 22 distributionrouter "github.com/docker/docker/api/server/router/distribution" 23 "github.com/docker/docker/api/server/router/image" 24 "github.com/docker/docker/api/server/router/network" 25 pluginrouter "github.com/docker/docker/api/server/router/plugin" 26 swarmrouter "github.com/docker/docker/api/server/router/swarm" 27 systemrouter "github.com/docker/docker/api/server/router/system" 28 "github.com/docker/docker/api/server/router/volume" 29 "github.com/docker/docker/cli/debug" 30 "github.com/docker/docker/daemon" 31 "github.com/docker/docker/daemon/cluster" 32 "github.com/docker/docker/daemon/config" 33 "github.com/docker/docker/daemon/logger" 34 "github.com/docker/docker/dockerversion" 35 "github.com/docker/docker/libcontainerd" 36 dopts "github.com/docker/docker/opts" 37 "github.com/docker/docker/pkg/authorization" 38 "github.com/docker/docker/pkg/jsonlog" 39 "github.com/docker/docker/pkg/listeners" 40 "github.com/docker/docker/pkg/pidfile" 41 "github.com/docker/docker/pkg/plugingetter" 42 "github.com/docker/docker/pkg/signal" 43 "github.com/docker/docker/plugin" 44 "github.com/docker/docker/registry" 45 "github.com/docker/docker/runconfig" 46 "github.com/docker/go-connections/tlsconfig" 47 swarmapi "github.com/docker/swarmkit/api" 48 "github.com/spf13/pflag" 49 ) 50 51 // DaemonCli represents the daemon CLI. 52 type DaemonCli struct { 53 *config.Config 54 configFile *string 55 flags *pflag.FlagSet 56 57 api *apiserver.Server 58 d *daemon.Daemon 59 authzMiddleware *authorization.Middleware // authzMiddleware enables to dynamically reload the authorization plugins 60 } 61 62 // NewDaemonCli returns a daemon CLI 63 func NewDaemonCli() *DaemonCli { 64 return &DaemonCli{} 65 } 66 67 func (cli *DaemonCli) start(opts *daemonOptions) (err error) { 68 stopc := make(chan bool) 69 defer close(stopc) 70 71 // warn from uuid package when running the daemon 72 uuid.Loggerf = logrus.Warnf 73 74 opts.SetDefaultOptions(opts.flags) 75 76 if cli.Config, err = loadDaemonCliConfig(opts); err != nil { 77 return err 78 } 79 cli.configFile = &opts.configFile 80 cli.flags = opts.flags 81 82 if cli.Config.Debug { 83 debug.Enable() 84 } 85 86 if cli.Config.Experimental { 87 logrus.Warn("Running experimental build") 88 } 89 90 logrus.SetFormatter(&logrus.TextFormatter{ 91 TimestampFormat: jsonlog.RFC3339NanoFixed, 92 DisableColors: cli.Config.RawLogs, 93 }) 94 95 if err := setDefaultUmask(); err != nil { 96 return fmt.Errorf("Failed to set umask: %v", err) 97 } 98 99 if len(cli.LogConfig.Config) > 0 { 100 if err := logger.ValidateLogOpts(cli.LogConfig.Type, cli.LogConfig.Config); err != nil { 101 return fmt.Errorf("Failed to set log opts: %v", err) 102 } 103 } 104 105 // Create the daemon root before we create ANY other files (PID, or migrate keys) 106 // to ensure the appropriate ACL is set (particularly relevant on Windows) 107 if err := daemon.CreateDaemonRoot(cli.Config); err != nil { 108 return err 109 } 110 111 if cli.Pidfile != "" { 112 pf, err := pidfile.New(cli.Pidfile) 113 if err != nil { 114 return fmt.Errorf("Error starting daemon: %v", err) 115 } 116 defer func() { 117 if err := pf.Remove(); err != nil { 118 logrus.Error(err) 119 } 120 }() 121 } 122 123 serverConfig := &apiserver.Config{ 124 Logging: true, 125 SocketGroup: cli.Config.SocketGroup, 126 Version: dockerversion.Version, 127 EnableCors: cli.Config.EnableCors, 128 CorsHeaders: cli.Config.CorsHeaders, 129 } 130 131 if cli.Config.TLS { 132 tlsOptions := tlsconfig.Options{ 133 CAFile: cli.Config.CommonTLSOptions.CAFile, 134 CertFile: cli.Config.CommonTLSOptions.CertFile, 135 KeyFile: cli.Config.CommonTLSOptions.KeyFile, 136 ExclusiveRootPools: true, 137 } 138 139 if cli.Config.TLSVerify { 140 // server requires and verifies client's certificate 141 tlsOptions.ClientAuth = tls.RequireAndVerifyClientCert 142 } 143 tlsConfig, err := tlsconfig.Server(tlsOptions) 144 if err != nil { 145 return err 146 } 147 serverConfig.TLSConfig = tlsConfig 148 } 149 150 if len(cli.Config.Hosts) == 0 { 151 cli.Config.Hosts = make([]string, 1) 152 } 153 154 api := apiserver.New(serverConfig) 155 cli.api = api 156 157 var hosts []string 158 159 for i := 0; i < len(cli.Config.Hosts); i++ { 160 var err error 161 if cli.Config.Hosts[i], err = dopts.ParseHost(cli.Config.TLS, cli.Config.Hosts[i]); err != nil { 162 return fmt.Errorf("error parsing -H %s : %v", cli.Config.Hosts[i], err) 163 } 164 165 protoAddr := cli.Config.Hosts[i] 166 protoAddrParts := strings.SplitN(protoAddr, "://", 2) 167 if len(protoAddrParts) != 2 { 168 return fmt.Errorf("bad format %s, expected PROTO://ADDR", protoAddr) 169 } 170 171 proto := protoAddrParts[0] 172 addr := protoAddrParts[1] 173 174 // It's a bad idea to bind to TCP without tlsverify. 175 if proto == "tcp" && (serverConfig.TLSConfig == nil || serverConfig.TLSConfig.ClientAuth != tls.RequireAndVerifyClientCert) { 176 logrus.Warn("[!] DON'T BIND ON ANY IP ADDRESS WITHOUT setting --tlsverify IF YOU DON'T KNOW WHAT YOU'RE DOING [!]") 177 } 178 ls, err := listeners.Init(proto, addr, serverConfig.SocketGroup, serverConfig.TLSConfig) 179 if err != nil { 180 return err 181 } 182 ls = wrapListeners(proto, ls) 183 // If we're binding to a TCP port, make sure that a container doesn't try to use it. 184 if proto == "tcp" { 185 if err := allocateDaemonPort(addr); err != nil { 186 return err 187 } 188 } 189 logrus.Debugf("Listener created for HTTP on %s (%s)", proto, addr) 190 hosts = append(hosts, protoAddrParts[1]) 191 api.Accept(addr, ls...) 192 } 193 194 registryService := registry.NewService(cli.Config.ServiceOptions) 195 containerdRemote, err := libcontainerd.New(cli.getLibcontainerdRoot(), cli.getPlatformRemoteOptions()...) 196 if err != nil { 197 return err 198 } 199 signal.Trap(func() { 200 cli.stop() 201 <-stopc // wait for daemonCli.start() to return 202 }) 203 204 // Notify that the API is active, but before daemon is set up. 205 preNotifySystem() 206 207 pluginStore := plugin.NewStore() 208 209 if err := cli.initMiddlewares(api, serverConfig, pluginStore); err != nil { 210 logrus.Fatalf("Error creating middlewares: %v", err) 211 } 212 213 d, err := daemon.NewDaemon(cli.Config, registryService, containerdRemote, pluginStore) 214 if err != nil { 215 return fmt.Errorf("Error starting daemon: %v", err) 216 } 217 218 d.StoreHosts(hosts) 219 220 // validate after NewDaemon has restored enabled plugins. Dont change order. 221 if err := validateAuthzPlugins(cli.Config.AuthorizationPlugins, pluginStore); err != nil { 222 return fmt.Errorf("Error validating authorization plugin: %v", err) 223 } 224 225 if cli.Config.MetricsAddress != "" { 226 if !d.HasExperimental() { 227 return fmt.Errorf("metrics-addr is only supported when experimental is enabled") 228 } 229 if err := startMetricsServer(cli.Config.MetricsAddress); err != nil { 230 return err 231 } 232 } 233 234 name, _ := os.Hostname() 235 236 // Use a buffered channel to pass changes from store watch API to daemon 237 // A buffer allows store watch API and daemon processing to not wait for each other 238 watchStream := make(chan *swarmapi.WatchMessage, 32) 239 240 c, err := cluster.New(cluster.Config{ 241 Root: cli.Config.Root, 242 Name: name, 243 Backend: d, 244 NetworkSubnetsProvider: d, 245 DefaultAdvertiseAddr: cli.Config.SwarmDefaultAdvertiseAddr, 246 RuntimeRoot: cli.getSwarmRunRoot(), 247 WatchStream: watchStream, 248 }) 249 if err != nil { 250 logrus.Fatalf("Error creating cluster component: %v", err) 251 } 252 d.SetCluster(c) 253 err = c.Start() 254 if err != nil { 255 logrus.Fatalf("Error starting cluster component: %v", err) 256 } 257 258 // Restart all autostart containers which has a swarm endpoint 259 // and is not yet running now that we have successfully 260 // initialized the cluster. 261 d.RestartSwarmContainers() 262 263 logrus.Info("Daemon has completed initialization") 264 265 logrus.WithFields(logrus.Fields{ 266 "version": dockerversion.Version, 267 "commit": dockerversion.GitCommit, 268 "graphdriver": d.GraphDriverName(), 269 }).Info("Docker daemon") 270 271 cli.d = d 272 273 initRouter(api, d, c) 274 275 // process cluster change notifications 276 watchCtx, cancel := context.WithCancel(context.Background()) 277 defer cancel() 278 go d.ProcessClusterNotifications(watchCtx, watchStream) 279 280 cli.setupConfigReloadTrap() 281 282 // The serve API routine never exits unless an error occurs 283 // We need to start it as a goroutine and wait on it so 284 // daemon doesn't exit 285 serveAPIWait := make(chan error) 286 go api.Wait(serveAPIWait) 287 288 // after the daemon is done setting up we can notify systemd api 289 notifySystem() 290 291 // Daemon is fully initialized and handling API traffic 292 // Wait for serve API to complete 293 errAPI := <-serveAPIWait 294 c.Cleanup() 295 shutdownDaemon(d) 296 containerdRemote.Cleanup() 297 if errAPI != nil { 298 return fmt.Errorf("Shutting down due to ServeAPI error: %v", errAPI) 299 } 300 301 return nil 302 } 303 304 func (cli *DaemonCli) reloadConfig() { 305 reload := func(config *config.Config) { 306 307 // Revalidate and reload the authorization plugins 308 if err := validateAuthzPlugins(config.AuthorizationPlugins, cli.d.PluginStore); err != nil { 309 logrus.Fatalf("Error validating authorization plugin: %v", err) 310 return 311 } 312 cli.authzMiddleware.SetPlugins(config.AuthorizationPlugins) 313 314 if err := cli.d.Reload(config); err != nil { 315 logrus.Errorf("Error reconfiguring the daemon: %v", err) 316 return 317 } 318 319 if config.IsValueSet("debug") { 320 debugEnabled := debug.IsEnabled() 321 switch { 322 case debugEnabled && !config.Debug: // disable debug 323 debug.Disable() 324 cli.api.DisableProfiler() 325 case config.Debug && !debugEnabled: // enable debug 326 debug.Enable() 327 cli.api.EnableProfiler() 328 } 329 330 } 331 } 332 333 if err := config.Reload(*cli.configFile, cli.flags, reload); err != nil { 334 logrus.Error(err) 335 } 336 } 337 338 func (cli *DaemonCli) stop() { 339 cli.api.Close() 340 } 341 342 // shutdownDaemon just wraps daemon.Shutdown() to handle a timeout in case 343 // d.Shutdown() is waiting too long to kill container or worst it's 344 // blocked there 345 func shutdownDaemon(d *daemon.Daemon) { 346 shutdownTimeout := d.ShutdownTimeout() 347 ch := make(chan struct{}) 348 go func() { 349 d.Shutdown() 350 close(ch) 351 }() 352 if shutdownTimeout < 0 { 353 <-ch 354 logrus.Debug("Clean shutdown succeeded") 355 return 356 } 357 select { 358 case <-ch: 359 logrus.Debug("Clean shutdown succeeded") 360 case <-time.After(time.Duration(shutdownTimeout) * time.Second): 361 logrus.Error("Force shutdown daemon") 362 } 363 } 364 365 func loadDaemonCliConfig(opts *daemonOptions) (*config.Config, error) { 366 conf := opts.daemonConfig 367 flags := opts.flags 368 conf.Debug = opts.Debug 369 conf.Hosts = opts.Hosts 370 conf.LogLevel = opts.LogLevel 371 conf.TLS = opts.TLS 372 conf.TLSVerify = opts.TLSVerify 373 conf.CommonTLSOptions = config.CommonTLSOptions{} 374 375 if opts.TLSOptions != nil { 376 conf.CommonTLSOptions.CAFile = opts.TLSOptions.CAFile 377 conf.CommonTLSOptions.CertFile = opts.TLSOptions.CertFile 378 conf.CommonTLSOptions.KeyFile = opts.TLSOptions.KeyFile 379 } 380 381 if conf.TrustKeyPath == "" { 382 conf.TrustKeyPath = filepath.Join( 383 getDaemonConfDir(conf.Root), 384 defaultTrustKeyFile) 385 } 386 387 if flags.Changed("graph") && flags.Changed("data-root") { 388 return nil, fmt.Errorf(`cannot specify both "--graph" and "--data-root" option`) 389 } 390 391 if opts.configFile != "" { 392 c, err := config.MergeDaemonConfigurations(conf, flags, opts.configFile) 393 if err != nil { 394 if flags.Changed("config-file") || !os.IsNotExist(err) { 395 return nil, fmt.Errorf("unable to configure the Docker daemon with file %s: %v\n", opts.configFile, err) 396 } 397 } 398 // the merged configuration can be nil if the config file didn't exist. 399 // leave the current configuration as it is if when that happens. 400 if c != nil { 401 conf = c 402 } 403 } 404 405 if err := config.Validate(conf); err != nil { 406 return nil, err 407 } 408 409 if flags.Changed("graph") { 410 logrus.Warnf(`the "-g / --graph" flag is deprecated. Please use "--data-root" instead`) 411 } 412 413 // Labels of the docker engine used to allow multiple values associated with the same key. 414 // This is deprecated in 1.13, and, be removed after 3 release cycles. 415 // The following will check the conflict of labels, and report a warning for deprecation. 416 // 417 // TODO: After 3 release cycles (17.12) an error will be returned, and labels will be 418 // sanitized to consolidate duplicate key-value pairs (config.Labels = newLabels): 419 // 420 // newLabels, err := daemon.GetConflictFreeLabels(config.Labels) 421 // if err != nil { 422 // return nil, err 423 // } 424 // config.Labels = newLabels 425 // 426 if _, err := config.GetConflictFreeLabels(conf.Labels); err != nil { 427 logrus.Warnf("Engine labels with duplicate keys and conflicting values have been deprecated: %s", err) 428 } 429 430 // Regardless of whether the user sets it to true or false, if they 431 // specify TLSVerify at all then we need to turn on TLS 432 if conf.IsValueSet(FlagTLSVerify) { 433 conf.TLS = true 434 } 435 436 // ensure that the log level is the one set after merging configurations 437 setLogLevel(conf.LogLevel) 438 439 return conf, nil 440 } 441 442 func initRouter(s *apiserver.Server, d *daemon.Daemon, c *cluster.Cluster) { 443 decoder := runconfig.ContainerDecoder{} 444 445 routers := []router.Router{ 446 // we need to add the checkpoint router before the container router or the DELETE gets masked 447 checkpointrouter.NewRouter(d, decoder), 448 container.NewRouter(d, decoder), 449 image.NewRouter(d, decoder), 450 systemrouter.NewRouter(d, c), 451 volume.NewRouter(d), 452 build.NewRouter(buildbackend.NewBackend(d, d), d), 453 swarmrouter.NewRouter(c), 454 pluginrouter.NewRouter(d.PluginManager()), 455 distributionrouter.NewRouter(d), 456 } 457 458 if d.NetworkControllerEnabled() { 459 routers = append(routers, network.NewRouter(d, c)) 460 } 461 462 if d.HasExperimental() { 463 for _, r := range routers { 464 for _, route := range r.Routes() { 465 if experimental, ok := route.(router.ExperimentalRoute); ok { 466 experimental.Enable() 467 } 468 } 469 } 470 } 471 472 s.InitRouter(debug.IsEnabled(), routers...) 473 } 474 475 func (cli *DaemonCli) initMiddlewares(s *apiserver.Server, cfg *apiserver.Config, pluginStore *plugin.Store) error { 476 v := cfg.Version 477 478 exp := middleware.NewExperimentalMiddleware(cli.Config.Experimental) 479 s.UseMiddleware(exp) 480 481 vm := middleware.NewVersionMiddleware(v, api.DefaultVersion, api.MinVersion) 482 s.UseMiddleware(vm) 483 484 if cfg.EnableCors || cfg.CorsHeaders != "" { 485 c := middleware.NewCORSMiddleware(cfg.CorsHeaders) 486 s.UseMiddleware(c) 487 } 488 489 cli.authzMiddleware = authorization.NewMiddleware(cli.Config.AuthorizationPlugins, pluginStore) 490 cli.Config.AuthzMiddleware = cli.authzMiddleware 491 s.UseMiddleware(cli.authzMiddleware) 492 return nil 493 } 494 495 // validates that the plugins requested with the --authorization-plugin flag are valid AuthzDriver 496 // plugins present on the host and available to the daemon 497 func validateAuthzPlugins(requestedPlugins []string, pg plugingetter.PluginGetter) error { 498 for _, reqPlugin := range requestedPlugins { 499 if _, err := pg.Get(reqPlugin, authorization.AuthZApiImplements, plugingetter.Lookup); err != nil { 500 return err 501 } 502 } 503 return nil 504 }