1. 程式人生 > 其它 >kubenetes學習筆記(2)

kubenetes學習筆記(2)

kubenetes學習筆記(2)

一、前言

    在上一篇文章中,我詳細介紹了 Linux 容器中用來實現“隔離”的技術手段:Namespace。
Namespace 技術實際上修改了應用程序看待整個計算機“檢視”,即它的“視線”被作業系統做了
限制,只能“看到”某些指定的內容。但對於宿主機來說,這些被“隔離”了的程序跟其他程序並
沒有太大區別。
    說到這一點,相信你也能夠知道我在上一篇文章最後給你留下的第一個思考題的答案了:
在之前虛擬機器與容器技術的對比圖裡,不應該把 Docker Engine 或者任何容器管理工具放在
跟 Hypervisor 相同的位置,因為它們並不像 Hypervisor 那樣對應用程序的隔離環境負責,
也不會建立任何實體的“容器”,真正對隔離環境負責的是宿主機作業系統本身:	
    所以,在這個對比圖裡,我們應該把 Docker 畫在跟應用同級別並且靠邊的位置。這意
味著,使用者執行在容器裡的應用程序,跟宿主機上的其他程序一樣,都由宿主機作業系統統
一管理,只不過這些被隔離的程序擁有額外設定過的 Namespace 引數。而 Docker 專案在
這裡扮演的角色,更多的是旁路式的輔助和管理工作。
    這樣的架構也解釋了為什麼 Docker 專案比虛擬機器更受歡迎的原因。
    這是因為,使用虛擬化技術作為應用沙盒,就必須要由 Hypervisor 來負責建立虛擬
機,這個虛擬機器是真實存在的,並且它裡面必須執行一個完整的 Guest OS 才能執行使用者
的應用程序。這就不可避免地帶來了額外的資源消耗和佔用。
    根據實驗,一個執行著 CentOS 的 KVM 虛擬機器啟動後,在不做優化的情況下,虛擬機器
自己就需要佔用 100~200  MB 記憶體。此外,使用者應用執行在虛擬機器裡面,它對宿主機操作
系統的呼叫就不可避免地要經過虛擬化軟體的攔截和處理,這本身又是一層效能損耗,尤其
對計算資源、網路和磁碟 I/O 的損耗非常大。
    而相比之下,容器化後的使用者應用,卻依然還是一個宿主機上的普通程序,這就意味
著這些因為虛擬化而帶來的效能損耗都是不存在的;而另一方面,使用 Namespace 作為
隔離手段的容器並不需要單獨的 Guest OS,這就使得容器額外的資源佔用幾乎可以忽略
不計。
    所以說,“敏捷”和“高效能”是容器相較於虛擬機器最大的優勢,也是它能夠在 PaaS 這
種更細粒度的資源管理平臺上大行其道的重要原因。

二、隔離

    不過,有利就有弊,基於 Linux Namespace 的隔離機制相比於虛擬化技術也有很多
不足之處,其中最主要的問題就是:隔離得不徹底。首先,既然容器只是執行在宿主機上
的一種特殊的程序,那麼多個容器之間使用的就還是同一個宿主機的作業系統核心。
    儘管你可以在容器裡通過 Mount Namespace 單獨掛載其他不同版本的作業系統文
件,比如 CentOS 或者 Ubuntu,但這並不能改變共享宿主機核心的事實。這意味著,如
果你要在 Windows 宿主機上執行 Linux 容器,或者在低版本的 Linux 宿主機上執行
高版本的 Linux 容器,都是行不通的。
    而相比之下,擁有硬體虛擬化技術和獨立 Guest OS 的虛擬機器就要方便得多了。最極
端的例子是,Microsoft 的雲端計算平臺 Azure,實際上就是執行在 Windows 伺服器叢集
上的,但這並不妨礙你在它上面建立各種 Linux 虛擬機器出來。
    其次,在 Linux 核心中,有很多資源和物件是不能被 Namespace 化的,最典型的
例子就是:時間。這就意味著,如果你的容器中的程式使用 settimeofday(2) 系統呼叫
修改了時間,整個宿主機的時間都會被隨之修改,這顯然不符合使用者的預期。相比於在虛擬
機裡面可以隨便折騰的自由度,在容器裡部署應用的時候,“什麼能做,什麼不能做”,就是
使用者必須考慮的一個問題。
    此外,由於上述問題,尤其是共享宿主機核心的事實,容器給應用暴露出來的攻擊面
是相當大的,應用“越獄”的難度自然也比虛擬機器低得多。更為棘手的是,儘管在實踐中我
們確實可以使用 Seccomp 等技術,對容器內部發起的所有系統呼叫進行過濾和甄別來進
行安全加固,但這種方法因為多了一層對系統呼叫的過濾,必然會拖累容器的效能。
    何況,預設情況下,誰也不知道到底該開啟哪些系統呼叫,禁止哪些系統呼叫。
    所以,在生產環境中,沒有人敢把執行在物理機上的 Linux 容器直接暴露到公網上。
當然,我後續會講到的基於虛擬化或者獨立核心技術的容器實現,則可以比較好地在隔離
與效能之間做出平衡。

三、限制

    我還是以 PID Namespace 為例,雖然容器內的第 1 號程序在“障眼法”的干擾下只
能看到容器裡的情況,但是宿主機上,它作為第 100 號程序與其他所有程序之間依然是
平等的競爭關係。這就意味著,雖然第 100 號程序表面上被隔離了起來,但是它所能夠
使用到的資源(比如 CPU、記憶體),卻是可以隨時被宿主機上的其他程序(或者其他容器)
佔用的。
    當然,這個 100 號程序自己也可能把所有資源吃光。這些情況,顯然都不是一個
“沙盒”應該表現出來的合理行為。而 Linux Cgroups 就是 Linux 核心中用來為程序
設定資源限制的一個重要功能。有意思的是,Google 的工程師在 2006 年發起這項特性
的時候,曾將它命名為程序容器processcontainer
    實際上,在 Google 內部,“容器”這個術語長期以來都被用於形容被 Cgroups 限
制過的程序組。Cgroup它最主要的作用,就是限制一個程序組能夠使用的資源上限,包括 
CPU、記憶體、磁碟、網路頻寬等等。此外,Cgroups 還能夠對程序進行優先順序設定、審計,
以及將程序掛起和恢復等操作
    Linux Cgroups 的設計還是比較易用的,簡單粗暴地理解呢,它就是一個子系統目
錄加上一組資源限制檔案的組合。而對於 Docker 等 Linux 容器專案來說,它們只需要
在每個子系統下面,為每個容器建立一個控制組(即建立一個新目錄),然後在啟動容器
程序之後,把這個程序的 PID 填寫到對應控制組的 tasks 檔案中就可以了。

四、單程序模型

   一個正在執行的 Docker 容器,其實就是一個啟用了多個 Linux Namespace 的應
用程序,而這個程序能夠使用的資源量,則受 Cgroups 配置的限制。這也是容器技術中
一個非常重要的概念,即:容器是一個“單程序”模型。
   由於一個容器的本質就是一個程序,使用者的應用程序實際上就是容器裡 PID=1 的
程序,也是其他後續建立的所有程序的父程序。這就意味著,在一個容器中,你沒辦法同
時執行兩個不同的應用,除非你能事先找到一個公共的 PID=1 的程式來充當兩個不同應
用的父程序,這也是為什麼很多人都會用 systemd 或者 supervisord 這樣的軟體來
代替應用本身作為容器的啟動程序。
   但是,在後面分享容器設計模式時,我還會推薦其他更好的解決辦法。這是因為容器
本身的設計,就是希望容器和應用能夠同生命週期,這個概念對後續的容器編排非常重要。
否則,一旦出現類似於“容器是正常執行的,但是裡面的應用早已經掛了”的情況,編排系
統處理起來就非常麻煩了。另外,跟 Namespace 的情況類似,Cgroups 對資源的限制
能力也有很多不完善的地方,被提及最多的自然是 /proc 檔案系統的問題。
   眾所周知,Linux 下的 /proc 目錄儲存的是記錄當前核心執行狀態的一系列特殊
檔案,使用者可以通過訪問這些檔案,檢視系統以及當前正在執行的程序的資訊,比如 CPU 
使用情況、記憶體佔用率等,這些檔案也是 top 指令檢視系統資訊的主要資料來源。
   但是,你如果在容器裡執行 top 指令,就會發現,它顯示的資訊居然是宿主機的 CPU 
和記憶體資料,而不是當前容器的資料。造成這個問題的原因就是,/proc 檔案系統並不知
道使用者通過 Cgroups 給這個容器做了什麼樣的資源限制,即:/proc 檔案系統不瞭解
Cgroups 限制的存在。在生產環境中,這個問題必須進行修正,否則應用程式在容器裡讀
取到的 CPU 核數、可用記憶體等資訊都是宿主機上的資料,這會給應用的執行帶來非常大
的困惑和風險。這也是在企業中,容器化應用碰到的一個常見問題,也是容器相較於虛擬機器
另一個不盡如人意的地方。