Docker容器實(shí)戰(zhàn)(六) - 容器的隔離與限制

Linux容器中用來實(shí)現(xiàn)“隔離”的技術(shù)手段:Namespace。
Namespace實(shí)際上修改了應(yīng)用進(jìn)程看待整個(gè)計(jì)算機(jī)“視圖”,即它的“視線”被操作系統(tǒng)做了限制,只能“看到”某些指定的內(nèi)容。對(duì)于宿主機(jī)來說,這些被“隔離”了的進(jìn)程跟其他進(jìn)程并沒有區(qū)別。

在之前虛擬機(jī)與容器技術(shù)的對(duì)比圖里,不應(yīng)該把Docker Engine或者任何容器管理工具放在跟Hypervisor相同的位置,因?yàn)樗鼈儾⒉幌馠ypervisor那樣對(duì)應(yīng)用進(jìn)程的隔離環(huán)境負(fù)責(zé),也不會(huì)創(chuàng)建任何實(shí)體的“容器”,真正對(duì)隔離環(huán)境負(fù)責(zé)的是宿主機(jī)操作系統(tǒng)本身:


在這個(gè)對(duì)比圖里,應(yīng)該把Docker畫在跟應(yīng)用同級(jí)別并且靠邊的位置。
用戶運(yùn)行在容器里的應(yīng)用進(jìn)程,跟宿主機(jī)上的其他進(jìn)程一樣,都由宿主機(jī)操作系統(tǒng)統(tǒng)一管理,只不過這些被隔離的進(jìn)程擁有額外設(shè)置過的Namespace參數(shù)
Docker在這里更多的是輔助和管理工作。

這樣的架構(gòu)也解釋了為什么Docker項(xiàng)目比虛擬機(jī)更受歡迎的原因。

使用虛擬化技術(shù)作為應(yīng)用沙盒,就必須要由Hypervisor來負(fù)責(zé)創(chuàng)建虛擬機(jī),這個(gè)虛擬機(jī)是真實(shí)存在的,它里面必須運(yùn)行一個(gè)完整的Guest OS才能執(zhí)行用戶的應(yīng)用進(jìn)程。這就不可避免地帶來了額外的資源消耗和占用。

根據(jù)實(shí)驗(yàn),一個(gè)運(yùn)行著CentOS的KVM虛擬機(jī)啟動(dòng)后,在不做優(yōu)化的情況下,虛擬機(jī)自己就需要占用100~200 MB內(nèi)存。此外,用戶應(yīng)用運(yùn)行在虛擬機(jī)里面,它對(duì)宿主機(jī)操作系統(tǒng)的調(diào)用就不可避免地要經(jīng)過虛擬化軟件的攔截和處理,這本身又是一層性能損耗,尤其對(duì)計(jì)算資源、網(wǎng)絡(luò)和磁盤I/O的損耗非常大。

而容器化后的用戶應(yīng)用,依然還是宿主機(jī)上的一個(gè)普通進(jìn)程,這就意味著這些因?yàn)樘摂M化而帶來的性能損耗都是不存在的
使用Namespace作為隔離手段的容器并不需要單獨(dú)的Guest OS,這就使得容器額外的資源占用幾乎可以忽略不計(jì)。

“敏捷”和“高性能”是容器相較于虛擬機(jī)最大的優(yōu)勢(shì)

不過,有利就有弊,基于Linux Namespace的隔離機(jī)制相比于虛擬化技術(shù)也有很多不足之處,其中最主要的問題就是:

1 隔離得不徹底

1.1 多個(gè)容器之間使用的還是同一宿主機(jī)的操作系統(tǒng)內(nèi)核

盡管可以在容器里通過 Mount Namespace 單獨(dú)掛載其他不同版本的操作系統(tǒng)文件,比如 CentOS 或者 Ubuntu,但這并不能改變共享宿主機(jī)內(nèi)核的事實(shí)!
這代表如果要在Windows宿主機(jī)上運(yùn)行Linux容器,或者在低版本的Linux宿主機(jī)上運(yùn)行高版本的Linux容器,都是impossible!

