要讲daemon启动过程,首先得列出DaemonCli结构,即daemon命令行结构,与前面相同,只列出后面用到的成员变量:
其中*daemon.Config如下:// DaemonCli represents the daemon CLI. type DaemonCli struct { *daemon.Config registryOptions *registry.Options }
// Config defines the configuration of a docker daemon. type Config struct { CommonConfig }CommonConfig如下,定义了运行docker daemon时的配置信息,只列出后面用到的成员变量:
// CommonConfig defines the configuration of a docker daemon which are // common across platforms. type CommonConfig struct { AutoRestart bool Bridge bridgeConfig // Bridge holds bridge network specific configuration. Context map[string][]string DisableBridge bool DNS []string DNSOptions []string DNSSearch []string ExecDriver string ExecOptions []string ExecRoot string GraphDriver string GraphOptions []string Labels []string LogConfig runconfig.LogConfig Mtu int Pidfile string RemappedRoot string Root string TrustKeyPath string DefaultNetwork string }*registry.Options是与registry相关的结构,定义如下:
// Options holds command line options. type Options struct { Mirrors opts.ListOpts InsecureRegistries opts.ListOpts }ListOpts定义如下:
// ListOpts holds a list of values and a validation function. type ListOpts struct { values *[]string validator ValidatorFctType }values用来存储值,validator存储有效性。
接下来,进入docker/docker/daemon.go中看具体启动流程:
var ( flDaemon = flag.Bool([]string{"#d", "#-daemon"}, false, "Enable daemon mode (deprecated; use docker daemon)") daemonCli cli.Handler = NewDaemonCli() )
首先,提取了用户输入的命令,并新建了DaemonCli,deamon命令行结构;最初通过/docker/docker/common.go中的init()函数,创建并且初始化daemonFlags和commonFlags两个结构,commonFlags结构如下:
// CommonFlags represents flags that are common to both the client and the daemon. type CommonFlags struct { FlagSet *flag.FlagSet PostParse func() Debug bool Hosts []string LogLevel string TLS bool TLSVerify bool TLSOptions *tlsconfig.Options TrustKey string }
其中,跟tls安全传输相关的参数是
var tlsOptions tlsconfig.Options commonFlags.TLSOptions = &tlsOptions cmd.StringVar(&tlsOptions.CAFile, []string{"-tlscacert"}, filepath.Join(dockerCertPath, defaultCaFile), "Trust certs signed only by this CA") cmd.StringVar(&tlsOptions.CertFile, []string{"-tlscert"}, filepath.Join(dockerCertPath, defaultCertFile), "Path to TLS certificate file") cmd.StringVar(&tlsOptions.KeyFile, []string{"-tlskey"}, filepath.Join(dockerCertPath, defaultKeyFile), "Path to TLS key file")
init函数最后一行
cmd.Var(opts.NewListOptsRef(&commonFlags.Hosts, opts.ValidateHost), []string{"H", "-host"}, "Daemon socket(s) to connect to")确定daemon进程在那个host上启动 然后根据用户数据的命令会执行docker/docker/daemon.go中的CmdDaemon函数,先继续之前的参数的设置
if len(commonFlags.Hosts) == 0 { commonFlags.Hosts = []string{opts.DefaultHost} } if commonFlags.TrustKey == "" { commonFlags.TrustKey = filepath.Join(getDaemonConfDir(), defaultTrustKeyFile) }如果没有设定Hosts就将其设置为本机的地址,如果没有设定TrustKey,就将其设置为本机的特定位置存储的TrustKey。
到此,daemon启动的基本初始化已经完成,下面就是Server启动过程,tls是server和client的重要部分,在此列出tls相关结构:
// Options represents the information needed to create client and server TLS configurations. type Options struct { CAFile string CertFile string KeyFile string // client-only option InsecureSkipVerify bool // server-only option ClientAuth tls.ClientAuthType }以上代码在/docker/pkg/tlsconfig/config.go中,是建立客户端和服务端tls配置时需要提供的信息,是为docker提供tls服务建立的结构
// A Config structure is used to configure a TLS client or server.. type Config struct { // Certificates contains one or more certificate chains // to present to the other side of the connection. // Server configurations must include at least one certificate. Certificates []Certificate // NameToCertificate maps from a certificate name to an element of // Certificates. NameToCertificate map[string]*Certificate // RootCAs defines the set of root certificate authorities // that clients use when verifying server certificates. // If RootCAs is nil, TLS uses the host's root CA set. RootCAs *x509.CertPool
// ServerName is used to verify the hostname on the returned // certificates unless InsecureSkipVerify is given. It is also included // in the client's handshake to support virtual hosting. ServerName string // ClientAuth determines the server's policy for // TLS Client Authentication. The default is NoClientCert. ClientAuth ClientAuthType // ClientCAs defines the set of root certificate authorities // that servers use if required to verify a client certificate // by the policy in ClientAuth. ClientCAs *x509.CertPool // InsecureSkipVerify controls whether a client verifies the // server's certificate chain and host name. InsecureSkipVerify bool // CipherSuites is a list of supported cipher suites. If CipherSuites // is nil, TLS uses a list of suites supported by the implementation. CipherSuites []uint16 // sessionTicketKeys contains zero or more ticket keys. If the length // is zero, SessionTicketsDisabled must be true. The first key is used // for new tickets and any subsequent keys can be used to decrypt old // tickets. sessionTicketKeys []ticketKey }以上代码在go语言开发包的crypto/tls/common.go文件中,是go语言实现tls所需要的参数
tlsConfig, err := tlsconfig.Server(*commonFlags.TLSOptions) if err != nil { logrus.Fatal(err) } serverConfig.TLSConfig = tlsConfig以上代码是docker/docker/daemon.go中,实现的功能是将在cli/common.go中结构CommonFlags中TLSOptions即上面的Options结构体提供的参数转化为go语言中的tls包中Config结构中的参数,又将server的Config中的TLSConfig值设为新建立的。Server函数中的tlsCert包含tls.Options中的Cert和Key,一个Certificate中的Certificate[][]byte后期转化成了x509Cert.Publickey,cert.Privatekey是key转化而来的。
for _, protoAddr := range commonFlags.Hosts { protoAddrParts := strings.SplitN(protoAddr, "://", 2) if len(protoAddrParts) != 2 { logrus.Fatalf("bad format %s, expected PROTO://ADDR", protoAddr) } serverConfig.Addrs = append(serverConfig.Addrs, apiserver.Addr{Proto: protoAddrParts[0], Addr: protoAddrParts[1]}) }
api, err := apiserver.New(serverConfig)
以上代码设置serverConfig的服务器地址,然后根据serverCofig新建server服务,它为ServeAPI分配资源。
func New(cfg *Config) (*Server, error) { s := &Server{ cfg: cfg, start: make(chan struct{}), } for _, addr := range cfg.Addrs { srv, err := s.newServer(addr.Proto, addr.Addr) if err != nil { return nil, err } logrus.Debugf("Server created for HTTP on %s (%s)", addr.Proto, addr.Addr) s.servers = append(s.servers, srv...) } return s, nil }
首先,根据传入的serverConfig参数给Server赋值,Server结构如下
// Server contains instance details for the server type Server struct { cfg *Config start chan struct{} servers []*HTTPServer routers []router.Router }
赋值后,根据cfg中地址的个数启动相应数量的server,在该地址上建立监听端口,并且将该地址加入到Server结构的servers参数中。至此http server已经启动并且开始设定Listener的初始化,之后
go func() { if err := api.ServeAPI(); err != nil { logrus.Errorf("ServeAPI error: %v", err) serveAPIWait <- err return } serveAPIWait <- nil }()
这段代码回让以上地址上的server启动相应的Listener,接受服务端传来的链接,并且为每个链接创建一个协程,调用一个Handler来提供服务。这块具体怎么调用Handler的代码在哪儿,我还没有找到。
启动server服务后便是配置registry服务,调用的函数是registry.NewService(cli.registryOptions),返回的是一个registry的ServiceConfig,其中包括
// ServiceConfig stores daemon registry services configuration. type ServiceConfig struct { InsecureRegistryCIDRs []*netIPNet `json:"InsecureRegistryCIDRs"` IndexConfigs map[string]*IndexInfo `json:"IndexConfigs"` Mirrors []string }InsecureRegistryCIDRs返回registry对应的ip和mask,IndexConfigs是registry的index信息,默认NewService函数会配置Docker Hub的Index信息并且返回。
server服务启动并配置registry信息再向后,daemon会调用daemon.NewDaemon,参数是cli.Config和registryService,即docker命令行配置和registry参数,在NewDaemon函数中会首先设置daemon是否启动网络服务,设置根文件系统、tmp目录等,之后设置Graphdriver并装载该driver,ubuntu上默认的是aufs。之后设置daemonRepo的目录,是/var/lib/docker下的containers目录,其中保存的是运行容器时的配置信息。之后,建立graph目录,其中保存的是每层layer的元数据以及layersize,在之后是建立volumes目录,其中保存的是含有volume数据卷的container的所有信息,之后根据trust目录建立trustService。之后,建立repositories文件,里面保存的是机器上含有的镜像信息;然后建立linkgraph.db目录,是一个sqlite数据库数据,里面保存的是各镜像文件之间的关系。之后建立exec Driver,至此daemon的初始配置已经完成。
if err := d.restore(); err != nil { return nil, err }最后调用这个函数,启动需要默认启动的containers。