前言
docker是go語言編寫的星澳,要看docker源碼,最起碼要學會go的基礎語法髓迎。
了解 docker 基礎架構 以后峦朗。可以對源碼總體結構有一個大體了解排龄,然后就可以順利的進行源碼的入門解讀了甚垦。
本文主要剖析 docker-ce 的源碼。
docker-ce github:
https://github.com/docker/docker-ce
環(huán)境
操作系統(tǒng):centos7
docker版本:docker-ce: 18.09
正文
docker 源碼簡介
https://github.com/docker涣雕,這里是docker源碼的家目錄艰亮。
可以看到這里有許多許多的項目,比如:docker-engine挣郭、docker-ce迄埃、cli等,這些項目和docker的架構都有關系兑障,我安裝時就只安裝了一個侄非,為啥這里這么多?到底如何查看流译?
首先要說一點逞怨,docker這個項目的源碼挺雜。
docker 家目錄下包含:docker-engine福澡、docker-ce叠赦、cli等。
進入docker-ce可以發(fā)現(xiàn) components下也有cli 和 engine革砸。
通過簡單得對比除秀,發(fā)現(xiàn)docker-ce下得cli 和 engine與docker家目錄下得docker-engine和cli是一樣得。也就是docker-ce只是組合了docker-engine算利,cli等册踩。
還有一個概念需要提,docker項目 fork 自 moby效拭。
進入到 docker-ce 中,
可以在 components 中看到:
在我們安裝完docker時暂吉,使用 docker version 命令可以看到一個docker client和一個docker server胖秒。docker client 對應這里的cli;docker server對應這里的engine慕的。
至于那個package扒怖,可以暫時不用管它。
當我們使用 docker start <container> 业稼。一個容器是如何被啟動起來的盗痒?
閱讀源碼前,可以先猜測一下主體流程:
- 如何生成命令行供我們使用低散?
- 在調用命令行后俯邓,如何訪問到docker server 提供的 restful api?
- docker engine 一定在監(jiān)聽 restful api 對應的地址!
- docker engine 收到指令熔号,接下來如何執(zhí)行稽鞭?
- docker engine 執(zhí)行完,我們也可以收拾收拾進入下一階段的學習了引镊!
docker 源碼解讀
根據(jù)上文猜測的 啟動容器 流程朦蕴,進行源碼解讀。
兩個部分:
- 命令行:https://github.com/docker/docker-ce/tree/18.09/components/cli
- server :https://github.com/docker/docker-ce/tree/18.09/components/engine
1. 如何生成命令行供我們使用弟头?
很明顯了吩抓,這是cli組件。那么直接進cli目錄中進行翻看赴恨。(注:docker 使用 cobra 這個包去實現(xiàn)命令行功能疹娶。)
每一個功能模塊或程序都會存在一個入口函數(shù)。
根據(jù)經(jīng)驗伦连,docker.go是功能的入口雨饺,它在:https://github.com/docker/docker-ce/blob/18.09/components/cli/cmd/docker/docker.go
入口函數(shù)找到了,先放到一邊』蟠荆現(xiàn)在去找具體命令行實現(xiàn)的地方额港。
現(xiàn)在去到 https://github.com/docker/docker-ce/tree/18.09/components/cli/cli/command 這個目錄下。這個目錄下的子目錄歧焦,看著是不是稍微有點眼熟移斩?使用 docker --help 列出來的子命令,有一個算一個倚舀,分別對應這些子目錄叹哭!
排查容器啟動流程忍宋,當然去container目錄下痕貌,進入container目錄后,可以清晰的看到容器相關命令對應的文件糠排!到這里舵稠,可以看出docker cli的目錄結構和命令行的結構十分相似!
找到 start.go,主要代碼如下:
稍微去了解了一下 cobra 哺徊。根據(jù)其使用方式室琢,可以知道這里就是定義docker start 命令的地方了。上圖標注處落追,是下面第2步驟需要分析的代碼盈滴。
至此,命令定義的地方已經(jīng)找到轿钠,接下來就是分析命令如何執(zhí)行巢钓。
2. 在調用命令行后,如何訪問到docker server 提供的 restful api?
在上面那張圖里的 runSatrt 方法中可以發(fā)現(xiàn) 啟動容器指向 dockerCli.Client().ContainerStart疗垛。如下:
https://github.com/docker/docker-ce/blob/18.09/components/cli/cli/command/container/start.go
結合導包症汹,dockerCli 指向 command.Cli , 而 command.Cli 中的 Client 指向 docker/docker/client。如下:
https://github.com/docker/docker-ce/blob/18.09/components/engine/client/client.go
現(xiàn)在 client 已找到贷腕,接下來順著 client 去找 ContainerStart 背镇。
在當前client下并沒有發(fā)現(xiàn)目標方法。這也是go的一個特點泽裳,其它的文件也可以聲明成該client瞒斩。
最后在 container_start.go 發(fā)現(xiàn)了目標方法,如下:
https://github.com/docker/docker-ce/blob/18.09/components/engine/client/container_start.go
如上圖涮总,這個 ContainerStart 方法調用的是 cli.post济瓢。很明顯的,這是一個訪問restful api的封裝方法妹卿。
至此旺矾,客戶端行為基本結束。向 "/containers/<containerID>/start" 地址發(fā)送了一個post請求夺克。
接下來便是去服務端找到監(jiān)聽這個地址的方法箕宙。
3. docker engine 一定在監(jiān)聽 restful api 對應的地址!
對于一個web server铺纽。路由可以幫助我們快速找到地址對應的方法柬帕。
現(xiàn)在,進入到engine目錄下狡门,開始分析源碼陷寝。
類似于 cli 組件,engine 組件也有入口函數(shù)其馏。也叫 docker.go凤跑。這個文件了解就好,暫時不會用到叛复。
至此仔引,路由已然找到扔仓。接下來就是分析 postContainersStart 這個方法干了什么。
4. docker engine 收到指令咖耘,接下來如何執(zhí)行翘簇?
postContainersStart 方法位于:
https://github.com/docker/docker-ce/blob/18.09/components/engine/api/server/router/container/container_routes.go
在當前方法中,可以看到引用了 backend.ContainerStart 儿倒。
在 https://github.com/docker/docker-ce/blob/18.09/components/engine/api/server/router/container/backend.go 中可以找到 ContainerStart 的定義版保。具體由daemon實現(xiàn)。
daemon位于:https://github.com/docker/docker-ce/blob/18.09/components/engine/daemon夫否。
其中 start.go 就是我們需要找的目標文件找筝,如下:
https://github.com/docker/docker-ce/blob/18.09/components/engine/daemon/start.go
當前方法指向:daemon.containerStart ,該方法就在當前文件中慷吊。
到這里應該已經(jīng)差不多了袖裕,我們不是要從零開始寫一個docker。
擴展
總結:
- 命令行或者sdk均是訪問 docker server 的restful api溉瓶。
- docker server 源碼總體層次:【api】--【daemon】急鳄。api 定義路由,daemon實現(xiàn)路由中的具體方法堰酿。
- docker-ce 中的cli和engine略有交叉疾宏,對于常見 c/s 架構,這種代碼交叉的情況比較少触创。一般 client 為單獨部署的方式坎藐,而docker 并沒有將cli獨立出來,可能是由于設計初衷就不是集群模式哼绑。