Docker與k8s的恩怨情仇(二)—用最簡單的技術實現「容器」

轉載請註明出處:葡萄城官網,葡萄城爲開發者提供專業的開發工具、解決方案和服務,賦能開發者。

上次咱們說到PaaS的發展歷史,從Cloud Foundry黯然退場,到Docker加冕,正是Docker「一點點」的改進,掀起了一場蝴蝶效應,煽動了整個PaaS開源項目市場風起雲涌。程序員

爲了讓你們更好的理解「容器」這個PaaS中最核心的技術,本篇將從一個進程開始,爲你們講述容器究竟是什麼,Cloud Foundry等PaaS「前浪」是如何實現容器的。docker

進程 vs 容器

以Linux操做系統爲例,計算機裏運行的進程是程序執行以後,從磁盤的二進制文件,到內存、寄存器、堆棧指令等等所用到的相關設備狀態的一個集合,是數據和狀態綜合的動態表現。而容器技術的目標就是對一個進程的狀態和數據進行的隔離和限制。能夠說,容器的本質其實就是Linux中的一個特殊進程。這個特殊的進程,主要靠Linux系統提供的兩個機制來實現,這裏先回顧一下。網絡

Namespace

Linux Namespace是Linux內核的一項功能,該功能對內核資源進行分區,以使一組進程看到一組資源,而另外一組進程看到另外一組資源。該功能經過爲一組資源和進程具備相同的名稱空間而起做用,可是這些名稱空間引用了不一樣的資源。資源可能存在於多個空間中。此類資源的示例是進程ID,主機名,用戶ID,文件名以及與網絡訪問和進程間通訊相關的某些名稱。其種類列舉以下:函數

  1. Mount namespaces
  2. UTS namespaces
  3. IPC namespaces
  4. PID namespaces
  5. Network namespaces
  6. User namespaces

超級進程

在Linux操做系統中,PID==1的進程被稱爲超級進程,它是整個進程樹的root,負責產生其餘全部用戶進程。全部的進程都會被掛在這個進程下,若是這個進程退出了,那麼全部的進程都被 kill。工具

隔離 & 限制

剛纔咱們提到了隔離和限制,具體指的是什麼呢?開發工具

隔離

以Docker爲例(Cloud Foundry同理,個人機器上沒有安裝後者),咱們能夠執行下列的命令建立一個簡單的鏡像:
$ docker run -it busybox /bin/shgoogle

這條語句執行的內容是:用docker運行一個容器,容器的鏡像名稱叫busybox,而且運行以後須要執行的命令是/bin/sh,而-it參數表示須要使用標準輸入stdin和分配一個文本輸入輸出環境tty與外部交互。經過這個命令,咱們就能夠進入到一個容器內部了,分別在容器中和宿主機中執行top命令,能夠看到如下結果:spa

7.png

(在容器內外執行top語句的返回結果)操作系統

從中能夠發現,容器中的運行進程只剩下了兩個。一個是主進程PID==1的/bin/sh超級進程,另外一個是咱們運行的top。而宿主機中的其他的全部進程在容器中都看不到了——這就是隔離。線程

5.jpg

(被隔離的top進程,圖片來自網絡)

本來,每當咱們在宿主機上運行一個/bin/sh程序,操做系統都會給它分配一個進程編號,好比PID==100。而如今,咱們要經過Docker把這個/bin/sh程序運行在一個容器中,這時候,Docker就會在這個PID==100建立時施加一個「障眼法」,讓他永遠看不到以前的99個進程,這樣運行在容器中的程序就會當本身是PID==1的超級進程。

而這種機制,其實就是對被隔離的程序的進程空間作了手腳,雖然在容器中顯示的PID==1,可是在本來的宿主機中,它其實仍是那個PID==100的進程。所使用到的技術就是Linux中的Namespace機制。而這個機制,其實就是Linux在建立進程時的一個可選參數。在Linux中,建立一個線程的函數是(這裏沒寫錯就是線程,Linux中線程是用進程實現的,因此能夠用來描述進程):

int pid = clone(main_function, stack_size, SIGCHLD, NULL);

若是咱們給這個方法添加一個參數好比CLONE_NEWPID:

int pid = clone(main_function, stack_size, CLONE_NEWPID | SIGCHLD, NULL);

那麼這個新的進程就會看到一個全新的進程空間,在這個空間裏,由於該空間中僅有這一個進程,因此它本身的PID就等於1了。

這樣一個過程就是Linux容器最基本的隔離實現了。

限制

光有namespace隔離的容器就和沒有電腦的程序員同樣,是殘缺不全的。

若是咱們只隔離不限制,籠子裏面的程序照樣佔用系統資源,訪問依舊自由。爲了給有了隔離性的程序添加資源限制,就用到了第二個技術:cgroups

cgroups原本是google的工程師在2006年開發的一個程序,全稱是Linux Control Group,是Linux操做系統中用來限制一個進程組能使用資源的上限,包括CPU、內存、磁盤、網絡帶寬等的功能。

經過Cgroups給用戶暴露的API文件系統,用戶能夠經過修改文件的值來操做Cgroups功能。

8.png

(被cgroup限制的進程,圖片來自網絡)

在Linux系統(Ubuntu)中能夠執行如下命令查看CgroupsAPI文件:

mount -t Cgroups

9.png

(cgroup文件系統)

從上圖能夠看到,系統中存在包括cpu、內存、IO等多個Cgroups配置文件。

咱們以CPU爲例來講明如下Cgroups這個功能。對CPU的限制須要引入兩個參數cfs_period和cfs_quota,咱們爲了給活字格公有云Docker內的程序限制CPU時,會常常操做這兩個參數。這兩個參數是組合使用的,意思是在長度爲cfs_period時間內,程序組只能分到總量爲cfs_quota的CPU時間。也就是說cfs_quota / cfs_period == cpu使用上限。

要想限制某個進程的CPU使用,能夠在/sys/fs/Cgroups/cpu目錄下,執行如下命令建立一個文件夾container:

/sys/fs/Cgroups/cpu/ > mkdir container

此時,咱們能夠發現系統自動在container目錄下生成的一系列CPU限制的參數文件,這是Linux系統自動生成的,表示咱們成功爲CPU建立了一個控制組container:

10.png

(默認的CPU資源文件列表)

爲了展現CPU限制的實際效果,讓咱們執行一個用如下腳本建立的死循環:

while : ; do : ; done &

咱們在top命令結果中會看到返回的進程爲398,由於死循環,cpu佔用率爲100%:

11.png

(死循環的進程佔了100% CPU)

這時,咱們再看下container目錄下的cpu.cfs_quota_us和cpu.cfs_period_us:

12.png

(默認狀況下CPU的限制參數)
這裏是沒有作過限制時的樣子。cfs_quota_us爲-1說明並無限制CPU的運行上限。如今咱們改一下這個值:

echo 20000 > /sys/fs/Cgroups/cpu/container/cpu.cfs_quota_us

而後將以前的進程398寫入這個控制組的tasks文件中:

echo 398 > /sys/fs/Cgroups/cpu/container/tasks

這時再top一下,發現剛纔的死循環的CPU使用率變成20%了,CPU使用資源限制開始生效。

13.png

(使用cgroup限制CPU使用量的死循環進程)

以上,就是經過Cgroups功能對容器作限制的原理了。同理,咱們能夠用此方法,對一個進程的內存、帶寬等作限制,若是這個進程是一個容器進程,一個資源受控的容器基本就能夠展示在你面前了事實上,在雲時代的早期,Cloud Foundry等「前浪」都是採用這種方式建立和管理容器。相比於後來者,Cloud Foundry等在容器的隔離和限制上,雖相對簡單、易於理解,但在一些場景下不免會受到制約。

這裏要作一個特別的說明,只有Linux中運行的容器是經過對進程進行限制模擬出來的結果,Windows和Mac下的容器,都是經過Docker Desktop等容器軟件,操做虛擬機模擬出來的「真實」的虛擬容器。

小結

本節從容器的原理和Linux下實現容器隔離和限制的技術入手,介紹了在雲時代早期Cloud Foundry等Paas平臺的容器原理。下一節將繼續爲你們介紹Docker在Cloud Foundry容器基礎之上又作了什麼改動,是如何解決Cloud Foundry致命短板的。

若是您想了解Docker如何攪動風雲,Docker的這個容器又和傳統虛擬機有何區別?

敬請期待下篇,咱們繼續嘮。

相關文章
相關標籤/搜索