红姐单双报

你好,歡迎來到js代碼網。

微信登錄

首頁>編程語言> 云計算時代,你真的懂 Docker 嗎?

云計算時代,你真的懂 Docker 嗎?

  • 分類:編程語言
  • 時間:07-21
  • 閱讀:646

要論云計算領域中,開發者需要具備哪些基本技能?那么 Docker 必是其一。 作為一個開源的應用容器引擎,Docker 能夠讓開發者可以打包他們的應用以及依賴包到一個可移植的容器中,然后發布到任何流行的 Linux 機器上,也可以實現虛擬化,方便快捷。

那么你真的足夠了解 Docker 嗎?



彩蛋:

點擊下載docker相關視頻教程





Docker是如何工作的?這是一個簡單的問題,但答案卻是出乎意料的復雜。你可能很多次地聽說過“守護進程(daemon)”和“運行時(runtime)”這兩個術語,但可能從未真正理解它們的含義以及它們是如何配合在一起的。如果你像我一樣,涉過源頭去發現真相,那么,在你沉溺于代碼之海時,你并不孤單。讓我們面對現實吧,假想Docker的源代碼是一頓意式大餐,而你正在狼吞虎咽你的美味意面。

就像一把叉子可以把面條送到你的口中,這篇文章會將Docker的技術的方方面面組織在一起并導入你饑餓的大腦。

為了更好地理解現在,我們首先需要回顧過去。2013年,dotCloud公司的Solomon Hykes在那年的Python大會上發表了Linux容器的未來的演講,第一次將Docker帶入了公眾的視線。讓我們將他的git代碼庫回溯到2013年1月,這個Docker開發更輕松的時間。


01.Docker 是如何工作的?





Docker由兩個主要組件組成,一個供用戶使用的命令行應用程序和一個管理容器的守護進程。這個守護進程依賴兩個子組件來執行它的任務:在宿主主機文件系統上用來存儲鏡像和容器數據的存儲組件;以及用于抽象原始內核調用來構建Linux容器的LXC接口。

命令行應用程序

Docker命令行應用程序是管理你的Docker運行副本已知的所有鏡像和容器的人工界面。它相對簡單,因為所有的管理都是由守護進程完成的。應用程序開始于一個main 函數:



它會立即建立一個TCP連接,指向存儲在DOCKER這個環境變量中的地址,這是Docker守護進程的地址。用戶提供的參數發送給守護進程,然后命令行應用程序等待打印成功答復的結果。

dockerd

Docker守護進程的代碼存放在同一個代碼庫中,這個進程稱為dockerd。它運行在后臺來處理用戶請求和容器清理工作。啟動后,dockerd將監聽在8080端口上傳入的HTTP連接和在4242端口上的TCP連接。



一旦命令接收到后,dockerd將使用反射機制查找并調用要運行的函數。

docker run命令

其中一個運行的函數是CmdRun,它對應這個docker run(注:這個命令創建一個新的容器并運行一個命令)命令。


用戶通常會提供一個鏡像和一個命令,以便dockerd運行。當它們被省略時,將默認使用鏡像base和命令/bin/bash。

查找鏡像

然后,我們通過將名稱(或ID)映射到文件系統上的一個位置來找到指定的鏡像(假設前面已經執行過docker pull (注:這個命令從鏡像倉庫中拉取或者更新指定鏡像)命令,鏡像已經生成)。


