docker client和daemom

client 模式算法

  docker命令對應的源文件是docker/docker.go,docker

docker [options] command [arg...]

  其中options參數爲flag,任什麼時候候執行一個命令docker命令都須要先解析flag,而後按照用戶生命的command向指定的子命令執行對應的操做數據庫

       若是子命令爲daemom,docker都會建立一個運行在宿主機上的daemom進程,即執行daemom模式。其他子命令都會執行client模式。處於client模式命令工做流程包含幾個步驟json

 1.解析flag信息api

      docker命令支持大量的option,或者說flag,列出對於client模式下的docker比較重要的一些flag數組

      Debug,對應-D和--debug參數,他將向系統中添加DEBUG環境變量且賦值爲1,並把日誌顯示級別調爲DEBUG級,這個flag用於啓動調試模式安全

      LogLevel,對應-l和--log-level 參數。默認等級爲info,即只輸出普通的操做信息。用戶能夠指定的日誌等級如今有panic、fatal、error、warn、info、DEBUG這幾種bash

      hosts,對應-h和--hosts=[]參數,對於client模式,就是指本次操做須要鏈接的docker daemom位置,而對於daemom模式,則提供所要監聽的地址,若host變量或者系統環境變量DOCKER_HOST不爲空,說明用戶指定了host對象;不然使用默認設置,默認狀況下Linux系統設置爲unix:///var/run/docker.sock服務器

      protAddrParts,這個參數來自-H參數中://先後的兩部分的組合,即與docker daemom創建通訊的協議方式與socke地址網絡

 2建立client實例

      client的建立就是在已有配置參數信息的基礎上,調用api/client/cli.go#NewDockerCli,須要設置好proto(傳輸協議)、addr(host的目標地址)和tlsConfig(安全傳輸層協議的配置),另外還會配置標準輸入輸出及錯誤輸出

  3執行具體的命令

     Docker client 對象建立成功後,剩下的執行具體命令的過程就交給cli/cli.go來處理

   從命令到映射的方法

    cli主要經過反射機制,從用戶輸入的命令(如run)獲得匹配的執行方法(CmdRun),這就是所謂「約定大於配置」的方法命名規範。

    同時,cli會根據參數列表的長度判斷是否用於多級docker命令支持,而後根據找到的執行方法,把剩下的參數傳入並執行。若參數傳入的方法不正確或者錯誤,則返回docker的幫助並退出

    每個相似api/client/commnds.go#CmdRun 的方法都剝離出來做爲一個單獨的文件存在。docker run 這個命令的執行過程,就須要尋找api/client/run.go這個文件

    執行對應的方法,發起請求

    1.解析傳入的參數,並針對參數進行配置處理

    2.獲取與Docker daemon通訊所須要的認證配置信息

    3.根據命令業務類型,給Docker daemon發送POST、GET等請求

    4.讀取來自Docker daemon

 daemom  模式

   一旦進入daemom模式,剩下的初始化工做都由docker的docker/daemon.go#CmdDaemon來完成;docker daemon經過一個server模塊(api/server/server.go)接收來自client的請求,而後根據請求的類型,交由具體方法執行,所以daemom首先要啓動並初始化這個server,另外一方面啓動server後。docker 進程須要初始化一個daemon對象(daemon/daemon.go)來負責處理server的請求。

 docker daemon 初始化啓動過程

API server的配置和初始化過程

啓動過程

(1)整理解析用戶指定的各項參數

(2)建立PID文件

(3)加載所需的server輔助配置,包括日誌、是否容許遠程訪問、版本以及TLS認證信等。

(4)根據上述server配置,加上以前解析出來的用戶指定的server配置(好比Host),經過goro-utine的方式啓動API server。這個server監聽的socket位置就是Host的值

(5)建立一個負責處理業務的daemon對象(對應daemon/daemon.go)做爲負責處理用戶請求的邏輯實體

(6)對APIserver中的路由表進行初始化,即將用戶的請求和對應的處理函數相對應起來。

(7)設置一個channel,保證上述goroutine只是在server出錯的狀況纔會退出

(8)設置信號捕獲,docker daemon進程收到INT、TERM、QUIT信號時,關閉API server,用shutdowndaemon中止這個daemon

(9)若是上面流程完成後,API server就會與daemon綁定,並接受client的鏈接。

(10)最後,docker daemon進程向宿主機的init守護進程發送「READY=1」信號,表示docker daemon已經開始工做

  關閉過程

(1)建立並設置一個channel,使用select監聽數據。在正確完成關閉daemon工做後將channel關閉,標識該工做的完成;不然在超時15秒後報錯

