二期吓、Docker Client 創(chuàng)建與命令執(zhí)行(摘自《Docker源碼分析》)

1乘盖、Docker Client 的創(chuàng)建

Docker Client 的創(chuàng)建焰檩,實(shí)質(zhì)上是 Docker 用戶通過可執(zhí)行文件 docker,與 Docker Server 建立聯(lián)系的客戶端订框。

docker 源代碼運(yùn)行的流程圖如下:

docker 源代碼運(yùn)行的流程圖.jpg
1.1 Docker 命令的 flag 參數(shù)解析

例1:

Docker Server 的啟動(dòng)命令為:docker -d 或 docker --daemon=true
Docker Client 的啟動(dòng)命令為:docker --daemon=false ps 析苫、docker pull NAME等

從上述例子中可以把請(qǐng)求中的參數(shù)分為兩類

1)命令行參數(shù):docker 程序運(yùn)行時(shí)所需要提供的參數(shù),如:-d穿扳、--daemon=true衩侥、--daemon=false等,我們通趁铮可以稱之為 flag 參數(shù)
2)請(qǐng)求參數(shù):docker 發(fā)送給Docker Server 的實(shí)際請(qǐng)求參數(shù)茫死,如:ps 、pull NAME等

例2:

docker 命令為例履羞,docker –daemon=false –version=false pull Name

根據(jù)流程圖可以分析流程為:
1)解析 flag 參數(shù)之后峦萎,將 docker 請(qǐng)求參數(shù)”pull”和“Name”存放于 flag.Args();
2)創(chuàng)建好的 Docker Client 為 cli,cli 執(zhí)行 cli.Cmd(flag.Args()…);
3)在 Cmd 函數(shù)中忆首,通過 args[0] 也就是”pull”, 執(zhí)行 cli.getMethod(args[0])爱榔,獲取 method 的名稱;
4)在 getMothod 方法中糙及,通過處理最終返回 method 的值為”CmdPull”;
5)最終執(zhí)行 method(args[1:]…) 也就是 CmdPull(args[1:]…)详幽。

2、Docker 命令執(zhí)行

main 函數(shù)執(zhí)行到目前為止丁鹉,有以下內(nèi)容需要為 Docker 命令的執(zhí)行服務(wù):創(chuàng)建完畢的 Docker Client妒潭,docker 命令中的請(qǐng)求參數(shù)(經(jīng) flag 解析后存放于 flag.Arg())。也就是說揣钦,需要使用 Docker Client 來分析 docker 命令中的請(qǐng)求參數(shù)雳灾,并最終發(fā)送相應(yīng)請(qǐng)求給 Docker Server。

4.1 Docker Client 解析請(qǐng)求命令

Docker Client 解析請(qǐng)求命令的工作冯凹,在 Docker 命令執(zhí)行部分第一個(gè)完成谎亩,直接進(jìn)入 main 函數(shù)之后的源碼部分

if err := cli.Cmd(flag.Args()...); err != nil {   
  if sterr, ok := err.(*utils.StatusError); ok {     
    if sterr.Status != "" {
      log.Println(sterr.Status) 
    }     
    os.Exit(sterr.StatusCode) 
  }   
  log.Fatal(err)
}

查閱以上源碼炒嘲,可以發(fā)現(xiàn),正如之前所說匈庭,首先解析存放于 flag.Args() 中的具體請(qǐng)求參數(shù)夫凸,執(zhí)行的函數(shù)為 cli 對(duì)象的 Cmd 函數(shù)。進(jìn)入[./docker/api/client/cli.go 的 Cmd 函數(shù)]

// Cmd executes the specified command
func (cli *DockerCli) Cmd(args ...string) error {
 if len(args) > 0 {
   method, exists := cli.getMethod(args[0])
   if !exists {
     fmt.Println("Error: Command not found:", args[0])
     return cli.CmdHelp(args[1:]...)
   }
   return method(args[1:]...)
 }
 return cli.CmdHelp(args...)
}

由代碼注釋可知阱持,Cmd 函數(shù)執(zhí)行具體的指令夭拌。源碼實(shí)現(xiàn)中,首先判斷請(qǐng)求參數(shù)列表的長度是否大于 0衷咽,若不是的話鸽扁,說明沒有請(qǐng)求信息,返回 docker 命令的 Help 信息镶骗;若長度大于 0 的話桶现,說明有請(qǐng)求信息,則首先通過請(qǐng)求參數(shù)列表中的第一個(gè)元素 args[0] 來獲取具體的 method 的方法鼎姊。如果上述 method 方法不存在骡和,則返回 docker 命令的 Help 信息,若存在的話相寇,調(diào)用具體的 method 方法慰于,參數(shù)為 args[1] 及其之后所有的請(qǐng)求參數(shù)。

