溫馨提示×

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

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

怎樣深入剖析Linux容器

發(fā)布時(shí)間:2021-11-22 16:22:19 來(lái)源:億速云 閱讀:155 作者:柒染 欄目:云計(jì)算

怎樣深入剖析Linux容器,針對(duì)這個(gè)問(wèn)題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問(wèn)題的小伙伴找到更簡(jiǎn)單易行的方法。

容器

容器是一種輕量級(jí)的虛擬化技術(shù),因?yàn)樗摂M機(jī)比起來(lái),它少了一層 hypervisor 層。先看一下下面這張圖,這張圖簡(jiǎn)單描述了一個(gè)容器的啟動(dòng)過(guò)程。

怎樣深入剖析Linux容器

最下面是一個(gè)磁盤,容器的鏡像是存儲(chǔ)在磁盤上面的。上層是一個(gè)容器引擎,容器引擎可以是 docker,也可以是其它的容器引擎。引擎向下發(fā)一個(gè)請(qǐng)求,比如說(shuō)創(chuàng)建容器,這時(shí)候它就把磁盤上面的容器鏡像運(yùn)行成在宿主機(jī)上的一個(gè)進(jìn)程。

對(duì)于容器來(lái)說(shuō),最重要的是怎么保證這個(gè)進(jìn)程所用到的資源是被隔離和被限制住的,在 Linux 內(nèi)核上面是由 cgroup 和 namespace 這兩個(gè)技術(shù)來(lái)保證的。接下來(lái)以 docker 為例,詳細(xì)介紹一下資源隔離和容器鏡像兩部分的內(nèi)容。

一、資源隔離和限制

namespace

namespace 是用來(lái)做資源隔離的,在 Linux 內(nèi)核上有七種 namespace,docker 中用到了前六種。第七種 cgroup namespace 在 docker 本身并沒有用到,但是在 runC 實(shí)現(xiàn)中實(shí)現(xiàn)了 cgroup namespace。

怎樣深入剖析Linux容器

我們先從頭看一下:

  • 第一個(gè)是 mout namespace。mout namespace 就是保證容器看到的文件系統(tǒng)的視圖,是容器鏡像提供的一個(gè)文件系統(tǒng),也就是說(shuō)它看不見宿主機(jī)上的其他文件,除了通過(guò) -v 參數(shù) bound 的那種模式,是可以把宿主機(jī)上面的一些目錄和文件,讓它在容器里面可見的;

  • 第二個(gè)是 uts namespace,這個(gè) namespace 主要是隔離了 hostname 和 domain;

  • 第三個(gè)是 pid namespace,這個(gè) namespace 是保證了容器的 init 進(jìn)程是以 1 號(hào)進(jìn)程來(lái)啟動(dòng)的;

  • 第四個(gè)是網(wǎng)絡(luò) namespace,除了容器用 host 網(wǎng)絡(luò)這種模式之外,其他所有的網(wǎng)絡(luò)模式都有一個(gè)自己的 network namespace 的文件;

  • 第五個(gè)是 user namespace,這個(gè) namespace 是控制用戶 UID 和 GID 在容器內(nèi)部和宿主機(jī)上的一個(gè)映射,不過(guò)這個(gè) namespace 用的比較少;

  • 第六個(gè)是 IPC namespace,這個(gè) namespace 是控制了進(jìn)程兼通信的一些東西,比方說(shuō)信號(hào)量;

  • 第七個(gè)是 cgroup namespace,上圖右邊有兩張示意圖,分別是表示開啟和關(guān)閉 cgroup namespace。用 cgroup namespace 帶來(lái)的一個(gè)好處是容器中看到的 cgroup 視圖是以根的形式來(lái)呈現(xiàn)的,這樣的話就和宿主機(jī)上面進(jìn)程看到的 cgroup namespace 的一個(gè)視圖方式是相同的;另外一個(gè)好處是讓容器內(nèi)部使用 cgroup 會(huì)變得更安全。

這里我們簡(jiǎn)單用 unshare 示例一下 namespace 創(chuàng)立的過(guò)程。容器中 namespace 的創(chuàng)建其實(shí)都是用 unshare 這個(gè)系統(tǒng)調(diào)用來(lái)創(chuàng)建的。

怎樣深入剖析Linux容器