(2)調用daemon/daemon.go#Shoutdown方法執行以下工做

   遍歷全部運行中的容器,先用SIGTERM軟殺死容器進程,若是10秒不能完成,則使用SIGKILL強制殺死

    若是netController被初始化過,調用#libnetwork/controler.go#GC 方法進行垃圾回收

   結束運行中的鏡像驅動程序

    在docker1.6版本之前的早期和之前全部版本,server的啓動和初始化使用了一種複雜的job機制(API server即被看做一種job),而且依賴於一個專門的docker Engine來管理和運行這些job。1.7版本,這個設計在整個社區的推進下唄重構,上述說的是新的server初始化過程,該server會經過與daemon對象綁定來接受並處理完成具體的請求(相似於一個API接受器綁定了一個業務邏輯處理器)

   daemon對象的建立與初始化

     對象建立過程至少包括功能有:docker容器配置信息、檢測系統支持及用戶權限、配置工做路徑、加載並配置graphdriver、建立docker網絡環境、建立並初始化鏡像數據庫、建立容器管理驅動、檢測DNS配置和加載已有Docker容器等。

    docker  容器配置信息

     容器配置信息的主要功能有:提供用戶自由配置的docker容器的可選功能,使得docker容器運行更貼近用戶期待的運行場景;設置默認的網絡最大傳輸單元:當用戶沒有對-mut參數進行指定是,將其設置爲1500.不然,沿用用戶指定參數值  ;檢測網橋配置信息:此部分配置爲進一步配置docker網絡提供鋪墊

    檢測系統支持及用戶權限

     初步處理完docker的配置信息後,docker自身運行的環境進行一系列檢測,主要包括3個方面

   * 操做系統類型對docker daemon的支持,目前docker daemon只能運行在Linux上

   * 用戶權限的級別,必須是root權限

   * 內核版本與處理器支持,只支持amd64架構的處理,且內核版本必須升至3.10.0及以是上。

   配置daemon工做路徑

   配置docker daemon的工做路徑,主要是建立Docker  daemon 運行中所在的工做目錄,默認爲/var/lib/docker.若該目錄不存在,則會建立,並賦予0700權限

   配置docker容器所需的文件環境

      這一步docker daemon會在docker工做目錄/var/lib/docker 下面初始化一些重要的目錄文件,來構建docker容器工做所需的文件系統環境

      這一,建立容器配置文件目錄。docker daemon在出建立docker容器以後,須要將容器內的配置文件放到這個目錄下統一管理。目錄默認位置:/var/lib/docker/containers,它下面會爲每一個具體容器保存以下幾個配置文件,其中xxx爲容器ID 

