docker client和daemom


client 模式

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

docker [options] command [arg...]

  其中options參數為flag,任何時候執行一個命令docker命令都需要先解析flag,然后按照用戶生命的command向指定的子命令執行對應的操作

       如果子命令為daemom,docker都會創建一個運行在宿主機上的daemom進程,即執行daemom模式。其余子命令都會執行client模式。處於client模式命令工作流程包含幾個步驟

 1.解析flag信息

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

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

      LogLevel,對應-l和--log-level 參數。默認等級為info,即只輸出普通的操作信息。用戶可以指定的日志等級現在有panic、fatal、error、warn、info、DEBUG這幾種

      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():回調方法


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM