轉載請註明出處:葡萄城官網,葡萄城為開發者提供專業的開發工具、解決方案和服務,賦能開發者。
上次我們說到PaaS的發展歷史,從Cloud Foundry黯然退場,到Docker加冕,正是Docker“一點點”的改進,掀起了一場蝴蝶效應,煽動了整個PaaS開源專案市場風起雲湧。
為了讓大家更好的理解“容器”這個PaaS中最核心的技術,本篇將從一個程式開始,為大家講述容器到底是什麼,Cloud Foundry等PaaS“前浪”是如何實現容器的。
程式 vs 容器
以Linux作業系統為例,計算機裡執行的程式是程式執行之後,從磁碟的二進位制檔案,到記憶體、暫存器、堆疊指令等等所用到的相關裝置狀態的一個集合,是資料和狀態綜合的動態表現。而容器技術的目標就是對一個程式的狀態和資料進行的隔離和限制。可以說,容器的本質其實就是Linux中的一個特殊程式。這個特殊的程式,主要靠Linux系統提供的兩個機制來實現,這裡先回顧一下。
Namespace
Linux Namespace是Linux核心的一項功能,該功能對核心資源進行分割槽,以使一組程式看到一組資源,而另一組程式看到另一組資源。該功能通過為一組資源和程式具有相同的名稱空間而起作用,但是這些名稱空間引用了不同的資源。資源可能存在於多個空間中。此類資源的示例是程式ID,主機名,使用者ID,檔名以及與網路訪問和程式間通訊相關的某些名稱。其種類列舉如下:
- Mount namespaces
- UTS namespaces
- IPC namespaces
- PID namespaces
- Network namespaces
- User namespaces
超級程式
在Linux作業系統中,PID==1的程式被稱為超級程式,它是整個程式樹的root,負責產生其他所有使用者程式。所有的程式都會被掛在這個程式下,如果這個程式退出了,那麼所有的程式都被 kill。
隔離 & 限制
剛才我們提到了隔離和限制,具體指的是什麼呢?
隔離
以Docker為例(Cloud Foundry同理,我的機器上沒有安裝後者),我們可以執行下列的命令建立一個簡單的映象:
$ docker run -it busybox /bin/sh
這條語句執行的內容是:用docker執行一個容器,容器的映象名稱叫busybox,並且執行之後需要執行的命令是/bin/sh,而-it參數列示需要使用標準輸入stdin和分配一個文字輸入輸出環境tty與外部互動。通過這個命令,我們就可以進入到一個容器內部了,分別在容器中和宿主機中執行top命令,可以看到以下結果:
(在容器內外執行top語句的返回結果)
從中可以發現,容器中的執行程式只剩下了兩個。一個是主程式PID==1的/bin/sh超級程式,另一個是我們執行的top。而宿主機中的其餘的所有程式在容器中都看不到了——這就是隔離。
(被隔離的top程式,圖片來自網路)
原本,每當我們在宿主機上執行一個/bin/sh程式,作業系統都會給它分配一個程式編號,比如PID100。而現在,我們要通過Docker把這個/bin/sh程式執行在一個容器中,這時候,Docker就會在這個PID100建立時施加一個“障眼法”,讓他永遠看不到之前的99個程式,這樣執行在容器中的程式就會當自己是PID==1的超級程式。
而這種機制,其實就是對被隔離的程式的程式空間做了手腳,雖然在容器中顯示的PID1,但是在原本的宿主機中,它其實還是那個PID100的程式。所使用到的技術就是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功能。
(被cgroup限制的程式,圖片來自網路)
在Linux系統(Ubuntu)中可以執行以下命令檢視CgroupsAPI檔案:
mount -t Cgroups
(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:
(預設的CPU資原始檔列表)
為了展示CPU限制的實際效果,讓我們執行一個用以下指令碼建立的死迴圈:
while : ; do : ; done &
我們在top命令結果中會看到返回的程式為398,因為死迴圈,cpu佔用率為100%:
(死迴圈的程式佔了100% CPU)
這時,我們再看下container目錄下的cpu.cfs_quota_us和cpu.cfs_period_us:
(預設情況下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使用資源限制開始生效。
(使用cgroup限制CPU使用量的死迴圈程式)
以上,就是通過Cgroups功能對容器做限制的原理了。同理,我們可以用此方法,對一個程式的記憶體、頻寬等做限制,如果這個程式是一個容器程式,一個資源受控的容器基本就可以展現在你面前了事實上,在雲時代的早期,Cloud Foundry等“前浪”都是採用這種方式建立和管理容器。相比於後來者,Cloud Foundry等在容器的隔離和限制上,雖相對簡單、易於理解,但在一些場景下難免會受到制約。
這裡要做一個特別的說明,只有Linux中執行的容器是通過對程式進行限制模擬出來的結果,Windows和Mac下的容器,都是通過Docker Desktop等容器軟體,操作虛擬機器模擬出來的“真實”的虛擬容器。
小結
本節從容器的原理和Linux下實現容器隔離和限制的技術入手,介紹了在雲時代早期Cloud Foundry等Paas平臺的容器原理。下一節將繼續為大家介紹Docker在Cloud Foundry容器基礎之上又做了什麼改動,是如何解決Cloud Foundry致命短板的。
如果您想了解Docker如何攪動風雲,Docker的這個容器又和傳統虛擬機器有何區別?
敬請期待下篇,我們繼續嘮。