<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                [TOC=3] ## 1 前言 Docker誕生以來,便引領了輕量級虛擬化容器領域的技術熱潮。在這一潮流下,Google、IBM、Redhat等業界翹楚紛紛加入Docker陣營。雖然目前Docker仍然主要基于Linux平臺,但是Microsoft卻多次宣布對Docker的支持,從先前宣布的Azure支持Docker與Kubernetes,到如今宣布的下一代Windows Server原生態支持Docker。Microsoft的這一系列舉措多少喻示著向Linux世界的妥協,當然這也不得不讓世人對Docker的巨大影響力有重新的認識。 Docker的影響力不言而喻,但如果需要深入學習Docker的內部實現,筆者認為最重要的是理解Docker Daemon。在Docker架構中,Docker Client通過特定的協議與Docker Daemon進行通信,而Docker Daemon主要承載了Docker運行過程中的大部分工作。本文即為《Docker源碼分析》系列的第三篇-——Docker Daemon篇。 ## 2 Docker Daemon簡介 Docker Daemon是Docker架構中運行在后臺的守護進程,大致可以分為Docker Server、Engine和Job三部分。Docker Daemon可以認為是通過Docker Server模塊接受Docker Client的請求,并在Engine中處理請求,然后根據請求類型,創建出指定的Job并運行,運行過程的作用有以下幾種可能:向Docker Registry獲取鏡像,通過graphdriver執行容器鏡像的本地化操作,通過networkdriver執行容器網絡環境的配置,通過execdriver執行容器內部運行的執行工作等。 以下為Docker Daemon的架構示意圖: ![](https://box.kancloud.cn/2015-11-12_56443b09324ff.jpg) ## 3 Docker Daemon源碼分析內容安排 本文從源碼的角度,主要分析Docker Daemon的啟動流程。由于Docker Daemon和Docker Client的啟動流程有很大的相似之處,故在介紹啟動流程之后,本文著重分析啟動流程中最為重要的環節:創建daemon過程中mainDaemon()的實現。 ## 4 Docker Daemon的啟動流程 由于Docker Daemon和Docker Client的啟動都是通過可執行文件docker來完成的,因此兩者的啟動流程非常相似。Docker可執行文件運行時,運行代碼通過不同的命令行flag參數,區分兩者,并最終運行兩者各自相應的部分。 啟動Docker Daemon時,一般可以使用以下命令:docker --daemon=true; docker –d; docker –d=true等。接著由docker的main()函數來解析以上命令的相應flag參數,并最終完成Docker Daemon的啟動。 首先,附上Docker Daemon的啟動流程圖: ![](https://box.kancloud.cn/2015-11-12_56443b094d3cd.jpg) 由于《Docker源碼分析》系列之[Docker Client篇](http://www.infoq.com/cn/articles/docker-source-code-analysis-part2)中,已經涉及了關于Docker中main()函數運行的很多前續工作(可參見[Docker Client篇](http://www.infoq.com/cn/articles/docker-source-code-analysis-part2)),并且Docker Daemon的啟動也會涉及這些工作,故本文略去相同部分,而主要針對后續僅和Docker Daemon相關的內容進行深入分析,即mainDaemon()的具體源碼實現。 ## 5 mainDaemon( )的具體實現 通過Docker Daemon的流程圖,可以得出一個這樣的結論:有關Docker Daemon的所有的工作,都被包含在mainDaemon()方法的實現中。 宏觀來講,mainDaemon()完成創建一個daemon進程,并使其正常運行。 從功能的角度來說,mainDaemon()實現了兩部分內容:第一,創建Docker運行環境;第二,服務于Docker Client,接收并處理相應請求。 從實現細節來講,[mainDaemon()的實現過程](https://github.com/docker/docker/blob/v1.2.0/docker/daemon.go#L28)主要包含以下步驟: * daemon的配置初始化(這部分在init()函數中實現,即在mainDaemon()運行前就執行,但由于這部分內容和mainDaemon()的運行息息相關,故可認為是mainDaemon()運行的先決條件); * 命令行flag參數檢查; * 創建engine對象; * 設置engine的信號捕獲及處理方法; * 加載builtins; * 使用goroutine加載daemon對象并運行; * 打印Docker版本及驅動信息; * Job之”serveapi”的創建與運行。 下文將一一深入分析以上步驟。 ### 5.0 配置初始化 在mainDaemon()運行之前,關于Docker Daemon所需要的config配置信息均已經初始化完畢。具體實現如下,位于[./docker/docker/daemon.go](https://github.com/docker/docker/blob/master/docker/daemon.go#L21): ~~~ var ( daemonCfg = &daemon.Config{} ) func init() { daemonCfg.InstallFlags() } ~~~ 首先,聲明一個為daemon包中Config類型的變量,名為daemonCfg。而Config對象,定義了Docker Daemon所需的配置信息。在Docker Daemon在啟動時,daemonCfg變量被傳遞至Docker Daemon并被使用。 Config對象的定義如下(含部分屬性的解釋),位于[./docker/daemon/config.go](https://github.com/docker/docker/blob/v1.2.0/daemon/config.go#L20): ~~~ type Config struct { Pidfile string //Docker Daemon所屬進程的PID文件 Root string //Docker運行時所使用的root路徑 AutoRestart bool //已被啟用,轉而支持docker run時的重啟 Dns []string //Docker使用的DNS Server地址 DnsSearch []string //Docker使用的指定的DNS查找域名 Mirrors []string //指定的優先Docker Registry鏡像 EnableIptables bool //啟用Docker的iptables功能 EnableIpForward bool //啟用net.ipv4.ip_forward功能 EnableIpMasq bool //啟用IP偽裝技術 DefaultIp net.IP //綁定容器端口時使用的默認IP BridgeIface string //添加容器網絡至已有的網橋 BridgeIP string //創建網橋的IP地址 FixedCIDR string //指定IP的IPv4子網,必須被網橋子網包含 InterContainerCommunication bool //是否允許相同host上容器間的通信 GraphDriver string //Docker運行時使用的特定存儲驅動 GraphOptions []string //可設置的存儲驅動選項 ExecDriver string // Docker運行時使用的特定exec驅動 Mtu int //設置容器網絡的MTU DisableNetwork bool //有定義,之后未初始化 EnableSelinuxSupport bool //啟用SELinux功能的支持 Context map[string][]string //有定義,之后未初始化 } ~~~ 已經有聲明的daemonCfg之后,init()函數實現了daemonCfg變量中各屬性的賦值,具體的實現為:daemonCfg.InstallFlags(),位于[./docker/daemon/config.go](https://github.com/docker/docker/blob/v1.2.0/daemon/config.go#L45),代碼如下: ~~~ func (config *Config) InstallFlags() { flag.StringVar(&config.Pidfile, []string{"p", "-pidfile"}, "/var/run/docker.pid", "Path to use for daemon PID file") flag.StringVar(&config.Root, []string{"g", "-graph"}, "/var/lib/docker", "Path to use as the root of the Docker runtime") …… opts.IPVar(&config.DefaultIp, []string{"#ip", "-ip"}, "0.0.0.0", "Default IP address to use when binding container ports") opts.ListVar(&config.GraphOptions, []string{"-storage-opt"}, "Set storage driver options") …… } ~~~ 在InstallFlags()函數的實現過程中,主要是定義某種類型的flag參數,并將該參數的值綁定在config變量的指定屬性上,如: flag.StringVar(&config.Pidfile, []string{"p", "-pidfile"}, " /var/run/docker.pid", "Path to use for daemon PID file") 以上語句的含義為: * 定義一個為String類型的flag參數; * 該flag的名稱為”p”或者”-pidfile”; * 該flag的值為” /var/run/docker.pid”,并將該值綁定在變量config.Pidfile上; * 該flag的描述信息為"Path to use for daemon PID file"。 至此,關于Docker Daemon所需要的配置信息均聲明并初始化完畢。 ### 5.1 flag參數檢查 從這一節開始,真正進入Docker Daemon的mainDaemon()運行分析。 第一個步驟即flag參數的檢查。具體而言,即當docker命令經過flag參數解析之后,判斷剩余的參數是否為0。若為0,則說明Docker Daemon的啟動命令無誤,正常運行;若不為0,則說明在啟動Docker Daemon的時候,傳入了多余的參數,此時會輸出錯誤提示,并退出運行程序。具體代碼如下: ~~~ if flag.NArg() != 0 { flag.Usage() return } ~~~ ### 5.2 創建engine對象 在mainDaemon()運行過程中,flag參數檢查完畢之后,隨即創建engine對象,代碼如下: ~~~ eng := engine.New() ~~~ Engine是Docker架構中的運行引擎,同時也是Docker運行的核心模塊。Engine扮演著Docker container存儲倉庫的角色,并且通過job的形式來管理這些容器。 在[./docker/engine/engine.go](https://github.com/docker/docker/blob/v1.2.0/engine/engine.go#L47)中,Engine結構體的定義如下: ~~~ 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 } ~~~ 其中,Engine結構體中最為重要的即為handlers屬性。該handlers屬性為map類型,key為string類型,value為Handler類型。其中Handler類型的定義如下: ~~~ type Handler func(*Job) Status ~~~ 可見,Handler為一個定義的函數。該函數傳入的參數為Job指針,返回為Status狀態。 介紹完Engine以及Handler,現在真正進入New()函數的實現中: ~~~ 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 } ~~~ 分析以上代碼,可以知道New()函數最終返回一個Engine對象。而在代碼實現部分,第一個工作即為創建一個Engine結構體實例eng;第二個工作是向eng對象注冊名為commands的Handler,其中Handler為臨時定義的函數func(job *Job) Status{ } , 該函數的作用是通過job來打印所有已經注冊完畢的command名稱,最終返回狀態StatusOK;第三個工作是:將已定義的變量globalHandlers中的所有的Handler,都復制到eng對象的handlers屬性中。最后成功返回eng對象。 ### 5.3 設置engine的信號捕獲 回到mainDaemon()函數的運行中,執行后續代碼: ~~~ signal.Trap(eng.Shutdown) ~~~ 該部分代碼的作用是:在Docker Daemon的運行中,設置Trap特定信號的處理方法,特定信號有SIGINT,SIGTERM以及SIGQUIT;當程序捕獲到SIGINT或者SIGTERM信號時,執行相應的善后操作,最后保證Docker Daemon程序退出。 該部分的代碼的實現位于[./docker/pkg/signal/trap.go](https://github.com/docker/docker/blob/v1.2.0/pkg/signal/trap.go#L20)。實現的流程分為以下4個步驟: * 創建并設置一個channel,用于發送信號通知; * 定義signals數組變量,初始值為os.SIGINT, os.SIGTERM;若環境變量DEBUG為空的話,則添加os.SIGQUIT至signals數組; * 通過gosignal.Notify(c, signals...)中Notify函數來實現將接收到的signal信號傳遞給c。需要注意的是只有signals中被羅列出的信號才會被傳遞給c,其余信號會被直接忽略; * 創建一個goroutine來處理具體的signal信號,當信號類型為os.Interrupt或者syscall.SIGTERM時,執行傳入Trap函數的具體執行方法,形參為cleanup(),實參為eng.Shutdown。 Shutdown()函數的定義位于[./docker/engine/engine.go](https://github.com/docker/docker/blob/v1.2.0/engine/engine.go#L153),主要做的工作是為Docker Daemon的關閉做一些善后工作。 善后工作如下: * Docker Daemon不再接收任何新的Job; * Docker Daemon等待所有存活的Job執行完畢; * Docker Daemon調用所有shutdown的處理方法; * 當所有的handler執行完畢,或者15秒之后,Shutdown()函數返回。 由于在signal.Trap( eng.Shutdown )函數的具體實現中執行eng.Shutdown,在執行完eng.Shutdown之后,隨即執行[os.Exit(0)](https://github.com/docker/docker/blob/v1.2.0/pkg/signal/trap.go#L41),完成當前程序的立即退出。 ### 5.4 加載builtins 為eng設置完Trap特定信號的處理方法之后,Docker Daemon實現了builtins的加載。代碼實現如下: ~~~ if err := builtins.Register(eng); err != nil { log.Fatal(err) } ~~~ 加載builtins的主要工作是為:為engine注冊多個Handler,以便后續在執行相應任務時,運行指定的Handler。這些Handler包括:網絡初始化、web API服務、事件查詢、版本查看、Docker Registry驗證與搜索。代碼實現位于[./docker/builtins/builtins.go](https://github.com/docker/docker/blob/v1.2.0/builtins/builtins.go#L16),如下: ~~~ 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) } ~~~ 以下分析實現過程中最為主要的5個部分:daemon(eng)、remote(eng)、events.New().Install(eng)、eng.Register(“version”,dockerVersion)以及registry.NewService().Install(eng)。 #### 5.4.1 注冊初始化網絡驅動的Handler daemon(eng)的實現過程,主要為eng對象注冊了一個key為”init_networkdriver”的Handler,該Handler的值為bridge.InitDriver函數,代碼如下: ~~~ func daemon(eng *engine.Engine) error { return eng.Register("init_networkdriver", bridge.InitDriver) } ~~~ 需要注意的是,向eng對象注冊Handler,并不代表Handler的值函數會被直接運行,如bridge.InitDriver,并不會直接運行,而是將bridge.InitDriver的函數入口,寫入eng的handlers屬性中。 Bridge.InitDriver的具體實現位于[./docker/daemon/networkdriver/bridge/driver.go](https://github.com/docker/docker/blob/v1.2.0/daemon/networkdriver/bridge/driver.go#L79) ,主要作用為: * 獲取為Docker服務的網絡設備的地址; * 創建指定IP地址的網橋; * 配置網絡iptables規則; * 另外還為eng對象注冊了多個Handler,如 ”allocate_interface”, ”release_interface”, ”allocate_port”,”link”。 #### 5.4.2 注冊API服務的Handler remote(eng)的實現過程,主要為eng對象注冊了兩個Handler,分別為”serveapi”與”acceptconnections”。代碼實現如下: ~~~ func remote(eng *engine.Engine) error { if err := eng.Register("serveapi", apiserver.ServeApi); err != nil { return err } return eng.Register("acceptconnections", apiserver.AcceptConnections) } ~~~ 注冊的兩個Handler名稱分別為”serveapi”與”acceptconnections”,相應的執行方法分別為apiserver.ServeApi與apiserver.AcceptConnections,具體實現位于[./docker/api/server/server.go](https://github.com/docker/docker/blob/v1.2.0/api/server/server.go)。其中,ServeApi執行時,通過循環多種協議,創建出goroutine來配置指定的http.Server,最終為不同的協議請求服務;而AcceptConnections的實現主要是為了通知init守護進程,Docker Daemon已經啟動完畢,可以讓Docker Daemon進程接受請求。 #### 5.4.3 注冊events事件的Handler events.New().Install(eng)的實現過程,為Docker注冊了多個event事件,功能是給Docker用戶提供API,使得用戶可以通過這些API查看Docker內部的events信息,log信息以及subscribers_count信息。具體的代碼位于[./docker/events/events.go](https://github.com/docker/docker/blob/v1.2.0/events/events.go#L29),如下: ~~~ func (e *Events) Install(eng *engine.Engine) error { 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 } ~~~ #### 5.4.4 注冊版本的Handler eng.Register(“version”,dockerVersion)的實現過程,向eng對象注冊key為”version”,value為”dockerVersion”執行方法的Handler,dockerVersion的執行過程中,會向名為version的job的標準輸出中寫入Docker的版本,Docker API的版本,git版本,Go語言運行時版本以及操作系統等版本信息。dockerVersion的具體實現如下: ~~~ func dockerVersion(job *engine.Job) engine.Status { v := &engine.Env{} v.SetJson("Version", dockerversion.VERSION) v.SetJson("ApiVersion", api.APIVERSION) v.Set("GitCommit", dockerversion.GITCOMMIT) v.Set("GoVersion", runtime.Version()) v.Set("Os", runtime.GOOS) v.Set("Arch", runtime.GOARCH) if kernelVersion, err := kernel.GetKernelVersion(); err == nil { v.Set("KernelVersion", kernelVersion.String()) } if _, err := v.WriteTo(job.Stdout); err != nil { return job.Error(err) } return engine.StatusOK } ~~~ #### 5.4.5 注冊registry的Handler registry.NewService().Install(eng)的實現過程位于[./docker/registry/service.go](https://github.com/docker/docker/blob/v1.2.0/registry/service.go#L25),在eng對象對外暴露的API信息中添加docker registry的信息。當registry.NewService()成功被Install安裝完畢的話,則有兩個調用能夠被eng使用:”auth”,向公有registry進行認證;”search”,在公有registry上搜索指定的鏡像。 Install的具體實現如下: ~~~ func (s *Service) Install(eng *engine.Engine) error { eng.Register("auth", s.Auth) eng.Register("search", s.Search) return nil } ~~~ 至此,所有builtins的加載全部完成,實現了向eng對象注冊特定的Handler。 ### 5.5 使用goroutine加載daemon對象并運行 執行完builtins的加載,回到mainDaemon()的執行,通過一個goroutine來加載daemon對象并開始運行。這一環節的執行,主要包含三個步驟: * 通過init函數中初始化的daemonCfg與eng對象來創建一個daemon對象d; * 通過daemon對象的Install函數,向eng對象中注冊眾多的Handler; * 在Docker Daemon啟動完畢之后,運行名為”acceptconnections”的job,主要工作為向init守護進程發送”READY=1”信號,以便開始正常接受請求。 代碼實現如下: ~~~ go func() { d, err := daemon.MainDaemon(daemonCfg, eng) if err != nil { log.Fatal(err) } if err := d.Install(eng); err != nil { log.Fatal(err) } if err := eng.Job("acceptconnections").Run(); err != nil { log.Fatal(err) } }() ~~~ 以下分別分析三個步驟所做的工作。 ### 5.5.1 創建daemon對象 daemon.MainDaemon(daemonCfg, eng)是創建daemon對象d的核心部分。主要作用為初始化Docker Daemon的基本環境,如處理config參數,驗證系統支持度,配置Docker工作目錄,設置與加載多種driver,創建graph環境等,驗證DNS配置等。 由于daemon.MainDaemon(daemonCfg, eng)是加載Docker Daemon的核心部分,且篇幅過長,故安排《Docker源碼分析》系列的第四篇專文分析這部分。 #### 5.5.2 通過daemon對象為engine注冊Handler 當創建完daemon對象,goroutine執行d.Install(eng),具體實現位于[./docker/daemon/daemon.go](https://github.com/docker/docker/blob/v1.2.0/daemon/daemon.go#L100): ~~~ func (daemon *Daemon) Install(eng *engine.Engine) error { for name, method := range map[string]engine.Handler{ "attach": daemon.ContainerAttach, …… "image_delete": daemon.ImageDelete, } { if err := eng.Register(name, method); err != nil { return err } } if err := daemon.Repositories().Install(eng); err != nil { return err } eng.Hack_SetGlobalVar("httpapi.daemon", daemon) return nil } ~~~ 以上代碼的實現分為三部分: * 向eng對象中注冊眾多的Handler對象; * daemon.Repositories().Install(eng)實現了向eng對象注冊多個與image相關的Handler,Install的實現位于[./docker/graph/service.go](https://github.com/docker/docker/blob/v1.2.0/graph/service.go#L12); * eng.Hack_SetGlobalVar("httpapi.daemon", daemon)實現向eng對象中map類型的hack對象中添加一條記錄,key為”httpapi.daemon”,value為daemon。 #### 5.5.3 運行acceptconnections的job 在goroutine內部最后運行名為”acceptconnections”的job,主要作用是通知init守護進程,Docker Daemon可以開始接受請求了。 這是源碼分析系列中第一次涉及具體Job的運行,以下簡單分析”acceptconnections”這個job的運行。 可以看到首先執行eng.Job("acceptconnections"),返回一個Job,隨后再執行eng.Job("acceptconnections").Run(),也就是該執行Job的run函數。 eng.Job(“acceptconnections”)的實現位于[./docker/engine/engine.go](https://github.com/docker/docker/blob/v1.2.0/engine/engine.go#L115),如下: ~~~ 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)) } if handler, exists := eng.handlers[name]; exists { job.handler = handler } else if eng.catchall != nil && name != "" { job.handler = eng.catchall } return job } ~~~ 由以上代碼可知,首先創建一個類型為Job的job對象,該對象中Eng屬性為函數的調用者eng,Name屬性為”acceptconnections”,沒有參數傳入。另外在eng對象所有的handlers屬性中尋找鍵為”acceptconnections”記錄的值,由于在加載builtins操作中的remote(eng)中已經向eng注冊過這樣的一條記錄,key為”acceptconnections”,value為apiserver.AcceptConnections。因此job對象的handler為apiserver.AcceptConnections。最后返回已經初始化完畢的對象job。 創建完job對象之后,隨即執行該job對象的run()函數。Run()函數的實現位于[./docker/engine/job.go](https://github.com/docker/docker/blob/v1.2.0/engine/job.go#L48),該函數執行指定的job,并在job執行完成前一直阻塞。對于名為”acceptconnections”的job對象,運行代碼為[job.status = job.handler(job)](https://github.com/docker/docker/blob/v1.2.0/engine/job.go#L79),由于job.handler值為apiserver.AcceptConnections,故真正執行的是job.status = apiserver.AcceptConnections(job)。 進入AcceptConnections的具體實現,位于[./docker/api/server/server.go](https://github.com/docker/docker/blob/v1.2.0/api/server/server.go#L1370),如下: ~~~ func AcceptConnections(job *engine.Job) engine.Status { // Tell the init daemon we are accepting requests go systemd.SdNotify("READY=1") if activationLock != nil { close(activationLock) } return engine.StatusOK } ~~~ 重點為go systemd.SdNotify("READY=1")的實現,位于[./docker/pkg/system/sd_notify.go](https://github.com/docker/docker/blob/v1.2.0/pkg/systemd/sd_notify.go#L12),主要作用是通知init守護進程Docker Daemon的啟動已經全部完成,潛在的功能是使得Docker Daemon開始接受Docker Client發送來的API請求。 至此,已經完成通過goroutine來加載daemon對象并運行。 ### 5.6 打印Docker版本及驅動信息 回到mainDaemon()的運行流程中,在goroutine的執行之時,mainDaemon()函數內部其它代碼也會并發執行。 第一個執行的即為顯示docker的版本信息,以及ExecDriver和GraphDriver這兩個驅動的具體信息,代碼如下: ~~~ log.Printf("docker daemon: %s %s; execdriver: %s; graphdriver: %s", dockerversion.VERSION, dockerversion.GITCOMMIT, daemonCfg.ExecDriver, daemonCfg.GraphDriver, ) ~~~ ### 5.7 Job之serveapi的創建與運行 打印部分Docker具體信息之后,Docker Daemon立即創建并運行名為”serveapi”的job,主要作用為讓Docker Daemon提供API訪問服務。實現代碼位于[./docker/docker/daemon.go#L66](https://github.com/docker/docker/blob/v1.2.0/docker/daemon.go#L66),如下: ~~~ 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) } ~~~ 實現過程中,首先創建一個名為”serveapi”的job,并將flHosts的值賦給job.Args。flHost的作用主要是為Docker Daemon提供使用的協議與監聽的地址。隨后,Docker Daemon為該job設置了眾多的環境變量,如安全傳輸層協議的環境變量等。最后通過job.Run()運行該serveapi的job。 由于在eng中key為”serveapi”的handler,value為apiserver.ServeApi,故該job運行時,執行apiserver.ServeApi函數,位于[./docker/api/server/server.go](https://github.com/docker/docker/blob/v1.2.0/api/server/server.go#L1339)。ServeApi函數的作用主要是對于用戶定義的所有支持協議,Docker Daemon均創建一個goroutine來啟動相應的http.Server,分別為不同的協議服務。 由于創建并啟動http.Server為Docker架構中有關Docker Server的重要內容,《Docker源碼分析》系列會在第五篇專文進行分析。 至此,可以認為Docker Daemon已經完成了serveapi這個job的初始化工作。一旦acceptconnections這個job運行完畢,則會通知init進程Docker Daemon啟動完畢,可以開始提供API服務。 ## 6 總結 本文從源碼的角度分析了Docker Daemon的啟動,著重分析了mainDaemon()的實現。 Docker Daemon作為Docker架構中的主干部分,負責了Docker內部幾乎所有操作的管理。學習Docker Daemon的具體實現,可以對Docker架構有一個較為全面的認識。總結而言,Docker的運行,載體為daemon,調度管理由engine,任務執行靠job。 ## 7 作者簡介 孫宏亮,[DaoCloud](http://www.daocloud.io/)初創團隊成員,軟件工程師,浙江大學VLIS實驗室應屆研究生。讀研期間活躍在PaaS和Docker開源社區,對Cloud Foundry有深入研究和豐富實踐,擅長底層平臺代碼分析,對分布式平臺的架構有一定經驗,撰寫了大量有深度的技術博客。2014年末以合伙人身份加入DaoCloud團隊,致力于傳播以Docker為主的容器的技術,推動互聯網應用的容器化步伐。郵箱:[allen.sun@daocloud.io](mailto:allen.sun@daocloud.io) ## 8 參考文獻 1. [http://www.infoq.com/cn/news/2014/10/windows-server-docker](http://www.infoq.com/cn/news/2014/10/windows-server-docker) 2. [http://www.freedesktop.org/software/systemd/man/sd_notify.html](http://www.freedesktop.org/software/systemd/man/sd_notify.html) 3. [http://www.ibm.com/developerworks/cn/linux/1407_liuming_init3/index.html](http://www.ibm.com/developerworks/cn/linux/1407_liuming_init3/index.html) 4. [http://docs.studygolang.com/pkg/os/](http://docs.studygolang.com/pkg/os/) 5. [https://docs.docker.com/reference/commandline/cli/](https://docs.docker.com/reference/commandline/cli/)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看