- Docker源碼分析
- 孫宏亮
- 1259字
- 2018-12-31 20:27:05
3.3.5 加載builtins
DockerDaemon設置完Trap特定信號的處理方法(即eng.shutdown()函數)之后,Docker Daemon實現了builtins的加載。Docker的builtins可以理解為:Docker Daemon運行過程中,注冊的一些任務(Job),這部分任務一般與容器的運行無關,與Docker Daemon的運行時信息有關。加載builtins的源碼實現如下:
if err := builtins.Register(eng); err != nil { log.Fatal(err) }
加載builtins完成的具體工作是:向engine注冊多個Handler,以便后續在執行相應任務時,運行指定的Handler。這些Handler包括:Docker Daemon宿主機的網絡初始化、Web API服務、事件查詢、版本查看、Docker Registry的驗證與搜索等。源碼實現位于./docker/docker/builtins/builtins.go#L16-L30,如下:
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) }
下面分析Register函數實現過程中最為主要的5個部分:daemon(eng)、remote(eng)、events.New().Install(eng)、eng.Register("version",dockerVersion)以及registry.NewService().Install(eng)。
1.注冊網絡初始化處理方法
daemon(eng)的實現過程,主要為eng對象注冊了一個鍵為"init_networkdriver"的處理方法,此處理方法的值為bridge.InitDriver函數,源碼如下:
func daemon(eng *engine.Engine) error { return eng.Register("init_networkdriver", bridge.InitDriver) }
需要注意的是,向eng對象注冊處理方法,并不代表處理方法的值函數會被立即調用執行,如注冊init_networkdrive時bridge.InitDriver并不會直接運行,而是將bridge.InitDriver的函數入口作為init_networkdriver的值,寫入eng的handlers屬性中。當Docker Daemon接收到名為init_networkdriver的Job的執行請求時,bridge.InitDriver才被Docker Daemon調用執行。
Bridge.InitDriver的具體實現位于./docker/docker/daemon/networkdriver/bridge/driver.go#79-L175,主要作用為:
□ 獲取為Docker服務的網絡設備地址。
□ 創建指定IP地址的網橋。
□ 配置網絡iptables規則。
□ 另外還為eng對象注冊了多個Handler,如allocate_interface、release_interface、allocate_port以及link等。
本書將在第6章詳細分析Docker Daemon如何初始化宿主機的網絡環境。
2.注冊API服務處理方法
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) }
注冊的兩個處理方法名稱分別為serveapi與acceptconnections,相應的執行方法分別為apiserver.ServeApi與apiserver.AcceptConnections,具體實現位于./docker/docker/api/server/server.go。其中,ServeApi執行時,通過循環多種指定協議,創建出goroutine協調來配置指定的http.Server,最終為不同協議的請求服務;而AcceptConnections的作用主要是:通知宿主機上init守護進程Docker Daemon已經啟動完畢,可以讓Docker Daemon開始服務API請求。
3.注冊events事件處理方法
events.New().Install(eng)的實現過程,為Docker注冊了多個event事件,功能是給Docker用戶提供API,使得用戶可以通過這些API查看Docker內部的events信息,log信息以及subscribers_count信息。具體的源碼位于./docker/docker/events/events.go#L29-L42,如下所示:
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 }
4.注冊版本處理方法
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.注冊registry處理方法
registry.NewService().Install(eng)的實現過程位于./docker/docker/registry/service.go,功能是:在eng對象對外暴露的API信息中添加docker registry的信息。若registry.NewService()被成功安裝,則會有兩個相應的處理方法注冊至eng,Docker Daemon通過Docker Client提供的認證信息向registry發起認證請求;search,在公有registry上搜索指定的鏡像,目前公有的registry只支持Docker Hub。
Install的具體實現如下:
func (s *Service) Install(eng *engine.Engine) error { eng.Register("auth", s.Auth) eng.Register("search", s.Search) return nil }
至此,Docker Daemon所有builtins的加載全部完成,實現了向eng對象注冊特定的處理方法。