[root@mast ~]# ls /var/lib/docker/containers/4d5464672680c97ed061b73e7d8336741b2971c2fb5a81fa5ac2ec8fac096cf9/
4d5464672680c97ed061b73e7d8336741b2971c2fb5a81fa5ac2ec8fac096cf9-json.log  checkpoints  config.v2.json  hostconfig.json  hostname  hosts  mounts  resolv.conf  resolv.conf.hash

       第二,配置graphdriver目錄。它用於完成docker容器鏡像管理所需的底層存儲驅動層,因此在這一步的配置工做就是加載並配置鏡像存儲驅動graphdriver,建立存在驅動鏡像管理層文件系統所需的目錄和環境,初始化鏡像層元數據存儲。建立graphdriver時,首先會從環境變量DOCKER_DRIVER中讀用戶指定的驅動,若爲空,則開始遍歷優先級數組選擇一個graphdriver,在Linux環境下,優先級從高到低依次爲aufs、btrfs、zfs、devicemapper、overlay和vfs。 不一樣操做系統下,優先級列表的內容和順序都會不一樣,並且隨着內核的發展以及驅動的完善,會繼續發生變化。

       須要注意,目前vfs在docker中時用來管理volume的,並不做爲鏡像存儲使用。另外,因爲目前在overlay文件系統上運行的docker容器不兼容SELinux,所以當config中配置信息須要啓動SELinux而且driver的類型爲overlay時,該過程就會報錯

       當識別出對應的driver後,docker執行這個driver對應的初始化方法(位於daemon/graphdriver/aufs/aufs,go),這個初始化的主要工做包括:嘗試加載內核aufs模塊來肯定docker主機支持aufs,發送statfs系統調用獲取當前docker主目錄(/var/lib/docker)的文件系統信息,肯定aufs是否支持該文件系統;建立aufs驅動根目錄(默認:/var/lib/docker/aufs)並將該目錄配置爲私有掛載,在根目錄下建立mnt、diff和layers目錄做aufs驅動的工做環境,工做完成後,graphdriver的配置工做就完成。

       第三,配置鏡像目錄。主要工做是在docker主目錄下建立一個image目錄,來存儲全部鏡像和鏡像層管理數據,默認目錄「/var/lib/docker/image」.在image目錄下,每一graphdriver都有一個具體的目錄用於存儲使用該graphdriver存儲的鏡像相關的元數據

       根據上一步graphdriver的選擇狀況(以aufs爲例)建立image/aufs/layerdb/目錄做爲鏡像層元數據存儲目錄,並建立MetadataStore用來管理元數據。根據graphdriver與元數據存儲結構建立layerStore,用來管理全部的鏡像和容器層,將邏輯鏡像層的操做映射到物理存儲驅動層graphdriver的操做,建立用於registry的鏡像上傳下載的uploadManager和downloadMannger

       建立image/aufs/imagedb/目錄用於存儲鏡像的元數據,根據layerStore建立imageStore,用來管理鏡像的元數據。

       第四,調用volume/local/local.go#New建立volume驅動目錄(默認爲/var/lib/docker/volumes),docker中volume是宿主機上掛載到docker容器內的特定目錄。volume目錄下有一個metadata.db 數據庫文件用於存儲volume相關的元數據,其他以volume ID 命名的文件夾用於存儲具體的volume內容。默認的volume驅動是local,用戶也能夠經過插件的形式使用其餘volume驅動來存儲

      第五,準備「可信鏡像」所需的工做目錄。docker工做根目錄下建立trust目錄。這個存儲目錄能夠根據用戶給出的可信URL加載受權文件,用來處理可信鏡像的受權和驗證過程。

      第六,建立distributionMetadataStore和referenceStore。referenceStore用於存儲鏡像倉庫列表。記錄鏡像倉庫的持久化文件位於docker根目錄下的image/[graphdriver]/repositories.json中,主要記錄鏡像ID與鏡像倉庫之間的映射。distributionMetadataStore存儲與第二版鏡像倉庫registry有關的元數據,主要用於作鏡像層的diff_id與registry中鏡像層元數據之間的映射

      第七,將持久化在Docker根目錄中的鏡像、鏡像層以及鏡像倉庫等的元數據內容恢復到daemon的imageStore、layerStore和reference中

      第八,執行鏡像遷移,docker1.10版本之後,鏡像管理部分使用了基於內容尋址存儲。在第一次啓動daemon時,爲了將老版本的graph鏡像管理遷移到新的鏡像管理體系中,這裏會根據docker根目錄中是否存在graph文件夾,若是存在就會讀取graph中的老版本鏡像信息,計算校驗和並將鏡像數據寫入到新版本的imageStore和layerStore中,注意的是,遷移鏡像中計算校驗和是一項很是佔CPU的工做,而且在未完成鏡像遷移時,docker daemon是不會響應任何請求的,全部若是你本地的老版本鏡像和容器比較多時,或者是在對服務器負載和響應比較敏感的線上環境嘗試,docker版本升級,那就要注意妥善安排時間,docker提供了遷移工具讓用戶在老版本daemon運行的時候進行鏡像遷移

     這裏docker daemon須要在docker根目錄(/var/lib/docker)下建立並初始化一系列容器文件系統密切相關的目錄和文件。

建立docker   network

     建立docker daemon運行環境的時候,建立網絡環境是極爲重要的一部分。這不只關係着容器對外通訊,一樣也關乎着容器之間的通訊。網絡部分早已被抽離出來做爲一個單獨的模塊,稱爲libnetwork,libnetwork經過插件的形式爲docker提供網絡功能,使得用戶能夠根據本身需求實現本身的dirver來提供不一樣的網絡功能。截止docker1.10版本,libnetwork實現了host、null、birdge和overlay的驅動。其中,birdge driver 爲默認驅動,和以前版本中的docker網絡功能是基本等價的,須要注意的是,同以前的docker網絡同樣,bridge driver並不提供跨主機通訊的能力,overlay driver則是用於多主機環境

 初始化execdriver

    execdriver是docker中用來管理容器的驅動,docker會調用execdrivers中NewDriver()函數來建立新的execdriver

    在建立execdriver的時候,須要注意一下5部分信息

    運行時中指定使用的驅動類型,在默認配置文件中默認使用native,即其對應的容器運行時爲libcontainer;

    用戶定義的execdirver選項,即-exec-opt參數值

    用戶定義的-exec-root參數值,docker execdriver運行的root路徑,默認爲/var/run/docker;

    docker 運行時的root路徑,默認爲/var/lib//docker

    系統功能的信息,包括容器的內存限制功能,交換分區內存限制功能、數據轉發功能以及AppArel安全功能等;AppArel經過host主機是否存在/sys/kernel/security/apparmor來判斷是否加入AppArel配置

    最後,若是選擇netive做爲這個execdriver的驅動實現,上述driver的建立過程就會新建一個libcontainer,這個libcontainer會在後面建立和啓動Linux容器時發揮做用