相比之下,擁有硬件虛擬化技術(shù)和獨(dú)立Guest OS的虛擬機(jī)就要方便
最極端的例子是,Microsoft的云計(jì)算平臺(tái)Azure,實(shí)際上就是運(yùn)行在Windows服務(wù)器集群上的,但這并不妨礙你在它上面創(chuàng)建各種Linux虛擬機(jī)

1.2 Linux內(nèi)核中很多資源和對(duì)象是不能被Namespace化的

最典型的例子:時(shí)間

如果你的容器中的程序使用settimeofday(2)系統(tǒng)調(diào)用修改了時(shí)間,整個(gè)宿主機(jī)的時(shí)間都會(huì)被隨之修改,這顯然不符合用戶的預(yù)期
相比于在虛擬機(jī)里面可以隨便折騰,在容器里部署應(yīng)用的時(shí)候,“什么能做,什么不能做”,都是用戶必須考慮的問題。

此外,由于上述問題,尤其是共享宿主機(jī)內(nèi)核的事實(shí)

1.3 容器給應(yīng)用暴露出來的攻擊面是相當(dāng)大的

應(yīng)用“越獄”的難度自然也比虛擬機(jī)低得多。

盡管可以使用Seccomp等技術(shù),過濾和甄別容器內(nèi)部發(fā)起的所有系統(tǒng)調(diào)用來進(jìn)行安全加固,但這就多了一層對(duì)系統(tǒng)調(diào)用的過濾,一定會(huì)拖累容器的性能。何況,默認(rèn)情況下,誰也不知道到底該開啟哪些系統(tǒng)調(diào)用,禁止哪些系統(tǒng)調(diào)用。

所以,在生產(chǎn)環(huán)境中,沒有人敢把運(yùn)行在物理機(jī)上的Linux容器直接暴露到公網(wǎng)上。

基于虛擬化或者獨(dú)立內(nèi)核技術(shù)的容器實(shí)現(xiàn),則可以比較好地在隔離與性能之間做出平衡。

2 限制容器

Linux Namespace創(chuàng)建了一個(gè)“容器”,為什么還要對(duì)容器做“限制”呢?

以PID Namespace為例

雖然容器內(nèi)的第1號(hào)進(jìn)程在“障眼法”的干擾下只能看到容器里的情況,但是宿主機(jī)上,它作為第100號(hào)進(jìn)程與其他所有進(jìn)程之間依然是平等的競(jìng)爭(zhēng)關(guān)系。
這就意味著,雖然第100號(hào)進(jìn)程表面上被隔離了起來,但是它所能夠使用到的資源(比如CPU、內(nèi)存),卻可隨時(shí)被宿主機(jī)上其他進(jìn)程(或容器)占用的。當(dāng)然,這個(gè)100號(hào)進(jìn)程自己也可能把所有資源吃光。這些情況,顯然都不是一個(gè)“沙盒”應(yīng)該表現(xiàn)出來的合理行為。

Linux Cgroups就是Linux內(nèi)核中用來為進(jìn)程設(shè)置資源限制的一個(gè)重要功能。

Google的工程師在2006年發(fā)起這項(xiàng)特性的時(shí)候,曾將它命名為“進(jìn)程容器”(process container)。實(shí)際上,在Google內(nèi)部,“容器”這個(gè)術(shù)語長(zhǎng)期以來都被用于形容被Cgroups限制過的進(jìn)程組。后來Google的工程師們說,他們的KVM虛擬機(jī)也運(yùn)行在Borg所管理的“容器”里,其實(shí)也是運(yùn)行在Cgroups“容器”當(dāng)中。這和我們今天說的Docker容器差別很大。

