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

要讲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.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。




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值