docker1.9源码分析(三):daemon启动过程

来源:互联网 发布:德约科维奇出轨知乎 编辑:程序博客网 时间:2024/05/29 18:33

要讲daemon启动过程,首先得列出DaemonCli结构,即daemon命令行结构,与前面相同,只列出后面用到的成员变量:

// DaemonCli represents the daemon CLI.type DaemonCli struct {   *daemon.Config   registryOptions *registry.Options}
其中*daemon.Config如下:

// 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.OptionscommonFlags.TLSOptions = &tlsOptionscmd.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 servertype 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。




0 0