Linux Cgroups的全稱是Linux Control Group。它最主要的作用,就是限制一個(gè)進(jìn)程組能夠使用的資源上限,包括CPU、內(nèi)存、磁盤、網(wǎng)絡(luò)帶寬等等。
此外,Cgroups還能夠?qū)M(jìn)程進(jìn)行優(yōu)先級(jí)設(shè)置、審計(jì),以及將進(jìn)程掛起和恢復(fù)等操作。只探討它與容器關(guān)系最緊密的“限制”能力,并通過一組實(shí)踐來認(rèn)識(shí)一下Cgroups。

在Linux中,Cgroups給用戶暴露出來的操作接口是文件系統(tǒng),即它以文件和目錄的方式組織在操作系統(tǒng)的/sys/fs/cgroup路徑下

  • 在筆者的 CentOS7 VM里,可以用mount指令把它們展示出來

    它的輸出結(jié)果,是一系列文件系統(tǒng)目錄(如果你在自己的機(jī)器上沒有看到這些目錄,那你就需要自己去掛載Cgroups)

在/sys/fs/cgroup下面有很多諸如cpuset、cpu、 memory這樣的子目錄,也叫子系統(tǒng)
這些都是我這臺(tái)機(jī)器當(dāng)前可以被Cgroups進(jìn)行限制的資源種類。

而在子系統(tǒng)對(duì)應(yīng)的資源種類下,你就可以看到該類資源具體可以被限制的方法。

  • 譬如,對(duì)CPU子系統(tǒng)來說,就可以看到如下配置文件

    注意到cfs_period和cfs_quota這樣的關(guān)鍵詞,這兩個(gè)參數(shù)需要組合使用,可用來
    限制進(jìn)程在長(zhǎng)度為cfs_period的一段時(shí)間內(nèi),只能被分配到總量為cfs_quota的CPU時(shí)間

這樣的配置文件如何使用呢?

需要在對(duì)應(yīng)的子系統(tǒng)下面創(chuàng)建一個(gè)目錄
比如,我們現(xiàn)在進(jìn)入/sys/fs/cgroup/cpu目錄下:



這個(gè)目錄就稱為一個(gè)“控制組”。
OS會(huì)在你新創(chuàng)建的container目錄下,自動(dòng)生成該子系統(tǒng)對(duì)應(yīng)的資源限制文件!

現(xiàn)在,我們?cè)诤笈_(tái)執(zhí)行這樣一條腳本:



顯然,它執(zhí)行了一個(gè)死循環(huán),可以把計(jì)算機(jī)的CPU吃到100%,根據(jù)它的輸出,我們可以看到這個(gè)腳本在后臺(tái)運(yùn)行的進(jìn)程號(hào)(PID)

于是,可以用top指令來確認(rèn)一下CPU有沒有被打滿:



在輸出里可以看到,CPU的使用率已經(jīng)100%了(%Cpu0 :100.0 us)。

而此時(shí),我們可以通過查看container目錄下的文件,看到container控制組里的CPU quota還沒有任何限制(即:-1),CPU period則是默認(rèn)的100 ms(100000 us):




接下來,我們可以通過修改這些文件的內(nèi)容來設(shè)置限制。

比如,向container組里的cfs_quota文件寫入20 ms(20000 us):



結(jié)合前面的介紹,你應(yīng)該能明白這個(gè)操作的含義,它意味著在每100 ms的時(shí)間里,被該控制組限制的進(jìn)程只能使用20 ms的CPU時(shí)間,也就是說這個(gè)進(jìn)程只能使用到20%的CPU帶寬。

接下來,我們把被限制的進(jìn)程的PID寫入container組里的tasks文件,上面的設(shè)置就會(huì)對(duì)該進(jìn)程生效了:



我們可以用top指令查看一下:



可以看到,計(jì)算機(jī)的CPU使用率立刻降到了20%

