docker deamon源码学习

docker-deamon

brief

这篇文章主要讨论docker deamon的源码,docker deamon在docker中是较重要的一部分,对理解docker源码很重要。主要分为三部分:docker server、engine、job,三部分。架构如下所示:
http://static.scloud.letv.com/res/1b88b892-6e99-4241-b325-8f9e8b3a4b85.jpg” alt=”image” title=”” />)

启动流程

上一篇文章说过:在docker/docker.go的main函数中,line53:

    if *flDaemon {
        mainDaemon()
        return
    }

注意:在运行此函数之前,docker其实初始化了docker deamon的flag参数,代码在docker/deamon/config中,感兴趣的同学可以看看。
在看看mainDaemon()代码,docker/deamon.go:line28-line81
以后针对稍微长一点的函数,咱一块一块的粘。

Line28-38

func mainDaemon() {
    if flag.NArg() != 0 {
        flag.Usage()
        return
    }
    eng := engine.New()
    signal.Trap(eng.Shutdown)
    // Load builtins
    if err := builtins.Register(eng); err != nil {
        log.Fatal(err)
    }

以上代码主要是:
1). 看看是否传入了多余参数
2). 创建了Engine对象。Engine是docker运行的引擎,其创建各种任务管理docker运行的各种任务。

接下来看一下Engine的定义:docker/engine/engine.go,Line44-60

Engine定义

// The Engine is the core of Docker.
// It acts as a store for *containers*, and allows manipulation of these
// containers by executing *jobs*.
type Engine struct {
    handlers   map[string]Handler
    catchall   Handler
    hack       Hack // data for temporary hackery (see hack.go)
    id         string
    Stdout     io.Writer
    Stderr     io.Writer
    Stdin      io.Reader
    Logging    bool
    tasks      sync.WaitGroup
    l          sync.RWMutex // lock for shutdown
    shutdown   bool
    onShutdown []func() // shutdown handlers
}

其中Handler的定义:

type Handler func(*Job) Status

Engine初始化

再具体的看看engine.New()究竟干了啥:docker/engine/engine.go,Line75-96

// New initializes a new engine.
func New() *Engine {
    eng := &Engine{
        handlers: make(map[string]Handler),
        id:       utils.RandomString(),
        Stdout:   os.Stdout,
        Stderr:   os.Stderr,
        Stdin:    os.Stdin,
        Logging:  true,
    }
    eng.Register("commands", func(job *Job) Status {
        for _, name := range eng.commands() {
            job.Printf("%s\n", name)
        }
        return StatusOK
    })
    // Copy existing global handlers
    for k, v := range globalHandlers {
        eng.handlers[k] = v
    }
    return eng
}

从以上代码中可以看到:
1.对Engine的部分属性进行了初始化,如:handlers、id、标准输入输出、日志标志灯;2.注册了一个临时函数commands来打印查看已经注册的命令;
3.把globalHandlers中已经注册的handler添加上。
对比Engine发现还有部分属性没进行初始化,在docker deamon启动过程中其它属性也会陆续初始化。

signal trap捕获信号

signal.Trap(eng.Shutdown)是信号处理函数,实现代码在docker/signal/trap.go

// Trap sets up a simplified signal "trap", appropriate for common
// behavior expected from a vanilla unix command-line tool in general
// (and the Docker engine in particular).
//
// * If SIGINT or SIGTERM are received, `cleanup` is called, then the process is terminated.
// * If SIGINT or SIGTERM are repeated 3 times before cleanup is complete, then cleanup is
// skipped and the process terminated directly.
// * If "DEBUG" is set in the environment, SIGQUIT causes an exit without cleanup.
//
func Trap(cleanup func()) {
    c := make(chan os.Signal, 1)
    signals := []os.Signal{os.Interrupt, syscall.SIGTERM}
    if os.Getenv("DEBUG") == "" {
        signals = append(signals, syscall.SIGQUIT)
    }
    gosignal.Notify(c, signals...)
    go func() {
        interruptCount := uint32(0)
        for sig := range c {
            go func(sig os.Signal) {
                log.Printf("Received signal '%v', starting shutdown of docker...\n", sig)
                switch sig {
                case os.Interrupt, syscall.SIGTERM:
                    // If the user really wants to interrupt, let him do so.
                    if atomic.LoadUint32(&interruptCount) < 3 {
                        atomic.AddUint32(&interruptCount, 1)
                        // Initiate the cleanup only once
                        if atomic.LoadUint32(&interruptCount) == 1 {
                            // Call cleanup handler
                            cleanup()
                            os.Exit(0)
                        } else {
                            return
                        }
                    } else {
                        log.Printf("Force shutdown of docker, interrupting cleanup\n")
                    }
                case syscall.SIGQUIT:
                }
                os.Exit(128 + int(sig.(syscall.Signal)))
            }(sig)
        }
    }()
}

trap函数的目的在于处理docker管理员发给docker deamon的信号, Docker Daemon 关闭时,做一些必要的善后工作。trap的形参是cleanup,实参是eng.Shutdown。

可以看一下eng.Shutdown,在docker/engine/engine.go Line153-199。善后工作主要包括:
1.不再接受新的job;
2.等待存活的job执行完毕;
3.调用所有任务shutdown方法;
4.15秒内还有未执行完的shutdown方法,强制停止返回。

Load builtins

Docker Daemon 运行过程中,
注册的一些任务(Job),这部分任务一般与容器的运行无关,与 Docker Daemon 的运行时信
息有关。

看代码:

func Register(eng *engine.Engine) error {
    if err := daemon(eng); err != nil {
        return err
    }
    if err := remote(eng); err != nil {
        return err
    }
    if err := events.New().Install(eng); err != nil {
        return err
    }
    if err := eng.Register("version", dockerVersion); err != nil {
        return err
    }
    return registry.NewService().Install(eng)
}

下面依次看daemon(eng)、 remote(eng)、
events.New().Install(eng)、 eng.Register(“version”,dockerVersion) 以及 registry.NewService().
Install(eng)。

daemon(eng)
func daemon(eng *engine.Engine) error {
    return eng.Register("init_networkdriver", bridge.InitDriver)
}

注册网络初始化处理方法,看到Register方法如下:docker/engine/engine.go Line62-69

func (eng *Engine) Register(name string, handler Handler) error {
    _, exists := eng.handlers[name]
    if exists {
        return fmt.Errorf("Can't overwrite handler for command %s", name)
    }
    eng.handlers[name] = handler
    return nil
}

上面的代码可以看到Register只是注册了handler,并没有执行函数。只有init_networkdriver 的 Job 的执行请求时, bridge.InitDriver 才被调用执行。

remote(eng)
// remote: a RESTful api for cross-docker communication
func remote(eng *engine.Engine) error {
    if err := eng.Register("serveapi", apiserver.ServeApi); err != nil {
        return err
    }
    return eng.Register("acceptconnections", apiserver.AcceptConnections)
}

创建出apiserver.ServeApi和apiserver.AcceptConnections,这两个handler在后续学习文章中详细介绍。

events.New().Install(eng)

给Docker 用户提供 API,使得用户可以通过这些 API 查看 Docker 内部的 events 信息, log 信息以及 subscribers_count 信息。

// Install installs events public api in docker engine
func (e *Events) Install(eng *engine.Engine) error {
    // Here you should describe public interface
    jobs := map[string]engine.Handler{
        "events":            e.Get,
        "log":               e.Log,
        "subscribers_count": e.SubscribersCount,
    }
    for name, job := range jobs {
        if err := eng.Register(name, job); err != nil {
            return err
        }
    }
    return nil
}
registry.NewService().Install(eng)

注册事件在公有 registry 上搜索指定的镜像。为用户提供API

Ok,现在又回到docker deamon启动主流程,docker/deamon/deamon.go Line40-56

    // load the daemon in the background so we can immediately start
    // the http api so that connections don't fail while the daemon
    // is booting
    go func() {
        d, err := daemon.NewDaemon(daemonCfg, eng)
        if err != nil {
            log.Fatal(err)
        }
        if err := d.Install(eng); err != nil {
            log.Fatal(err)
        }
        // after the daemon is done setting up we can tell the api to start
        // accepting connections
        if err := eng.Job("acceptconnections").Run(); err != nil {
            log.Fatal(err)
        }
    }()

以上代码启动一个goroutine来启动docker server。主要包括:
1.创建Daemon类型对象d;–这部分由于内容比较多,下一篇文章将详细;
2.d.Install(eng)注册一些handler;
3.运行Job acceptconnections。

下面将具体看下acceptconnections:
eng.Job(“acceptconnections”) 的实现位于 docker/engine/engine.go Line115-L137

// Job creates a new job which can later be executed.
// This function mimics `Command` from the standard os/exec package.
func (eng *Engine) Job(name string, args ...string) *Job {
    job := &Job{
        Eng:    eng,
        Name:   name,
        Args:   args,
        Stdin:  NewInput(),
        Stdout: NewOutput(),
        Stderr: NewOutput(),
        env:    &Env{},
    }
    if eng.Logging {
        job.Stderr.Add(utils.NopWriteCloser(eng.Stderr))
    }

    // Catchall is shadowed by specific Register.
    if handler, exists := eng.handlers[name]; exists {
        job.handler = handler
    } else if eng.catchall != nil && name != "" {
        // empty job names are illegal, catchall or not.
        job.handler = eng.catchall
    }
    return job
}

在加载 builtins 时,源码 remote(eng) 已经向 eng 注册过这样一条记录,键为 acceptconnections,值为 apiserver.AcceptConnections。故job.status =apiserver.AcceptConnections(job)。
AcceptConnections 的具体实现在docker/api/server/server.go Line1370-1380:

func AcceptConnections(job *engine.Job) engine.Status {
    // Tell the init daemon we are accepting requests
    go systemd.SdNotify("READY=1")

    // close the lock so the listeners start accepting connections
    if activationLock != nil {
        close(activationLock)
    }

    return engine.StatusOK
}

Job启动大同小异,最后我们看到启动名为serveapi的任务,主要作用是为docker clinet提供api服务。
代码如下:

    // Serve api
    job := eng.Job("serveapi", flHosts...)
    job.SetenvBool("Logging", true)
    job.SetenvBool("EnableCors", *flEnableCors)
    job.Setenv("Version", dockerversion.VERSION)
    job.Setenv("SocketGroup", *flSocketGroup)

    job.SetenvBool("Tls", *flTls)
    job.SetenvBool("TlsVerify", *flTlsVerify)
    job.Setenv("TlsCa", *flCa)
    job.Setenv("TlsCert", *flCert)
    job.Setenv("TlsKey", *flKey)
    job.SetenvBool("BufferRequests", true)
    if err := job.Run(); err != nil {
        log.Fatal(err)
    }

以上代码创建了docker server,将在下一篇文章中具体讨论,docker server的创建。

路漫漫其修远兮,吾将…

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值