上圖上半部分是 unshare 使用的一個(gè)例子,下半部分是我實(shí)際用 unshare 這個(gè)命令去創(chuàng)建的一個(gè) pid namespace??梢钥吹竭@個(gè) bash 進(jìn)程已經(jīng)是在一個(gè)新的 pid namespace 里面,然后 ps 看到這個(gè) bash 的 pid 現(xiàn)在是 1,說(shuō)明它是一個(gè)新的 pid namespace。

cgroup

兩種 cgroup 驅(qū)動(dòng)

cgroup 主要是做資源限制的,docker 容器有兩種 cgroup 驅(qū)動(dòng):一種是 systemd 的,另外一種是 cgroupfs 的。

怎樣深入剖析Linux容器

  • cgroupfs 比較好理解。比如說(shuō)要限制內(nèi)存是多少、要用 CPU share 為多少?其實(shí)直接把 pid 寫入對(duì)應(yīng)的一個(gè) cgroup 文件,然后把對(duì)應(yīng)需要限制的資源也寫入相應(yīng)的 memory cgroup 文件和 CPU 的 cgroup 文件就可以了;

  • 另外一個(gè)是 systemd 的一個(gè) cgroup 驅(qū)動(dòng)。這個(gè)驅(qū)動(dòng)是因?yàn)?systemd 本身可以提供一個(gè) cgroup 管理方式。所以如果用 systemd 做 cgroup 驅(qū)動(dòng)的話,所有的寫 cgroup 操作都必須通過(guò) systemd 的接口來(lái)完成,不能手動(dòng)更改 cgroup 的文件。

容器中常用的 cgroup

接下來(lái)看一下容器中常用的 cgroup。Linux 內(nèi)核本身是提供了很多種 cgroup,但是 docker 容器用到的大概只有下面六種:

怎樣深入剖析Linux容器

  • 第一個(gè)是 CPU,CPU 一般會(huì)去設(shè)置 cpu share 和 cupset,控制 CPU 的使用率;

  • 第二個(gè)是 memory,是控制進(jìn)程內(nèi)存的使用量;

  • 第三個(gè) device ,device 控制了你可以在容器中看到的 device 設(shè)備;

  • 第四個(gè) freezer。它和第三個(gè) cgroup(device)都是為了安全的。當(dāng)你停止容器的時(shí)候,freezer 會(huì)把當(dāng)前的進(jìn)程全部都寫入 cgroup,然后把所有的進(jìn)程都凍結(jié)掉,這樣做的目的是:防止你在停止的時(shí)候,有進(jìn)程會(huì)去做 fork。這樣的話就相當(dāng)于防止進(jìn)程逃逸到宿主機(jī)上面去,是為安全考慮;

  • 第五個(gè)是 blkio,blkio 主要是限制容器用到的磁盤的一些 IOPS 還有 bps 的速率限制。因?yàn)?cgroup 不唯一的話,blkio 只能限制同步 io,docker io 是沒辦法限制的;

  • 第六個(gè)是 pid cgroup,pid cgroup 限制的是容器里面可以用到的最大進(jìn)程數(shù)量。

不常用的 cgroup

也有一部分是 docker 容器沒有用到的 cgroup。容器中常用的和不常用的,這個(gè)區(qū)別是對(duì) docker 來(lái)說(shuō)的,因?yàn)閷?duì)于 runC 來(lái)說(shuō),除了最下面的 rdma,所有的 cgroup 其實(shí)都是在 runC 里面支持的,但是 docker 并沒有開啟這部分支持,所以說(shuō) docker 容器是不支持下圖這些 cgroup 的。

怎樣深入剖析Linux容器

二、容器鏡像

docker images

接下來(lái)我們講一下容器鏡像,以 docker 鏡像為例去講一下容器鏡像的構(gòu)成。

docker 鏡像是基于聯(lián)合文件系統(tǒng)的。簡(jiǎn)單描述一下聯(lián)合文件系統(tǒng),大概的意思就是說(shuō):它允許文件是存放在不同的層級(jí)上面的,但是最終是可以通過(guò)一個(gè)統(tǒng)一的視圖,看到這些層級(jí)上面的所有文件。

怎樣深入剖析Linux容器

如上圖所示,右邊是從 docker 官網(wǎng)拿過(guò)來(lái)的容器存儲(chǔ)的一個(gè)結(jié)構(gòu)圖。