還是以一個(gè)具體的 docker 命令為例裆赵,docker –daemon=false –version=false pull Name东囚。通過以上的分析,可以總結(jié)出以下操作流程:

(1) 解析 flag 參數(shù)之后战授,將 docker 請(qǐng)求參數(shù)”pull”和“Name”存放于 flag.Args();
(2) 創(chuàng)建好的 Docker Client 為 cli页藻,cli 執(zhí)行 cli.Cmd(flag.Args()…);
在 Cmd 函數(shù)中,通過 args[0] 也就是”pull”, 執(zhí)行 cli.getMethod(args[0])植兰,獲取 method 的名稱份帐;
(3) 在 getMothod 方法中,通過處理最終返回 method 的值為”CmdPull”;
(4) 最終執(zhí)行 method(args[1:]…) 也就是 CmdPull(args[1:]…)楣导。

4.2 Docker Client 執(zhí)行請(qǐng)求命令

上一節(jié)通過一系列的命令解析废境,最終找到了具體的命令的執(zhí)行方法,本節(jié)內(nèi)容主要介紹 Docker Client 如何通過該執(zhí)行方法處理并發(fā)送請(qǐng)求筒繁。

由于不同的請(qǐng)求內(nèi)容不同噩凹,執(zhí)行流程大致相同,本節(jié)依舊以一個(gè)例子來闡述其中的流程毡咏,例子為:docker pull NAME驮宴。

Docker Client 在執(zhí)行以上請(qǐng)求命令的時(shí)候,會(huì)執(zhí)行 CmdPull 函數(shù)呕缭,傳入?yún)?shù)為 args[1:]…堵泽。源碼具體為./docker/api/client/command.go 中的CmdPull 函數(shù)修己。

以下逐一分析CmdPull 的源碼實(shí)現(xiàn)。

(1) 通過 cli 包中的 Subcmd 方法定義一個(gè)類型為 Flagset 的對(duì)象 cmd迎罗。

 cmd := cli.Subcmd("pull", "NAME[:TAG]", "Pull an image or a repository from the registry")

(2) 給 cmd 對(duì)象定義一個(gè)類型為 String 的 flag睬愤,名為”#t”或”#-tag”,初始值為空纹安。

 tag := cmd.String([]string{"#t", "#-tag"}, "", "Download tagged image in a repository")

(3) 將 args 參數(shù)進(jìn)行解析尤辱,解析過程中,先提取出是否有符合 tag 這個(gè) flag 的參數(shù)钻蔑,若有啥刻,將其給賦值給 tag 參數(shù)奸鸯,其余的參數(shù)存入 cmd.NArg(); 若無的話咪笑,所有的參數(shù)存入 cmd.NArg() 中。

if err := cmd.Parse(args); err != nil {
return nil }

(4) 判斷經(jīng)過 flag 解析后的參數(shù)列表娄涩,若參數(shù)列表中參數(shù)的個(gè)數(shù)不為 1窗怒,則說明需要 pull 多個(gè) image,pull 命令不支持蓄拣,則調(diào)用錯(cuò)誤處理方法 cmd.Usage()扬虚,并返回 nil。

if cmd.NArg() != 1 {
cmd.Usage()
return nil
   }

(5) 創(chuàng)建一個(gè) map 類型的變量 v球恤,該變量用于存放 pull 鏡像時(shí)所需的 url 參數(shù)辜昵;隨后將參數(shù)列表的第一個(gè)值賦給 remote 變量,并將 remote 作為鍵為 fromImage 的值添加至 v咽斧;最后若有 tag 信息的話堪置,將 tag 信息作為鍵為”tag”的值添加至 v。

var (
 v      = url.Values{}
 remote = cmd.Arg(0)
)
v.Set("fromImage", remote)
if *tag == "" {
 v.Set("tag", *tag)
}