除CPU子系統(tǒng)外,Cgroups的每一項(xiàng)子系統(tǒng)都有其獨(dú)有的資源限制能力,比如:

  • blkio,為???塊???設(shè)???備???設(shè)???定???I/O限???制,一般用于磁盤等設(shè)備
  • cpuset,為進(jìn)程分配單獨(dú)的CPU核和對(duì)應(yīng)的內(nèi)存節(jié)點(diǎn)
  • memory,為進(jìn)程設(shè)定內(nèi)存使用的限制

Linux Cgroups 就是一個(gè)子系統(tǒng)目錄加上一組資源限制文件的組合
而對(duì)于Docker等Linux容器項(xiàng)目來說,只需在每個(gè)子系統(tǒng)下面,為每個(gè)容器創(chuàng)建一個(gè)控制組(即創(chuàng)建一個(gè)新目錄),然后在啟動(dòng)容器進(jìn)程之后,把這個(gè)進(jìn)程的PID填寫到對(duì)應(yīng)控制組的tasks文件中!

而至于在這些控制組下面的資源文件里填上什么值,就靠用戶執(zhí)行docker run時(shí)的參數(shù)指定了,比如這樣一條命令:

$ docker run -it --cpu-period=100000 --cpu-quota=20000 ubuntu /bin/bash

在啟動(dòng)這個(gè)容器后,我們可以通過查看Cgroups文件系統(tǒng)下,CPU子系統(tǒng)中,“docker”這個(gè)控制組里的資源限制文件的內(nèi)容來確認(rèn):

$ cat /sys/fs/cgroup/cpu/docker/5d5c9f67d/cpu.cfs_period_us 
100000
$ cat /sys/fs/cgroup/cpu/docker/5d5c9f67d/cpu.cfs_quota_us 
20000

這就意味著這個(gè)Docker容器,只能使用到20%的CPU帶寬。

3 總結(jié)

首先介紹了容器使用Linux Namespace作為隔離手段的優(yōu)勢(shì)和劣勢(shì),對(duì)比了Linux容器跟虛擬機(jī)技術(shù)的不同,進(jìn)一步明確了“容器只是一種特殊的進(jìn)程”這個(gè)結(jié)論。

除了創(chuàng)建Namespace之外,在后續(xù)還會(huì)介紹一些其他Namespace的操作,比如看不見摸不著的Linux Namespace在計(jì)算機(jī)中到底如何表示、一個(gè)進(jìn)程如何“加入”到其他進(jìn)程的Namespace當(dāng)中,等等。

緊接著詳細(xì)介紹了容器在做好了隔離工作之后,又如何通過Linux Cgroups實(shí)現(xiàn)資源的限制,并通過一系列簡(jiǎn)單的實(shí)驗(yàn),模擬了Docker項(xiàng)目創(chuàng)建容器限制的過程。

現(xiàn)在應(yīng)該能夠理解,一個(gè)正在運(yùn)行的Docker容器,其實(shí)就是一個(gè)啟用了多個(gè)Linux Namespace的應(yīng)用進(jìn)程,而這個(gè)進(jìn)程能夠使用的資源量,則受Cgroups配置的限制。

這也是容器技術(shù)中一個(gè)非常重要的概念,即:容器是一個(gè)“單進(jìn)程”模型

由于一個(gè)容器的本質(zhì)就是一個(gè)進(jìn)程,用戶的應(yīng)用進(jìn)程實(shí)際上就是容器里PID=1的進(jìn)程,也是其他后續(xù)創(chuàng)建的所有進(jìn)程的父進(jìn)程。
這就意味著,在一個(gè)容器中,你沒辦法同時(shí)運(yùn)行兩個(gè)不同的應(yīng)用,除非你能事先找到一個(gè)公共的PID=1的程序來充當(dāng)兩個(gè)不同應(yīng)用的父進(jìn)程,這也是為什么很多人都會(huì)用systemd或者supervisord這樣的軟件來代替應(yīng)用本身作為容器的啟動(dòng)進(jìn)程。