這張圖非常形象地表明了 docker 的存儲(chǔ),docker 存儲(chǔ)也就是基于聯(lián)合文件系統(tǒng),是分層的。每一層是一個(gè) Layer,這些 Layer 由不同的文件組成,它是可以被其他鏡像所復(fù)用的??梢钥匆幌拢?dāng)鏡像被運(yùn)行成一個(gè)容器的時(shí)候,最上層就會(huì)是一個(gè)容器的讀寫層。這個(gè)容器的讀寫層也可以通過(guò) commit 把它變成一個(gè)鏡像頂層最新的一層。

docker 鏡像的存儲(chǔ),它的底層是基于不同的文件系統(tǒng)的,所以它的存儲(chǔ)驅(qū)動(dòng)也是針對(duì)不同的文件系統(tǒng)作為定制的,比如 AUFS、btrfs、devicemapper 還有 overlay。docker 對(duì)這些文件系統(tǒng)做了一些相對(duì)應(yīng)的 graph driver 的驅(qū)動(dòng),通過(guò)這些驅(qū)動(dòng)把鏡像存在磁盤上面。

以 overlay 為例

存儲(chǔ)流程

接下來(lái)我們以 overlay 這個(gè)文件系統(tǒng)為例,看一下 docker 鏡像是怎么在磁盤上進(jìn)行存儲(chǔ)的。

先看一下下面這張圖,簡(jiǎn)單地描述了 overlay 文件系統(tǒng)的工作原理。

怎樣深入剖析Linux容器

  • 最下層是一個(gè) lower 層,也就是鏡像層,它是一個(gè)只讀層;

  • 右上層是一個(gè) upper 層,upper 是容器的讀寫層,upper 層采用了寫實(shí)復(fù)制的機(jī)制,也就是說(shuō)只有對(duì)某些文件需要進(jìn)行修改的時(shí)候才會(huì)從 lower 層把這個(gè)文件拷貝上來(lái),之后所有的修改操作都會(huì)對(duì) upper 層的副本進(jìn)行修改;

  • upper 并列的有一個(gè) workdir,它的作用是充當(dāng)一個(gè)中間層的作用。也就是說(shuō),當(dāng)對(duì) upper 層里面的副本進(jìn)行修改時(shí),會(huì)先放到 workdir,然后再?gòu)?workdir 移到 upper 里面去,這個(gè)是 overlay 的工作機(jī)制;

  • 最上面的是 mergedir,是一個(gè)統(tǒng)一視圖層。從 mergedir 里面可以看到 upper 和 lower 中所有數(shù)據(jù)的整合,然后我們 docker exec 到容器里面,看到一個(gè)文件系統(tǒng)其實(shí)就是 mergedir 統(tǒng)一視圖層。

文件操作

接下來(lái)我們講一下基于 overlay 這種存儲(chǔ),怎么對(duì)容器里面的文件進(jìn)行操作?

怎樣深入剖析Linux容器

先看一下讀操作,容器剛創(chuàng)建出來(lái)的時(shí)候,upper 其實(shí)是空的。這個(gè)時(shí)候如果去讀的話,所有數(shù)據(jù)都是從 lower 層讀來(lái)的。

寫操作如剛才所提到的,overlay 的 upper 層有一個(gè)寫實(shí)數(shù)據(jù)的機(jī)制,對(duì)一些文件需要進(jìn)行操作的時(shí)候,overlay 會(huì)去做一個(gè) copy up 的動(dòng)作,然后會(huì)把文件從 lower 層拷貝上來(lái),之后的一些寫修改都會(huì)對(duì)這個(gè)部分進(jìn)行操作。

然后看一下刪除操作,overlay 里面其實(shí)是沒有真正的刪除操作的。它所謂的刪除其實(shí)是通過(guò)對(duì)文件進(jìn)行標(biāo)記,然后從最上層的統(tǒng)一視圖層去看,看到這個(gè)文件如果做標(biāo)記,就會(huì)讓這個(gè)文件顯示出來(lái),然后就認(rèn)為這個(gè)文件是被刪掉的。這個(gè)標(biāo)記有兩種方式:

  • 一種是 whiteout 的方式;

  • 第二個(gè)就是通過(guò)設(shè)置目錄的一個(gè)擴(kuò)展權(quán)限,通過(guò)設(shè)置擴(kuò)展參數(shù)來(lái)做到目錄的刪除。

操作步驟

接下來(lái)看一下實(shí)際用 docker run 去啟動(dòng) busybox 的容器,它的 overlay 的掛載點(diǎn)是什么樣子的?

怎樣深入剖析Linux容器