(6) 通過 remote 變量解析出鏡像所在的 host 地址张惹,以及鏡像的名稱舀锨。

  remote, _ = parsers.ParseRepositoryTag(remote)
   // Resolve the Repository name from fqn to hostname + name
   hostname, _, err := registry.ResolveRepositoryName(remote)
   if err != nil {
     return err
   }

(7) 通過 cli 對(duì)象獲取與 Docker Server 通信所需要的認(rèn)證配置信息。

cli.LoadConfigFile()
   // Resolve the Auth config relevant for this server
   authConfig := cli.configFile.ResolveAuthConfig(hostname)

(8) 定義一個(gè)名為 pull 的函數(shù)宛逗,傳入的參數(shù)類型為 registry.AuthConfig坎匿,返回類型為 error。函數(shù)執(zhí)行塊中最主要的內(nèi)容為:cli.stream(……) 部分雷激。該部分具體發(fā)起了一個(gè)給 Docker Server 的 POST 請(qǐng)求替蔬,請(qǐng)求的 url 為"/images/create?"+v.Encode(),請(qǐng)求的認(rèn)證信息為:map[string][]string{“X-Registry-Auth”: registryAuthHeader,}屎暇。

pull := func(authConfig registry.AuthConfig) error {
     buf, err := json.Marshal(authConfig)
     if err != nil {
       return err
     }
     registryAuthHeader := []string{
       ase64.URLEncoding.EncodeToString(buf),
     }
     return cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.out, map[string][]string{     "  X-Registry-Auth": registryAuthHeader,
     })
 }

(9) 由于上一個(gè)步驟只是定義 pull 函數(shù)承桥,這一步驟具體調(diào)用執(zhí)行 pull 函數(shù),若成功則最終返回恭垦,若返回錯(cuò)誤快毛,則做相應(yīng)的錯(cuò)誤處理格嗅。若返回錯(cuò)誤為 401,則需要先登錄唠帝,轉(zhuǎn)至登錄環(huán)節(jié)屯掖,完成之后,繼續(xù)執(zhí)行 pull 函數(shù)襟衰,若完成則最終返回贴铜。

if err := pull(authConfig); err != nil {
  if strings.Contains(err.Error(), "Status 401") {
    fmt.Fprintln(cli.out, "\nPlease login prior to pull:")
    if err := cli.CmdLogin(hostname); err != nil {
      return err
    }
        authConfig := cli.configFile.ResolveAuthConfig(hostname)       return pull(authConfig)
  }
  return err
}

以上便是 pull 請(qǐng)求的全部執(zhí)行過程,其他請(qǐng)求的執(zhí)行在流程上也是大同小異瀑晒∩馨樱總之,請(qǐng)求執(zhí)行過程中苔悦,大多都是將命令行中關(guān)于請(qǐng)求的參數(shù)進(jìn)行初步處理轩褐,并添加相應(yīng)的輔助信息,最終通過指定的協(xié)議給 Docker Server 發(fā)送 Docker Client 和 Docker Server 約定好的 API 請(qǐng)求玖详。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末把介,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蟋座,更是在濱河造成了極大的恐慌拗踢,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件向臀,死亡現(xiàn)場(chǎng)離奇詭異巢墅,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)券膀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門君纫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人三娩,你說我怎么就攤上這事庵芭。” “怎么了雀监?”我有些...
    開封第一講書人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵双吆,是天一觀的道長。 經(jīng)常有香客問我会前,道長好乐,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任瓦宜,我火速辦了婚禮蔚万,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘临庇。我一直安慰自己反璃,他們只是感情好昵慌,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著淮蜈,像睡著了一般斋攀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上梧田,一...
    開封第一講書人閱讀 49,185評(píng)論 1 284
  • 那天淳蔼,我揣著相機(jī)與錄音,去河邊找鬼裁眯。 笑死鹉梨,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的穿稳。 我是一名探鬼主播存皂,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼司草!你這毒婦竟也來了艰垂?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤埋虹,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后娩怎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體搔课,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年截亦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了爬泥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡崩瓤,死狀恐怖袍啡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情却桶,我是刑警寧澤境输,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站颖系,受9級(jí)特大地震影響嗅剖,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜嘁扼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一信粮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧趁啸,春花似錦强缘、人聲如沸督惰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽姑丑。三九已至,卻和暖如春辞友,著一層夾襖步出監(jiān)牢的瞬間栅哀,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來泰國打工称龙, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留留拾,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓鲫尊,卻偏偏與公主長得像痴柔,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子疫向,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容