但是,在后面分享容器設(shè)計(jì)模式時(shí),我還會(huì)推薦其他更好的解決辦法。這是因?yàn)槿萜鞅旧淼脑O(shè)計(jì),就是希望容器和應(yīng)用能夠同生命周期,這個(gè)概念對(duì)后續(xù)的容器編排非常重要。否則,一旦出現(xiàn)類似于“容器是正常運(yùn)行的,但是里面的應(yīng)用早已經(jīng)掛了”的情況,編排系統(tǒng)處理起來就非常麻煩了。

另外,跟Namespace的情況類似,Cgroups對(duì)資源的限制能力也有很多不完善的地方,被提及最多的自然是/proc文件系統(tǒng)的問題。
Linux下的/proc目錄存儲(chǔ)的是記錄當(dāng)前內(nèi)核運(yùn)行狀態(tài)的一系列特殊文件,用戶可以通過訪問這些文件,查看系統(tǒng)以及當(dāng)前正在運(yùn)行的進(jìn)程的信息,比如CPU使用情況、內(nèi)存占用率等,這些文件也是top指令查看系統(tǒng)信息的主要數(shù)據(jù)來源。

但是如果在容器里執(zhí)行top指令,就會(huì)發(fā)現(xiàn),它顯示的信息居然是宿主機(jī)的CPU和內(nèi)存數(shù)據(jù),而不是當(dāng)前容器的數(shù)據(jù)。
造成這個(gè)問題的原因就是,/proc文件系統(tǒng)并不知道用戶通過Cgroups給這個(gè)容器做了什么樣的資源限制,即:/proc文件系統(tǒng)不了解Cgroups限制的存在。

在生產(chǎn)環(huán)境中,這個(gè)問題必須進(jìn)行修正,否則應(yīng)用程序在容器里讀取到的CPU核數(shù)、可用內(nèi)存等信息都是宿主機(jī)上的數(shù)據(jù),這會(huì)給應(yīng)用的運(yùn)行帶來非常大的困惑和風(fēng)險(xiǎn)。
這也是在企業(yè)中,容器化應(yīng)用碰到的一個(gè)常見問題,也是容器相較于虛擬機(jī)另一個(gè)不盡如人意的地方

參考

  • Docker官網(wǎng)
  • Docker實(shí)戰(zhàn)
  • 深入剖析Kubernetes
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Docker容器技術(shù)已經(jīng)發(fā)展了好些年,在很多項(xiàng)目都有應(yīng)用,線上運(yùn)行也很穩(wěn)定。整理了部分Docker的學(xué)習(xí)筆記以及新...
    __七把刀__閱讀 11,639評(píng)論 0 58
  • 一、Docker 簡(jiǎn)介 Docker 兩個(gè)主要部件:Docker: 開源的容器虛擬化平臺(tái)Docker Hub: 用...
    R_X閱讀 4,523評(píng)論 0 27
  • 1. 容器 1.1 定義 一種沙盒技術(shù),可以將應(yīng)用運(yùn)行在其中,與外界隔離這個(gè)沙盒可以被方便地“轉(zhuǎn)移”。 本質(zhì)上,他...
    小劉要學(xué)習(xí)閱讀 4,358評(píng)論 0 1
  • 你好,我是張磊。今天我和你分享的主題是:白話容器基礎(chǔ)之從進(jìn)程說開去。 在前面的 4 篇預(yù)習(xí)文章中,我梳理了“容器”...
    脆皮雞大蝦閱讀 1,167評(píng)論 0 1
  • 真的是一道很難的選擇題嗎,現(xiàn)在的工作已經(jīng)駕輕就熟了,工作一年多,有些疲憊了,不知道是不是應(yīng)該繼續(xù)堅(jiān)持下去,應(yīng)該在...
    劃船去找企鵝閱讀 166評(píng)論 0 0

友情鏈接更多精彩內(nèi)容