第二張圖是 mount,可以看到這個(gè)容器 rootfs 的一個(gè)掛載,它是一個(gè) overlay 的 type 作為掛載的。里面包括了 upper、lower 還有 workdir 這三個(gè)層級(jí)。

然后看一下容器里面新文件的寫入。docker exec 去創(chuàng)建一個(gè)新文件,diff 這個(gè)從上面可以看到,是它的一個(gè) upperdir。再看 upperdir 里面有這個(gè)文件,文件里面的內(nèi)容也是 docker exec 寫入的。

最后看一下最下面的是 mergedir,mergedir 里面整合的 upperdir 和 lowerdir 的內(nèi)容,也可以看到我們寫入的數(shù)據(jù)。

三、容器引擎

containerd 容器架構(gòu)詳解

接下來(lái)我們基于 CNCF 的一個(gè)容器引擎上的 containerd,來(lái)講一下容器引擎大致的構(gòu)成。下圖是從 containerd 官網(wǎng)拿過(guò)來(lái)的一張架構(gòu)圖,基于這張架構(gòu)圖先簡(jiǎn)單介紹一下 containerd 的架構(gòu)。

怎樣深入剖析Linux容器

上圖如果把它分成左右兩邊的話,可以認(rèn)為 containerd 提供了兩大功能。

第一個(gè)是對(duì)于 runtime,也就是對(duì)于容器生命周期的管理,左邊 storage 的部分其實(shí)是對(duì)一個(gè)鏡像存儲(chǔ)的管理。containerd 會(huì)負(fù)責(zé)進(jìn)行的拉取、鏡像的存儲(chǔ)。

按照水平層次來(lái)看的話:

  • 第一層是 GRPC,containerd 對(duì)于上層來(lái)說(shuō)是通過(guò) GRPC serve 的形式來(lái)對(duì)上層提供服務(wù)的。Metrics 這個(gè)部分主要是提供 cgroup Metrics 的一些內(nèi)容;

  • 下面這層的左邊是容器鏡像的一個(gè)存儲(chǔ),中線 images、containers 下面是 Metadata,這部分 Matadata 是通過(guò) bootfs 存儲(chǔ)在磁盤上面的。右邊的 Tasks 是管理容器的容器結(jié)構(gòu),Events 是對(duì)容器的一些操作都會(huì)有一個(gè) Event 向上層發(fā)出,然后上層可以去訂閱這個(gè) Event,由此知道容器狀態(tài)發(fā)生什么變化;

  • 最下層是 Runtimes 層,這個(gè) Runtimes 可以從類型區(qū)分,比如說(shuō) runC 或者是安全容器之類的。

shim v1/v2 是什么

接下來(lái)講一下 containerd 在 runtime 這邊的大致架構(gòu)。下面這張圖是從 kata 官網(wǎng)拿過(guò)來(lái)的,上半部分是原圖,下半部分加了一些擴(kuò)展示例,基于這張圖我們來(lái)看一下 containerd 在 runtime 這層的架構(gòu)。

怎樣深入剖析Linux容器

如圖所示:按照從左往右的一個(gè)順序,從上層到最終 runtime 運(yùn)行起來(lái)的一個(gè)流程。

我們先看一下最左邊,最左邊是一個(gè) CRI Client。一般就是 kubelet 通過(guò) CRI 請(qǐng)求,向 containerd 發(fā)送請(qǐng)求。containerd 接收到容器的請(qǐng)求之后,會(huì)經(jīng)過(guò)一個(gè) containerd shim。containerd shim 是管理容器生命周期的,它主要負(fù)責(zé)兩方面:

  • 第一個(gè)是它會(huì)對(duì) io 進(jìn)行轉(zhuǎn)發(fā);

  • 第二是它會(huì)對(duì)信號(hào)進(jìn)行傳遞。

圖的上半部分畫的是安全容器,也就是 kata 的一個(gè)流程,這個(gè)就不具體展開了。下半部分,可以看到有各種各樣不同的 shim。下面介紹一下 containerd shim 的架構(gòu)。

一開始在 containerd 中只有一個(gè) shim,也就是藍(lán)色框框起來(lái)的 containerd-shim。這個(gè)進(jìn)程的意思是,不管是 kata 容器也好、runc 容器也好、gvisor 容器也好,上面用的 shim 都是 containerd。