在這個版本的Docker中,所有鏡像都存儲在/var/lib/docker/images文件夾中。想進一步了解Docker鏡像中有什么,請參閱文章(https://cameronlonsdale.com/2018/11/26/whats-in-a-docker-image/)。

創建容器

接著我們開始創建容器。dockerd創建了一個結構(struct)來保存與這個容器相關的所有元數據,并將它存儲在一個列表中以便于訪問。


當創建這個struct時,dockerd 會在下列路徑為容器創建下面這個唯一目錄:/var/lib/docker/containers/。在這個目錄下中有兩個子目錄:/rootfs 只讀根文件系統層(來自聯合掛載(union mounted)的鏡像),/rw提供一個單獨的讀寫層,供容器來創建臨時文件。

最后,使用我們新創建的容器數據填充一個模板,用以生成LXC 配置文件。更多關于LXC的信息,請參見下一節。

運行容器

我們的容器終于創建出來了!但它還沒有運行,現在讓我們啟動它。



第一步是確保容器的文件系統已掛載。



使用AUFS 聯合掛載文件系統(union mount file system),鏡像的各個層以read-only方式掛載在彼此的頂部,以向容器呈現一個一致的視圖。read-write路徑被掛載在最頂層,為容器提供臨時存儲。

然后,為了啟動容器,dockerd使用剛才生成的LXC模板來運行另一個程序lxc-start。

LXC

LXC(Linux Containers)是一個抽象層,它為用戶空間應用程序提供了一個簡單的API來創建和管理容器。事實是,容器不是真實的東西,在Linux內核中沒有稱作容器的對象。容器是一組內核對象的集合,它們協同工作以提供進程隔離。因此,簡單的lxc-start命令實際上被翻譯成下面的設置和應用:

內核名字空間 (ipc, uts, mount, pid, network 和 user)

Apparmor和 SELinux profiles

Seccomp policies

Chroots (使用 pivot_root)

Kernel capabilities

CGroups (control groups)

容器清理




最后,dockerd將監控容器直到完成,并在容器完成后清理不必要的數據。

總結

概括起來,使用Docker 2013來管理一個容器的運行涉及以下步驟:




02.Docker發生了什么變化?

Docker自從被引入以來已經過去6年了,容器化模式在此期間已經得到了迅猛發展。無論是大小企業都采用了Docker,特別在它和容器編排系統Kubernetes的結合之后。

通過開源的力量,起初的3位貢獻者已經發展到1800多人,每個人都為項目帶來了新的想法。為了促進可擴展性,Open Container Initiative(OCI)標準于2015年發布,它定義了容器的鏡像和運行時規范的開放標準。鏡像規范概述了容器鏡像的結構,運行時規范則描述了在其平臺上運行容器時的實現應該遵循的接口和行為標準。因此,社區開發了廣泛的容器管理項目,涵蓋了從原生容器到被虛擬機隔離的容器。在微軟的支持下,該行業現在也擁有了符合OCI標準的原生Windows容器。

所有這些變化都反映在moby代碼庫中。基于這種歷史背景,我們可以開始分析Docker 2019及其組件了。

03.Docker 2019是如何工作的?

經過6年和36207次的代碼提交,moby代碼庫已經發展成為一個大型的合作項目,它正在影響和依賴許多組件。




從一個非常簡單的角度來看,Moby 2019有兩個新的主要組件,一個是在容器的生命周期中管理容器的containerd 組件,另一個是符合OCI標準的運行時(例如runc)組件,它是用于創建容器的最低用戶級抽象(替換LXC)。

命令行應用程序

命令行應用程序的控制流程大部分沒有改變。今天,JSON格式的HTTP(S)報文是與 dockerd通信的標準。

為了實現可擴展性,API和docker二進制文件是分開的。程序代碼位于docker/cli目錄,它依賴moby/moby/client包作為接口與dockerd對話。

dockerd

dockerd負責監聽用戶請求,并根據預先定義的路由對這些請求進行處理。



這個引擎仍然負責各種各樣的任務,例如與鏡像注冊項的交互,并在文件系統上設置目錄供容器使用。默認驅動程序會把一個鏡像聯合掛載到/var/lib/docker/overlay2/下的目錄。

dockerd不再負責管理運行容器的生命周期。隨著項目的發展,決定將容器監管分開到一個名為containerd的單獨項目。這樣docker守護進程可以繼續創新,而不必擔心破壞運行時的實現。

盡管 docker/engine是從moby/moby分支出來的,考慮到可能的代碼差異,到目前為止,它們共享相同的代碼提交樹。

docker run命令


docker run命令首先請求守護進程創建一個容器。此請求被路由到postContainersCreate命令。

創建容器

稍后調用幾個函數,我們來創建一個容器。


首先,我們創建一個對象來存儲容器的元數據。

然后像以前一樣,我們創建一個根目錄,其中包含鏡像數據和讀寫(read-write)層,供容器使用。現在的區別在于聯合掛載文件系統(union mount file system)已經成長到能夠支持btrfs, OverlayFS和其它文件系統。為了方便這一點,驅動系統抽象了實現。

最后,容器對象被添加到守護進程的容器列表(map)中,以供將來使用。

啟動容器

現在容器已創建成功,但尚未運行。接下來,我們請求啟動它。


這就是containerd 起作用的地方,首先我們請求按照OCI規格(spec)來創建一個容器。然后,開始在這個容器內運行一個進程。然后把隨后的監管交給containerd來處理。

containerd

containerd這個術語有令人困惑的地方。它被認為是一個運行時(runtime),但是又不實現OCI 運行時規范,因此它是一個和runc不一樣的的運行時。containerd 是一個守護進程,它使用了符合OCI規格的運行時來監管容器的生命周期。正如Michael Crosby所描述的,containerd是容器的監管者。


containerd被設計成監控容器的通用基礎層,專注于速度和簡單性。

很簡單,創建一個容器所需要的只是它的規格(spec)和一個對根文件系統所在的位置進行編碼的包。

創建容器




dockerd(通過一個GRPC客戶端)請求containerd創建一個容器。containerd 接收這個請求后,將容器規格(spec)存儲在位于/var/lib/containerd/下的文件系統支持的數據庫中。

啟動容器


啟動一個容器涉及創建和啟動一個稱為Task的新對象,該對象代表著容器內的一個進程。

創建Task


Task的創建由底層容器的運行時負責。containerd 復用了OCI運行時,因此我們需要查找哪個運行時被用來創建這個Task。第一個和默認的運行時是runc。這個運行時的創建命令最終是運行一個外部進程runc,但它是通過間接調用docker-shim進程來完成的。

如果containerd崩潰了,運行中的容器的信息將會丟失。為了防止這種情況發生,containerd為每個容器創建一個稱為墊片(shim)的管理進程。shim進程將調用一個OCI運行時來創建和啟動一個容器,然后執行監視容器的職責,以捕獲退出代碼并管理標準IO。

在嵌套代碼中,shim將在執行create命令時使用go-runc bindings庫來啟動/run/containerd/runc命令。。有關runc的更多信息,請參見下一節。

如果containerd 真的崩潰了,可以通過與shim通信并從/var/run/containerd/目錄讀取狀態信息來恢復。

啟動Task

既然容器已經創建了,啟動Task需要做的事情就是簡單地指示shim程序通過調用runc start命令來啟動進程。

Runc

runc是一個命令行工具,用于根據OCI規格生成和運行容器。當它執行與LXC類似的工作時,它抽象出創建容器所需的Linux內核調用。


runc 只是OCI運行時規格(spec)的一個實現,更多的可用于在各種系統上創建容器的實現可以在這里找到(https://github.com/opencontainers/runtime-spec/blob/master/implementations.md)。

創建容器(runc create)


當runc創建一個容器時,它會在容器內設置名字空間、cgroups甚至init進程。當創建結束時,進程暫停,等待信號開始運行。

啟動容器(runc start)


最后,runc向暫停的進程發送一個信號以開始容器的啟動。

直觀總結

概括起來,使用Docker 2019來管理一個容器的運行涉及以下步驟:

一個創建容器的POST請求發送給dockerd;

dockerd查找被請求的鏡像;

一個容器對象被創建,并存儲起來以供將來使用

設置文件系統的目錄結構供容器使用;

一個啟動容器的POST請求發送給dockerd;

為容器創建OCI規格(spec);

containerd被調用來創建容器;

containerd將容器規格儲存在數據庫中;

containerd被調用來啟動容器;

containerd為容器創建一個Task


Task使用shim來調用runc create指令

containerd啟動這個Task

Task使用shim來調用runc start指令

shim/containerd繼續監控容器直到任務完成

借助containerd的架構圖,可以讓我們直觀地理解整個過程。




04.結論

表面上看,Docker及其配套項目顯得雜亂無章,但實際上其底層的結構清晰并且已經實現了模塊化。也就是說,發現所有上面的這些信息不是一件輕松的事。它散落在代碼、博客帖子、會議討論、文檔和會議筆記中。擁有清晰的“自文檔化”的代碼是一個偉大的目標,但當涉及到大型系統時,我認為這還不夠。有時,你只需要用簡單的語言寫下系統的外觀,以及每個組件的職責。

非常感謝這些項目的所有貢獻者,特別是那些編寫解釋這些系統的文檔的人。

希望這篇文章這有助于幫助你理解Docker是如何運行容器的。



彩蛋:

點擊下載docker相關視頻教程


Docker相關熱門文章推薦:

這可能是最為詳細的Docker入門吐血總結


相關文章

红姐单双报 奥迅球探比分直播 豆豆广西麻将下载 陕西体彩11选5开奖结果 天天棋牌龙虎技巧 钱怎样投资才能赚钱 2012中甲比分直播 河北11选5推荐号今天 重庆时时采彩计划 棕榈期货走势 苹果版 重庆时时彩 13256平特一肖 双面盘彩票 狗币矿池搭建 重庆时时彩是正规的吗? 单期计划软件 AG水上乐园预测