daemon對象誕生   

   docker daemon進程在通過以上諸多設置以及建立對象以後,最終建立出了daemon對象實例

ID 根據傳入的證書生成的容器ID,若沒有傳入則自動使用ECDSA算法生成
repository 部署全部docker容器的路徑
containers 用於存儲具體的docker容器信息的對象
execCommands docker容器所執行的命令
referenceStore 存儲docker鏡像倉庫名和鏡像ID的映射
distributionMetadataStore v2版registry相關的元數據存儲
trustkey 可信任證書
IDInfo 用於經過簡短有效的字符串前綴定位惟一的鏡像
sysInfo docker所在宿主機的系統信息
configStore docker所需配置信息
execDriver docker 容器執行驅動,默認native類型
statsCollector 收集容器網絡以及cgroups的信息
dafaultLogConfig

提供日誌的默認配置信息

registryService 鏡像存儲服務相關信息
EvenetsServer 事件服務相關信息
volume

volume所使用的驅動,默認爲local

root docker運行的工做根目錄
uidMaps uid的對應圖
gidMaps gid的對應圖
seccompEnabled 是否使用seccompute
nameIndex 記錄建和其名字的對應關係
linkIndex 容器的link目錄,記錄容器的link關係

 

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

恢復已有的docker容器

      當docker daemon啓動時,會去查看在daemon.repository也就是在/var/lib/docker/containers中的內容。如有已經存在的docker容器,則將相應信息收集並進行維護,同時重啓restart policy 爲always的容器

      docker daemon的啓動看起來很是複雜,這是docker在演進的過程當中不斷增長功能點形成的,但無論從此docker的功能點增長多少,docker daemon進程的啓動都將遵循3步

(1)首先建立一個API server,它工做在用戶經過-H指定socket

(2)而後docker使用NewDaemon方法建立一個daemon對象來保存信息和處理業務邏輯

(3)最後將上述API server和daemon對象綁定起來,接受並處理client的請求

只不過,NewDaemon方法的長度會不斷增長而已

從client到daemon

   發起請求

   (1)docker  run命令開始運行,用戶端的docker進入client模式

   (2)通過初始化,新建出了一個client

   (3)上述client經過反射機制找到了CmdRun方法

    CmdRun在解析過程用戶提供的容器參數等一系列操做後,最終發出了這樣兩個請求:

 「POST」,「/containers/create?」+containerValues   //建立容器

 「POST」 ,「/containers/」+createResponse.ID+"/start"  //啓動容器

   至此,client  任務結束

   建立容器 

   在這一步docker daemon並不須要建立一個真正的Linux容器,它只須要理解用戶經過client提交的POST表單,而後使用這些參數在daemon中新建一個container對象出來便可,這個container實體就是container/container_unix.go,其中的commonContainer字段定義在平臺爲主。

   啓動容器

    這個時候daemon這邊的重點來了。API server接受到start請求後告訴docker daemon進行container啓動容器操做,這個過程daemon/start.go

    此時,因爲container所需的各項參數,如NetworkSetings、ImageID等,都已經在容器過程當中賦好了值,docker daemon會在start.go 中直接執行daemon.ContainerStart,就可以宿主機上建立對應的容器了;建立容器過程是docker daemon,containerMonitor將daemon設置爲本身的supervisor。因此通過一系列調用後。daemon.ContainerStart 實際上執行的操做是

    即告訴daemon進程,請使用container相關的信息做參數,執行對應的execdriver的Run方法

   最後一步

   「萬事俱備,只欠東風」。在docker daemon已經完成全部的準備工做,最後下達了執行Run操做的命令後,跟系統打交道的任務都交給ExecDriver.Run來完成;execdriver是docker的重要組成部分,它封裝了對namespace、cgroups等全部對OS資源操做的方法,而在docker中。execdriver的默認實現(native)就是libcontainer了,到這一步。docker daemon只須要提供三大參數,接下來等着返回結果

    * commandv:該容器須要的全部配置信息集合

    * pipes:用於將容器stdin、stdout、stderr重定向到daemon

    * startCallback():回調方法

相關文章
相關標籤/搜索