后面針對(duì)不同類型的 runtime,containerd 去做了一個(gè)擴(kuò)展。這個(gè)擴(kuò)展是通過(guò) shim-v2 這個(gè) interface 去做的,也就是說(shuō)只要去實(shí)現(xiàn)了這個(gè) shim-v2 的 interface,不同的 runtime 就可以定制不同的 shim。比如:runC 可以自己做一個(gè) shim,叫 shim-runc;gvisor 可以自己做一個(gè) shim 叫 shim-gvisor;像上面 kata 也可以自己去做一個(gè) shim-kata 的 shim。這些 shim 可以替換掉上面藍(lán)色框的 containerd-shim。

這樣做的好處有很多,舉一個(gè)比較形象的例子??梢钥匆幌?kata 這張圖,它上面原先如果用 shim-v1 的話其實(shí)有三個(gè)組件,之所以有三個(gè)組件的原因是因?yàn)?kata 自身的一個(gè)限制,但是用了 shim-v2 這個(gè)架構(gòu)后,三個(gè)組件可以做成一個(gè)二進(jìn)制,也就是原先三個(gè)組件,現(xiàn)在可以變成一個(gè) shim-kata 組件,這個(gè)可以體現(xiàn)出 shim-v2 的一個(gè)好處。

containerd 容器架構(gòu)詳解 - 容器流程示例

接下來(lái)我們以兩個(gè)示例來(lái)詳細(xì)解釋一下容器的流程是怎么工作的,下面的兩張圖是基于 containerd 的架構(gòu)畫的一個(gè)容器的工作流程。

start 流程

先看一下容器 start 的流程:

怎樣深入剖析Linux容器

這張圖由三個(gè)部分組成:

  • 第一個(gè)部分是容器引擎部分,容器引擎可以是 docker,也可以是其它的;

  • 兩個(gè)虛線框框起來(lái)的 containerd 和 containerd-shim,它們兩個(gè)是屬于 containerd 架構(gòu)的部分;

  • 最下面就是 container 的部分,這個(gè)部分是通過(guò)一個(gè) runtime 去拉起的,可以認(rèn)為是 shim 去操作 runC 命令創(chuàng)建的一個(gè)容器。

先看一下這個(gè)流程是怎么工作的,圖里面也標(biāo)明了 1、2、3、4。這個(gè) 1、2、3、4 就是 containerd 怎么去創(chuàng)建一個(gè)容器的流程。

首先它會(huì)去創(chuàng)建一個(gè) matadata,然后會(huì)去發(fā)請(qǐng)求給 task service 說(shuō)要去創(chuàng)建容器。通過(guò)中間一系列的組件,最終把請(qǐng)求下發(fā)到一個(gè) shim。containerd 和 shim 的交互其實(shí)也是通過(guò) GRPC 來(lái)做交互的,containerd 把創(chuàng)建請(qǐng)求發(fā)給 shim 之后,shim 會(huì)去調(diào)用 runtime 創(chuàng)建一個(gè)容器出來(lái),以上就是容器 start 的一個(gè)示例。

exec 流程

接下來(lái)看下面這張圖是怎么去 exec 一個(gè)容器的。
和 start 流程非常相似,結(jié)構(gòu)也大概相同,不同的部分其實(shí)就是 containerd 怎么去處理這部分流程。和上面的圖一樣,我也在圖中標(biāo)明了 1、2、3、4,這些步驟就代表了 containerd 去做 exec 的一個(gè)先后順序。

怎樣深入剖析Linux容器

由上圖可以看到:exec 的操作還是發(fā)給 containerd-shim 的。對(duì)容器來(lái)說(shuō),去 start 一個(gè)容器和去 exec 一個(gè)容器,其實(shí)并沒有本質(zhì)的區(qū)別。

最終的一個(gè)區(qū)別無(wú)非就是:是否對(duì)容器中跑的進(jìn)程做一個(gè) namespace 的創(chuàng)建。

  • exec 的時(shí)候,需要把這個(gè)進(jìn)程加入到一個(gè)已有的 namespace 里面;

  • start 的時(shí)候,容器進(jìn)程的 namespace 是需要去專門創(chuàng)建。

這里為大家簡(jiǎn)單總結(jié)一下本文的內(nèi)容:

  1. 容器如何用 namespace 做資源隔離以及 cgroup 做資源限制;

  2. 簡(jiǎn)單介紹了基于 overlay 文件系統(tǒng)的容器鏡像存儲(chǔ);

  3. 以 docker+containerd 為例介紹了容器引擎如何工作的。

關(guān)于怎樣深入剖析Linux容器問(wèn)題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識(shí)。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI