淺談Linux容器和鏡像簽名
從根本上說,幾乎所有的主要軟件,即使是開源軟件,都是在基于鏡像的容器技術(shù)出現(xiàn)之前設(shè)計(jì)的。這意味著把軟件放到容器中相當(dāng)于是一次平臺(tái)移植。這也意味著一些程序可以很容易就遷移,而另一些就更困難。
我大約在三年半前開展基于鏡像的容器相關(guān)工作。到目前為止,我已經(jīng)容器化了大量應(yīng)用。我了解到什么是現(xiàn)實(shí)情況,什么是迷信。今天,我想簡要介紹一下 Linux 容器是如何設(shè)計(jì)的,以及談?wù)勭R像簽名。
Linux 容器是如何設(shè)計(jì)的
對(duì)于基于鏡像的 Linux 容器,讓大多數(shù)人感到困惑的是,它把操作系統(tǒng)分割成兩個(gè)部分:內(nèi)核空間與用戶空間。在傳統(tǒng)操作系統(tǒng)中,內(nèi)核運(yùn)行在硬件上,你無法直接與其交互。用戶空間才是你真正能交互的,這包括所有你可以通過文件瀏覽器或者運(yùn)行l(wèi)s命令能看到的文件、類庫、程序。當(dāng)你使用ifconfig命令調(diào)整 IP 地址時(shí),你實(shí)際上正在借助用戶空間的程序來使內(nèi)核根據(jù) TCP 協(xié)議棧改變。這點(diǎn)經(jīng)常讓沒有研究過 Linux/Unix 基礎(chǔ)的人大吃一驚。
過去,用戶空間中的類庫支持了與內(nèi)核交互的程序(比如 ifconfig、sysctl、tuned-adm)以及如網(wǎng)絡(luò)服務(wù)器和數(shù)據(jù)庫之類的面向用戶的程序。這些所有的東西都堆積在一個(gè)單一的文件系統(tǒng)結(jié)構(gòu)中。用戶可以在 /sbin 或者 /lib 文件夾中找到所有操作系統(tǒng)本身支持的程序和類庫,或者可以在 /usr/sbin 或 /usr/lib 文件夾中找到所有面向用戶的程序或類庫(參閱文件系統(tǒng)層次結(jié)構(gòu)標(biāo)準(zhǔn))。這個(gè)模型的問題在于操作系統(tǒng)程序和業(yè)務(wù)支持程序沒有絕對(duì)的隔離。/usr/bin 中的程序可能依賴 /lib 中的類庫。如果一個(gè)應(yīng)用所有者需要改變一些東西,就很有可能破壞操作系統(tǒng)。相反地,如果負(fù)責(zé)安全更新的團(tuán)隊(duì)需要改變一個(gè)類庫,就(常常)有可能破壞面向業(yè)務(wù)的應(yīng)用。這真是一團(tuán)糟。
借助基于鏡像的容器,比如 Docker、LXD、RKT,應(yīng)用程序所有者可以打包和調(diào)整所有放在 /sbin、/lib、/usr/bin 和 /usr/lib 中的依賴部分,而不用擔(dān)心破壞底層操作系統(tǒng)。本質(zhì)上講,容器技術(shù)再次干凈地將操作系統(tǒng)隔離為兩部分:內(nèi)核空間與用戶空間?,F(xiàn)在開發(fā)人員和運(yùn)維人員可以分別獨(dú)立地更新各自的東西。
然而還是有些令人困擾的地方。通常,每個(gè)應(yīng)用所有者(或開發(fā)者)并不想負(fù)責(zé)更新這些應(yīng)用依賴:像 openssl、glibc,或很底層的基礎(chǔ)組件,比如,XML 解析器、JVM,再或者處理與性能相關(guān)的設(shè)置。過去,這些問題都委托給運(yùn)維團(tuán)隊(duì)來處理。由于我們?cè)谌萜髦写虬撕芏嘁蕾?,?duì)于很多組織來講,對(duì)容器內(nèi)的所有東西負(fù)責(zé)仍是個(gè)嚴(yán)峻的問題。
遷移現(xiàn)有應(yīng)用到 Linux 容器
把應(yīng)用放到容器中算得上是平臺(tái)移植,我準(zhǔn)備突出介紹究竟是什么讓移植某些應(yīng)用到容器當(dāng)中這么困難。
(通過容器,)開發(fā)者現(xiàn)在對(duì) /sbin 、/lib、 /usr/bin、 /usr/lib 中的內(nèi)容有完全的控制權(quán)。但是,他們面臨的挑戰(zhàn)是,他們?nèi)孕枰獙?shù)據(jù)和配置放到 /etc 或者 /var/lib 文件夾中。對(duì)于基于鏡像的容器來說,這是一個(gè)糟糕的想法。我們真正需要的是代碼、配置以及數(shù)據(jù)的隔離。我們希望開發(fā)者把代碼放在容器當(dāng)中,而數(shù)據(jù)和配置通過不同的環(huán)境(比如,開發(fā)、測試或生產(chǎn)環(huán)境)來獲得。
這意味著我們(或者說平臺(tái))在實(shí)例化容器時(shí),需要掛載 /etc 或 /var/lib 中的一些文件或文件夾。這會(huì)允許我們到處移動(dòng)容器并仍能從環(huán)境中獲得數(shù)據(jù)和配置。聽起來很酷吧?這里有個(gè)問題,我們需要能夠干凈地隔離配置和數(shù)據(jù)。很多現(xiàn)代開源軟件比如 Apache、MySQL、MongoDB、Nginx 默認(rèn)就這么做了。但很多自產(chǎn)的、歷史遺留的、或?qū)S谐绦虿⑽茨J(rèn)這么設(shè)計(jì)。對(duì)于很多組織來講,這是主要的痛點(diǎn)。對(duì)于開發(fā)者來講的最佳實(shí)踐是,開始架構(gòu)新的應(yīng)用,移植遺留代碼,以完成配置和數(shù)據(jù)的完全隔離。
鏡像簽名簡介
信任機(jī)制是容器的重要議題。容器鏡像簽名允許用戶添加數(shù)字指紋到鏡像中。這個(gè)指紋隨后可被加密算法測試驗(yàn)證。這使得容器鏡像的用戶可以驗(yàn)證其來源并信任。
容器社區(qū)經(jīng)常使用“容器鏡像”這個(gè)詞組,但這個(gè)命名方法會(huì)讓人相當(dāng)困惑。Docker、LXD 和 RKT 推行獲取遠(yuǎn)程文件來當(dāng)作容器運(yùn)行這樣的概念。這些技術(shù)各自通過不同的方式處理容器鏡像。LXD 用單獨(dú)的一層來獲取單獨(dú)一個(gè)容器,而 Docker 和 RKT 使用基于開放容器鏡像(OCI)格式,可由多層組成。糟糕的是,會(huì)出現(xiàn)不同團(tuán)隊(duì)和組織對(duì)容器鏡像中的不同層負(fù)責(zé)的情況。容器鏡像概念下隱含的是容器鏡像格式的概念。擁有標(biāo)準(zhǔn)的鏡像格式比如 OCI 會(huì)讓容器生態(tài)系統(tǒng)圍繞著鏡像掃描、簽名,和在不同云服務(wù)提供商間轉(zhuǎn)移而繁榮發(fā)展。
現(xiàn)在談到簽名了。
容器存在一個(gè)問題,我們把一堆代碼、二進(jìn)制文件和類庫放入其中。一旦我們打包了代碼,我們就要把它和必要的文件服務(wù)器(注冊(cè)服務(wù)器)共享。代碼只要被共享,它基本上就是不具名的,缺少某種密文簽名。更糟糕的是,容器鏡像經(jīng)常由不同人或團(tuán)隊(duì)控制的各個(gè)鏡像層組成。每個(gè)團(tuán)隊(duì)都需要能夠檢查上一個(gè)團(tuán)隊(duì)的工作,增加他們自己的工作內(nèi)容,并在上面添加他們自己的批準(zhǔn)印記。然后他們需要繼續(xù)把工作交給下個(gè)團(tuán)隊(duì)。
(由很多鏡像組成的)容器鏡像的最終用戶需要檢查監(jiān)管鏈。他們需要驗(yàn)證每個(gè)往其中添加文件的團(tuán)隊(duì)的可信度。對(duì)于最終用戶而言,對(duì)容器鏡像中的每一層都有信心是極其重要的。