溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

docker源碼分析Libcontainer

發(fā)布時間:2021-12-14 11:34:52 來源:億速云 閱讀:273 作者:iii 欄目:云計算

這篇文章主要講解了“docker源碼分析Libcontainer”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“docker源碼分析Libcontainer”吧!

1. Libcontainer簡介

1.1 linux container相關技術

要了解Libcontainer首先要了解linux container所用到的一些基本技術。linux container是一種內核虛擬化技術,可以提供輕量級的虛擬化,以便隔離進程和資源。而這正是docker容器技術和核心,docker正是linux container的一種實現(xiàn)。linux container所用到的基本技術包括namespace、cgroup、chroot、veth、union FS、iptables和netfilter、TC、quota、setrlimit,下面對這些基本技術做一個簡要的概括:

1. Namespace:用來做資源隔離以實現(xiàn)輕量級虛擬化,包括六種namespace,UTS namespace提供了主機名和域名之間的隔離;IPC namespace提供了進程間通信的隔離;Network namespace提供了網絡的隔離包括網絡設備、網絡棧、端口等;Mount namespace提供了文件系統(tǒng)的隔離;User namespace提供了用戶權限間的隔離。

2. Cgroups:實現(xiàn)資源限制,可以限制、記錄任務組所使用的物理資源。還可用于優(yōu)先級分配,通過分配的CPU時間片數(shù)量及磁盤IO寬帶大小控制任務運行的優(yōu)先級。用于資源統(tǒng)計,統(tǒng)計系統(tǒng)的資源使用量,如CPU使用時長、內存用量等。用于任務控制,可以對任務執(zhí)行掛起、控制等操作。

3. Chroot:更改root目錄,用于在container里查看到的文件系統(tǒng)。他有三大優(yōu)點:增加系統(tǒng)的安全性,限制了用戶的權利;建立一個與原系統(tǒng)隔離的系統(tǒng)目錄,這一點對容器極為重要;切換系統(tǒng)的根目錄位置。

4. Veth:把一個從網絡用戶空間(network namespace )發(fā)出的數(shù)據包轉發(fā)到另一個用戶空間。即實現(xiàn)容器和宿主機之間的通信。

5. Union FS:疊加的文件系統(tǒng),其中包括aufs一種支持聯(lián)合掛載的文件系統(tǒng)。

6. Iptables,netfilter:主要用來做ip數(shù)據包的過濾。

7. TC:主要用來做流量隔離,帶寬的限制。

8. Quota:用來做磁盤讀寫大小的限制,用來限制用戶可用空間的大小。

9. Setrlimit:可以限制container中打開的進程數(shù),限制打開的文件個數(shù)等。

1.2 Libcontainer簡介

       基于上文對linux container相關技術,docker基本是實現(xiàn)了前五個的技術,用libcontainer做了一層封裝。也就是說docker通過libcontainer封裝了linux container的部分技術,這樣使得Docker具有持續(xù)部署與測試、跨云平臺支持、環(huán)境標準化和版本控制、高資源利用率與隔離、容器跨平臺性與鏡像、易于理解且易用以及具有應用鏡像倉庫等優(yōu)點。Libcontainer本質上是Docker中用于容器管理的包,基于Go語言實現(xiàn),通過管理namespace、Cgroups、capabilities以及文件系統(tǒng)等來進行容器控制。Libcontainer可用于創(chuàng)建容器并對容器進行生命周期管理。提到Libcontainer就要提到execdriver,execdriver封裝了對namespace、cgroups等對OS資源進行操作的所有方法,而Libcontainer是execdriver的默認實現(xiàn)。execdriver通過得到的command信息加載生成容器配置container,然后調用libcontainer加載容器配置container,創(chuàng)建真正的docker容器,完成容器的創(chuàng)建并對容器的生命周期進行管理。

2. execdriver工作流程

       Execdriver的工作流程如圖2.1所示:

docker源碼分析Libcontainer

圖2.1 execdriver的工作流程

2.1 配置信息簡介

Execdriver首先得到Docker daemon提交的command信息,提交過來的command信息包含namespace、cgroup等配置容器所需的重要信息。相對應的command結構體源碼如圖2.2,其中包含了生成容器所需的基本配置,有namespace相關比如UTS可提主機名和域名之間的隔離;IPC提供了進程間通信的隔離;Network提供了網絡的隔離包括網絡設備、網絡棧、端口等;Mount提供了文件系統(tǒng)的隔離。Resource包含了cgroup相關的信息,ProcessConfig表示容器中運行的進程的信息。

對圖2.2中的部分參數(shù)做簡要解釋,其中:ContainerPid表示容器中進程的pid;ID是容器ID,代表容器的唯一標識,非常重要;Mount是namespace的一種用于文件系統(tǒng)的隔離;Network也是namespace的一種用于進行網絡的隔離;ProcessConfig描述了容器中運行的進程的信息;Resource提供了cgroup相關的信息,后面會對Resource結構體展開做詳細的分析;Rootfs是容器的根目錄系統(tǒng);WorkingDir顧名思義是容器的工作路徑;TmpDir是用來存儲docker臨時文件的目錄;

docker源碼分析Libcontainer

docker源碼分析Libcontainer

圖2.2 command結構體

Cgroups用于實現(xiàn)資源限制,可以限制、記錄任務組所使用的物理資源。cgroups相關信息包含在resource里,resource包含了對driver配置的所有資源的信息,resource結構體相關定義如圖2.3,其中:memory表示所使用的存儲容量,還定義了CPU用量等cgroup所需的信息。

docker源碼分析Libcontainer

docker源碼分析Libcontainer

圖2.3 resource結構體

       ProcessConfig中包含了表示容器中運行的進程的信息,ProcessConfig結構體相關定義源碼如圖2.4,

docker源碼分析Libcontainer

docker源碼分析Libcontainer

圖2.4ProcessConfig結構體

2.2 主要流程分析

       圖2.1中所示的工作流程相對應的源碼在deamon/execdriver/native/driver.go的run函數(shù)中,run函數(shù)部分截圖如圖2.5所示,其中container, err := d.createContainer(c, hooks)語句的作用是調用createContainer函數(shù)創(chuàng)建容器配置。函數(shù)傳入的參數(shù)c表示execdriver.Command,即上文提到的command結構體,也就是說createContainer函數(shù)根據command參數(shù)創(chuàng)建相關的容器配置。

docker源碼分析Libcontainer

圖2.5 Run函數(shù)部分函數(shù)體

       上文說到createContainer函數(shù)根據command參數(shù)創(chuàng)建相關的容器配置,下面我們看一下createContainer函數(shù)的內部結構,如圖2.6為createContainer函數(shù)的部分結構。其中container = execdriver.InitContainer(c)可以看到調用InitContainer函數(shù)通過傳入的execdriver.Command參數(shù)生成容器配置container。其中一系列的createXXX()方法根據InitContainer函數(shù)得到的container填充模板,配置IPC、Pid、network等所需字段。其中createIpc()表示配置Ipc提供提供了進程間通信的隔離;createPid()表示配置Pid;createUTS()表示配置UTS提供主機名和域名之間的隔離;createNetwork()配置Network提供了網絡的隔離包括網絡設備、網絡棧、端口等。

docker源碼分析Libcontainer

圖2.6 createContainer函數(shù)的部分函數(shù)體

       由createContainer函數(shù)的源碼的內部結構可以看到在createContainer函數(shù)中首先調用InitContainer函數(shù)生成了一個叫做container的變量,InitContainer函數(shù)通過傳入的execdriver.Command參數(shù)生成容器配置container,如圖2.7是execdriver.InitContainer函數(shù)的內部結構。在InitContainer函數(shù)中根據command配置container的hostname主機名、cgroup、devices、rootfs等信息,最后返回一個容器配置container,這時候的返回的container其實是一個Config對象,表示容器配置。后面再由createContainer函數(shù)中的createXXX()方法根據InitContainer函數(shù)返回的container容器配置,配置相應IPC、Pid、network等所需字段。

docker源碼分析Libcontainer

圖2.7 InitContainer函數(shù)的部分函數(shù)體

    至此我們已經分析完了deamon/execdriver/native/driver.go的run函數(shù)中container, err := d.createContainer(c, hooks)語句,簡單的說該語句的結果就是生成了一份container容器配置。接下來在run函數(shù)中execdriver調用libcontainer加載已經生成好的容器配置container,創(chuàng)建真正的Docker容器。

3. Libcontainer實現(xiàn)原理

       在deamon/execdriver/native/driver.go的run函數(shù)中,成功生成container容器配置以后,工作就交由libcontainer。libcontainer的主要工作為:

1. 創(chuàng)建libcontainer構建容器所需要使用的進程對象,即Process。對應源碼如圖3.1所示。Process指定了容器內進程對象的配置和IO,其中有指定若干參數(shù),并對參數(shù)賦值。Args表示將要運行的一系列指令;Env指定該進程對象的環(huán)境變量;Cwd將進程的工作目錄改至容器的rootfs中;User將為容器中的正在運行的進程設置UID和GID。

docker源碼分析Libcontainer

圖3.1 構建Process

2.接下來在run函數(shù)中err := setupPipes(container, &c.ProcessConfig, p, pipes);語句調用setupPipes函數(shù)設置容器的輸出管道。而setupPipes函數(shù)即為設置容器輸出管道函數(shù),其函數(shù)體定義在deamon/execdriver/native/driver.go的setupPipes函數(shù)中。setupPipes函數(shù)主要通過execdriver.Pipes配置容器的輸出管道,其主要作用是將容器的輸出成標準輸入、標準輸出和標準錯誤。

3. 使用Factory工廠類,用容器ID和容器配置container創(chuàng)建邏輯容器Container,在run函數(shù)中對應的源碼為:d.factory.Create(c.ID, container),其中c為execdriver.Command,c.ID為容器ID,container即為之前多次提到的容器配置。在生成邏輯容器的過程中,容器配置container的各項會填充到邏輯容器Container對像的配置項config里。

4.接下來用啟動容器,啟動容器對應的語句為cont.Start(p),其中cont為d.factory.Create(c.ID, container)函數(shù)生成的Container邏輯容器,而參數(shù)p為之前生成的容器所需要使用的進程對象Process。

5. 下面的代碼p.Wait()即為process.Wait(),表示等待之前Process的所有工作都完成,直到物理容器創(chuàng)建成功。Processd的Wait函數(shù)所對應的源碼為圖3.2所示。

docker源碼分析Libcontainer

圖3.2 Process的Wait()函數(shù)

6. 最后的cont.Destroy()表示Container.Destory(),即在需要的情況下可以銷毀容器。

通過上述對libcontainer主要工作分析,我們發(fā)現(xiàn)libcontainer的重點正是Process、Container、Factory這3個邏輯實體的實現(xiàn)。其中Factory用于創(chuàng)建一個邏輯上的容器對象;Container是包含容器配置信息的邏輯容器;Process用于物理容器中進程的配置和IO管理。下面我們libcontainer中這三個邏輯實體進行詳細的解析。

3.1 Factory創(chuàng)建邏輯容器

       Factory的作用是用給定的容器ID創(chuàng)建一個新的容器,并在該容器中啟動初始進程。并且接受的容器ID為只包含字母、數(shù)字、下劃線組成的字符創(chuàng),且長度必須在1到1024之間。容器ID不能與已經存在的容器的ID重合,使用同一路徑(和文件系統(tǒng))的Factory創(chuàng)建的容器必須有不同的標識。最后用一個正在運行的進程返回一個新的容器。

    在這個過程中可能出現(xiàn)的錯誤有:IdInUse表示容器ID已經被其他容器占用;InvalidIdFormat表示容器ID的格式不正確;ConfigInvalid表示配置信息無用;Systemerror表示系統(tǒng)錯誤。一但發(fā)生錯誤,那么任何已經創(chuàng)建的容器部分都會被清除,保證了容器創(chuàng)建的原子性,要不全部創(chuàng)建成功,否則全部不創(chuàng)建。

    Factory對象中包含三個函數(shù),他們分別為:

    1. Create()函數(shù):其傳入參數(shù)為一個容器ID和一份Config類型的配置參數(shù),并且接受的容器ID為只包含字母、數(shù)字、下劃線組成的字符創(chuàng),且長度必須在1到1024之間。容器ID不能與已經存在的容器的ID重合,使用同一路徑(和文件系統(tǒng))的Factory創(chuàng)建的容器必須有不同的標識。根據傳入的這兩個參數(shù)創(chuàng)建并返回一個Container類,其中包括容器ID、容器工作目錄、容器配置、初始化指令和參數(shù)、以及Cgroup管理器等信息。在這個函數(shù)中Container創(chuàng)建完畢。其中可能出現(xiàn)路徑不存在、容器已經停止、系統(tǒng)故障等錯誤。

    2. Load()函數(shù):傳入參數(shù)為一個已經被成功Create過的容器的容器ID,返回該容器的信息。如果容器已經Create過說明存在id目錄,則會從id目錄下直接讀取state.json來載入容器信息。其中可能出現(xiàn)的錯誤有管道連接錯誤和系統(tǒng)故障。

    3. StartInitialization()函數(shù):是容器初始化函數(shù),是Libcontainer在容器重新執(zhí)行期間會調用的內部API。

    4. Type()函數(shù):返回容器管理的類型,比如lxc或libcontainer等。

    至此,F(xiàn)actory對象完成了容器的創(chuàng)建和初始化。接下來就了解一下包含包含容器配置信息的邏輯容器Container。

3.2 邏輯容器Container

       Container對象相當于是邏輯容器主要包含了容器配置、控制、狀態(tài)顯示等功能。其中ID表示容器的ID。Status表示容器內進程的狀態(tài),容器的狀態(tài)包括:Running表示容器存在并且正在運行;Pausing表示容器存在并且進程正在被停止;Paused表示容器存在但是所有的進程都被停止了;Checkpointed表示容器存在并且容器狀態(tài)都已保存至磁盤;Destoryed表示容器不存在。

    Container對象中具有一系列容器相關的函數(shù)操作,其中包括:

    ID():返回容器的ID,代表容器的唯一標識

    Status():返回容器內進程的狀態(tài),可能為運行狀態(tài)也可能是停止狀態(tài)??赡軖伋龅腻e誤為ContainerDestroyed表示容器不存在已經被銷毀;Systemerror表示系統(tǒng)錯誤。

    State():返回容器的狀態(tài)信息,包括容器ID、配置信息、初始進程ID、進程啟動時間、cgroup文件路徑、namespace路徑等??赡艹霈F(xiàn)的錯誤為Systemerror即系統(tǒng)錯誤。

    Config():返回容器的配置信息

    Processes():返回容器的PID,這個PID即為用來調用進程的namespace。有些PID可能不在與容器中的進程相關,除非容器的狀態(tài)是PAUSED,這樣才能保證每一個PID都是有效的。

    Stats():返回容器統(tǒng)計信息,包括cgroup中的統(tǒng)計以及網卡設備的統(tǒng)計信息。

    Set():設置容器的資源配置,例如cgroup各個子系統(tǒng)的文件路徑等。

    Start():在容器內啟動一個進程,如果進程啟動失敗就返回一個錯誤??梢愿鶕酝腜rocess結構追蹤進程的生命周期。其中主要工作有兩個:創(chuàng)建ParentProcess實例,執(zhí)行ParentProcess.start()來啟動物理容器。ParentProcess是一個接口其具體實現(xiàn)為initProcess對象,initProcess用于創(chuàng)建容器所需的ParentProcess,為創(chuàng)建物理容器做準備。用邏輯容器Container執(zhí)行initProcess.start(),真正的物理容器即Docker容器就生成了。

    Destory():在結束所有的正在運行的進程以后銷毀容器。

3.3  Process對象

    Process分為兩類,一類是Process另外一類是ParentProcess。Process用于容器內進程的配置和IO的管理,其參數(shù)包括:Args表示將要運行的一系列指令;Env指定該進程對象的環(huán)境變量;Cwd將進程的工作目錄改至容器的rootfs中;User將為容器中的正在運行的進程設置UID和GID;Stdin io.Reader表示標準輸入;Stdout io.Writer表示標準輸出;Stderr io.Writer表示標準錯誤;consolePath表示到容器的控制臺的路徑;Capabilities表示容器中進程運行所需的權限;ops表示ParentProcess對象。ParentProcess負責處理容器啟動工作,包含一系列的函數(shù)動作:

    pid():返回一個正在運行的進程的pid,可以通過管道從已啟動的容器進程中獲得。

    start():開始容器中的執(zhí)行進程。

    terminate():發(fā)送SIGKILL信號結束進程。

    StartTime():獲取進程啟動時間。

    signal():發(fā)送信號給進程。

    wait():等待程序執(zhí)行結束,返回結束的程序狀態(tài)。

感謝各位的閱讀,以上就是“docker源碼分析Libcontainer”的內容了,經過本文的學習后,相信大家對docker源碼分析Libcontainer這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI