筆者前邊介紹過(guò)Docker容器技術(shù)有三大支柱,他們分別是:命名空間(Namespace)作喘,資源約束(CGroups)和文件系統(tǒng)隔離,筆者今天希望通過(guò)這篇文章,來(lái)和大家一起詳細(xì)的聊聊這些技術(shù)支柱的詳細(xì)信息壶辜。
我們從Namespace開(kāi)始,Linux操作系統(tǒng)的Namespaces提供了讓每個(gè)進(jìn)程都有獨(dú)立系統(tǒng)視圖的能力担租,這句話不是很好理解砸民。大白話來(lái)說(shuō),每個(gè)容器進(jìn)程都只能看到部分文件奋救,進(jìn)程岭参,網(wǎng)絡(luò)資源以及不同的系統(tǒng)hostname,通過(guò)命名空間就如同給每個(gè)進(jìn)程施加了魔法尝艘,讓每個(gè)容器進(jìn)程都以為自己是這臺(tái)機(jī)器上的唯一運(yùn)行的集成演侯,也就是員工號(hào)是1的創(chuàng)始人。
對(duì)于操作系統(tǒng)來(lái)說(shuō)背亥,一開(kāi)始所有的系統(tǒng)資源秒际,包括文件系統(tǒng),進(jìn)程ID狡汉,用戶ID娄徊,網(wǎng)絡(luò)接口等都在同一個(gè)池子里,所有在這臺(tái)機(jī)器上運(yùn)行的進(jìn)程都可以看到和使用這些資源盾戴。但是內(nèi)核給我們提供了命名空間這個(gè)能力寄锐,通過(guò)命名空間,我們就可以將前邊提到的這些資源切分成更小的集合尖啡。對(duì)著資源進(jìn)行重新組合的結(jié)果就是橄仆,我們可以讓一個(gè)或者一組進(jìn)程只能使用切分后的資源集合。
由于有了Namespace這個(gè)可以更加細(xì)粒度切分資源的機(jī)制可婶,我們?cè)趧?chuàng)建進(jìn)程的時(shí)候沿癞,可以指定具體的命名空間,這樣新創(chuàng)建的進(jìn)程就只能看到給這個(gè)命名空間分配的資源矛渴。
對(duì)于Linux操作系統(tǒng)來(lái)說(shuō)椎扬,命名空間的類型有很多惫搏,每個(gè)命名空間類型都代表了不同類型的資源,因此我們?cè)谑褂妹臻g創(chuàng)建進(jìn)程的時(shí)候蚕涤,可以指定多個(gè)命名空間來(lái)劃分這臺(tái)機(jī)器的物理資源集筐赔。
具體來(lái)說(shuō),Linux提供了如下的命名空間類型:
- Mount命名空間mnt揖铜,用來(lái)隔離掛載點(diǎn)(文件系統(tǒng))茴丰。
- Process ID命名空間pid,用來(lái)隔離線程的ID編號(hào)天吓。
- Network命名空間net贿肩,用來(lái)隔離網(wǎng)絡(luò)設(shè)備,網(wǎng)站棧和端口等龄寞。
- 進(jìn)程內(nèi)通信命名空間ipc汰规,用來(lái)隔離進(jìn)程間通信的資源,包括隔離消息隊(duì)列物邑,共享內(nèi)存等資源溜哮。
- Unix分時(shí)系統(tǒng)命名空間UTS,用來(lái)隔離系統(tǒng)的hostname色解,以及NIS用來(lái)隔離域名茂嗓。
- 用戶ID命名空間user,用來(lái)隔離用戶和用戶組科阎。
- Cgroup命名空間用來(lái)隔離控制組的根目錄述吸,我們會(huì)在后邊詳細(xì)介紹Cgroups,因?yàn)橥ㄟ^(guò)這個(gè)控制組萧恕,可以限制容器實(shí)例的資源使用量刚梭。
接下來(lái)我們來(lái)深入的介紹一下幾個(gè)關(guān)鍵的命名空間類型,為理解容器的三個(gè)支柱做好知識(shí)儲(chǔ)備票唆。
【使用network命名空間來(lái)賦予每個(gè)進(jìn)程獨(dú)占的網(wǎng)絡(luò)接口】
運(yùn)行在機(jī)器上的容器進(jìn)程能看到哪些網(wǎng)絡(luò)接口由網(wǎng)絡(luò)命名空間決定,每個(gè)網(wǎng)絡(luò)接口只能從屬于一個(gè)命名空間屹徘,但是我們可以將網(wǎng)絡(luò)接口從一個(gè)命名空間移動(dòng)到另外一個(gè)命名空間中走趋。
也就是說(shuō)如果我們給每個(gè)進(jìn)程都分配一個(gè)獨(dú)享的網(wǎng)絡(luò)命名空間,那么每個(gè)容器進(jìn)程就會(huì)看到自己獨(dú)享的網(wǎng)絡(luò)接口噪伊,這其實(shí)就類似于通過(guò)命名空間給每個(gè)容器進(jìn)程做了網(wǎng)絡(luò)接口層面的隔離簿煌。
為了更好的理解我們接受的第一個(gè)命名空間,筆者準(zhǔn)備如下的這張圖鉴吹,這張圖想展示的核心信息是:創(chuàng)建一個(gè)容器換的進(jìn)程姨伟,并給這個(gè)進(jìn)程提供1個(gè)獨(dú)享的網(wǎng)絡(luò)命名空間,這樣的話這個(gè)進(jìn)程只能看到屬于這個(gè)命名空間的網(wǎng)絡(luò)接口豆励。
如上圖所示夺荒,剛開(kāi)始瞒渠,機(jī)器上只有默認(rèn)網(wǎng)絡(luò)命名空間,接著我們創(chuàng)建了兩個(gè)新的網(wǎng)絡(luò)接口ethAA和loAA技扼,以及一個(gè)新的網(wǎng)絡(luò)命名空間A伍玖。然后我們把新創(chuàng)建的兩個(gè)網(wǎng)絡(luò)接口移動(dòng)到新創(chuàng)建的網(wǎng)絡(luò)命名空間A中,這個(gè)時(shí)候剿吻,我們就可以將這兩個(gè)網(wǎng)絡(luò)接口的名字修改為標(biāo)準(zhǔn)的名字eth0和lo窍箍。最后,容器進(jìn)程在這個(gè)命名空間中啟動(dòng)丽旅,就只會(huì)看到網(wǎng)絡(luò)接口eh0和lo了椰棘。
如果我們從容器進(jìn)程的角度來(lái)看,單純通過(guò)網(wǎng)絡(luò)接口是無(wú)法判斷這個(gè)進(jìn)程是運(yùn)行在容器中榄笙,虛擬機(jī)上還是直接運(yùn)行在宿主機(jī)上邪狞。接下來(lái)我們來(lái)看看如何讓每個(gè)容器進(jìn)程有自己專有的hostname,這樣才能體現(xiàn)出在這臺(tái)機(jī)器上的“唯我獨(dú)宗”办斑,請(qǐng)繼續(xù)閱讀外恕。
【通過(guò)UTS命名空間來(lái)給每個(gè)容器進(jìn)程分配專屬Hostname】
Hostname對(duì)于一個(gè)容器具備“自我意識(shí)”來(lái)說(shuō)非常重要,邏輯上講乡翅,一個(gè)容器實(shí)例運(yùn)行的機(jī)器上的hostname和另外一個(gè)容器實(shí)例運(yùn)行的hostname不一樣的情況下,我們才會(huì)覺(jué)得他們運(yùn)行在不同的機(jī)器上蠕蚜,UTS命名空間可以提供這種隔離。
具體來(lái)說(shuō)腺毫,UTS命名空間讓運(yùn)行在其中的進(jìn)程有專屬的hostname和domain name,這樣不同的容器之間就好像運(yùn)行在不同的機(jī)器上一樣挣柬,我們可以創(chuàng)建兩個(gè)不同的UTS命名空間,然后創(chuàng)建兩個(gè)容器進(jìn)程邪蛔,讓兩個(gè)進(jìn)程分別運(yùn)行在這兩個(gè)命名空間中,這樣的話侧到, 兩個(gè)容器進(jìn)程即便是運(yùn)行在相同的宿主機(jī)上勃教,但是他們會(huì)看到不同的系統(tǒng)參數(shù)hostname。
好了匠抗,我們介紹了兩個(gè)命名空間,以及通過(guò)命名空間我們是如何給容器進(jìn)程施加魔法绳军,讓即便是運(yùn)行在同一臺(tái)宿主機(jī)上的兩個(gè)容器進(jìn)程,可以看到不同的hostname删铃,網(wǎng)絡(luò)接口等,那么你是不是很好奇咒劲,命名空間是如何時(shí)間這種魔法的诫隅,請(qǐng)繼續(xù)閱讀。
【命名空間是如何讓容器進(jìn)程實(shí)現(xiàn)資源隔離】
通過(guò)前邊列舉的兩個(gè)例子逐纬,我們通過(guò)創(chuàng)建網(wǎng)絡(luò)命名空間和UTS命名空間,就可以給運(yùn)行在其中的容器進(jìn)程提供資源的隔離兔毒,讓容器進(jìn)程以為自己是這臺(tái)宿主機(jī)的唯一進(jìn)程甸箱,本質(zhì)是通過(guò)命名空間給每個(gè)進(jìn)程創(chuàng)造了一個(gè)隔離的運(yùn)行環(huán)境。
雖然我們通常情況下需要這個(gè)隔離的邊界芍殖,但是如果我們要把關(guān)系親密的兩個(gè)組件容器化豌骏,比如一個(gè)生產(chǎn)網(wǎng)頁(yè)的進(jìn)程和Nginx進(jìn)程,他們?cè)谖锢頇C(jī)上通過(guò)localhost進(jìn)行通信窃躲,在這種情況下,我們就需要這兩個(gè)進(jìn)程運(yùn)行在一個(gè)命名空間中蒂窒,比如說(shuō)網(wǎng)絡(luò)命名空間中蹦玫,這樣他們才能通過(guò)localhost進(jìn)行通信。
如下圖所示刘绣,兩個(gè)進(jìn)程共享了網(wǎng)絡(luò)命名空間挣输,但是有自己的專屬的文件系統(tǒng):
我們聚焦一下停士,來(lái)看看網(wǎng)絡(luò)接口,兩個(gè)容器進(jìn)程由于在同一個(gè)網(wǎng)絡(luò)命名空間中拇舀,因此它們看到的是相同的網(wǎng)絡(luò)設(shè)備(eth0和lo)蜻底,這就意味著這兩個(gè)容器有相同的IP地址,以及可以通過(guò)loopback設(shè)備來(lái)進(jìn)行通信要拂,這就解決了筆者在上邊提到的問(wèn)題站楚,如何容器化這種具有親密性的應(yīng)用之間的關(guān)系。
另外拉一,由于這兩個(gè)進(jìn)程的UTS命名空間也一樣旧乞,他們會(huì)看到相同的hostname,與之相反的是良蛮,進(jìn)程1和進(jìn)程2有自己專屬的mount命名空間决瞳,因此他們其實(shí)看到的是不同的文件系統(tǒng)。通過(guò)上邊的描述你可以看到皮胡,如果我們想讓兩個(gè)進(jìn)程共享某些資源,我們可以通過(guò)將兩個(gè)容器進(jìn)程加入到相同的資源組(命名空間)來(lái)實(shí)現(xiàn)蠢棱。
介紹到這里為止甩栈,我們終于可以回答這個(gè)問(wèn)題:為什么我們需要容器?你可以細(xì)細(xì)的品味一下這句話:運(yùn)行在容器中的進(jìn)程其實(shí)和運(yùn)行在虛擬機(jī)中的進(jìn)程是不對(duì)等的玉转,這個(gè)進(jìn)程只是為他設(shè)置了上邊提到的7個(gè)不同的命名空間殴蹄,有些命名空間在多個(gè)進(jìn)程之間共享猾担,有些沒(méi)有绑嘹,這就意味著我們?cè)谔摂M機(jī)中的進(jìn)程和容器中的進(jìn)程其實(shí)不對(duì)等橘茉,因此我們需要容器這個(gè)抽象,來(lái)提供從虛擬機(jī)部署的應(yīng)用到容器化部署應(yīng)用的映射捺癞。
好了髓介,介紹完7大命名空間后,不知道你是否會(huì)想唐础,如果我登陸到這臺(tái)容器進(jìn)程“機(jī)器”上一膨,我看到的景象應(yīng)該是什么樣子,接下來(lái)豹绪,我們來(lái)一起看看瞒津,登陸到容器進(jìn)程中,能看到什么信息巷蚪?
【從容器內(nèi)部分析運(yùn)行環(huán)境】
從容器內(nèi)部屁柏,我們關(guān)心這臺(tái)機(jī)器的系統(tǒng)參數(shù)hostname具體是什么值?配置的ip地址是多少淌喻?文件系統(tǒng)上能看到那些文件和文件夾裸删,或者說(shuō)有哪些二進(jìn)制庫(kù)和依賴可以訪問(wèn)到?等等。在登陸到一個(gè)容器之前,筆者問(wèn)大家一個(gè)問(wèn)題伤塌?如果你要登陸到一臺(tái)遠(yuǎn)程的虛擬機(jī)上每聪,你會(huì)怎么做?我們首先需要遠(yuǎn)程連接到這臺(tái)機(jī)器药薯,然后運(yùn)行shell的腳本童本,而登陸容器的方式和登陸虛擬機(jī)完全一樣。
注意:并不是所有的應(yīng)用容器是實(shí)例都可以遠(yuǎn)程登陸穷娱,筆者要強(qiáng)調(diào)的是這里的討論只限于開(kāi)發(fā)和測(cè)試環(huán)境泵额,因?yàn)樯a(chǎn)環(huán)境從原則上講,就不應(yīng)該安裝可以進(jìn)行遠(yuǎn)程登陸的shell腳本嫁盲。
接下來(lái)我們看看如何在登陸到一臺(tái)運(yùn)行中的容器進(jìn)程羞秤。我們?cè)谇耙黄恼轮袠?gòu)建的yunpan-container提供了bash shell,因此我們可以通過(guò)命令來(lái)遠(yuǎn)程登錄到這個(gè)容器進(jìn)程中:docker exec -it yunpan-container bash,
?? sample1 docker exec -it yunpan-container bash
root@e48ca2f1460d:/#
看到這個(gè)#是不是很激動(dòng)嗦董,我們已經(jīng)登陸到這個(gè)容器進(jìn)程中了瘦黑。我們來(lái)稍微解釋一下上邊的這句命令,通過(guò)提供bash參數(shù)匹摇,我們就可以在yunpan-container中增加一個(gè)bash的進(jìn)程甲葬,這個(gè)bash進(jìn)程和我們的Node js進(jìn)程運(yùn)行在相同的命名空間中,因此我們可以通過(guò)這個(gè)bash進(jìn)程坡垫,來(lái)分析node js應(yīng)用運(yùn)行的環(huán)境,而命令中的-it參數(shù)的詳細(xì)介紹如下:
-i 參數(shù)告訴Docker要把bash運(yùn)行在交互模式下.
-t 參數(shù)會(huì)分配偽終端給用戶, 這樣我們就會(huì)看到這個(gè)/#的輸出, 等待登陸用戶輸入后續(xù)的命令.
因?yàn)槲覀円呀?jīng)登陸到容器進(jìn)程中了,接下來(lái)我們就可以運(yùn)行 ps aux命令,來(lái)列出在容器中運(yùn)行的進(jìn)程信息, 如下圖是這個(gè)命令的輸出:
root@e48ca2f1460d:/# ps aux
USER? ? ? PID %CPU %MEM? ? VSZ? RSS TTY? ? ? STAT START? TIME COMMAND
root? ? ? ? 1? 0.0? 1.4 565636 29644 ?? ? ? ? Ssl? 01:07? 0:00 node app.js
root? ? ? ? 14? 0.0? 0.1? 18188? 3084 pts/0? ? Ss? 10:05? 0:00 bash
root? ? ? ? 21? 0.0? 0.1? 36640? 2644 pts/0? ? R+? 10:13? 0:00 ps aux
root@e48ca2f1460d:/#
從上邊的輸出可以看到只輸出了三個(gè)進(jìn)程,分別是1號(hào)進(jìn)程,14號(hào)進(jìn)程和21號(hào)進(jìn)程, 其中1號(hào)進(jìn)程就是我們前邊說(shuō)的那個(gè)唯我獨(dú)宗的應(yīng)用進(jìn)程, 是在容器啟動(dòng)的時(shí)候加載并開(kāi)始運(yùn)行的, 而進(jìn)程14和21就是我們的bash和ps命令.
從上圖我希望大家也能分析到, 我們看不到這臺(tái)機(jī)器上運(yùn)行的其他進(jìn)程, 以及其他容器中運(yùn)行的進(jìn)程, 這就證明了我們上邊的分析過(guò)程是準(zhǔn)確的堡妒。
筆者前邊介紹過(guò)溉卓,在Mac系統(tǒng)上,容器都是運(yùn)行在宿主機(jī)上創(chuàng)建的一個(gè)Linux虛擬機(jī)中伏尼,因此其實(shí)對(duì)于剛才看到的容器進(jìn)程來(lái)說(shuō)尉尾,宿主機(jī)就是這個(gè)Linux虛擬機(jī),那么我們來(lái)分析一下扰她,從這個(gè)Linux虛擬機(jī)宿主機(jī)上能看到什么芭碍?
我們可以在另外一個(gè)終端上,運(yùn)行這個(gè)命令來(lái)登陸到這個(gè)Linux虛擬機(jī)上:docker run --net=host --ipc=host --uts=host --pid=host -it --security-opt=seccomp=unconfined --privileged --rm -v /:/host alpine chroot /Hosted:私有倉(cāng)庫(kù)忧勿,內(nèi)部項(xiàng)目的發(fā)布倉(cāng)庫(kù)瞻讽,專門(mén)用來(lái)存儲(chǔ)我們自己生成的jar文件
root@docker-desktop:/# ps aux | grep app.js
root? ? ? 4259? 0.0? 1.4 565636 29644 ?? ? ? ? Ssl? 01:07? 0:00 node app.js
root? ? ? 4484? 0.0? 0.0? 3088? 880 ?? ? ? ? S+? 10:20? 0:00 grep app.js
root@docker-desktop:/#
如果你眼神比較鋒利速勇,應(yīng)該很容就看到在宿主機(jī)上看到的進(jìn)程ID和在容器中看到的是不一樣的, 比如在宿主機(jī)上看到的是4259, 而在容器中是1, 這背后的原理是:容器有自己的進(jìn)程命名空間,因此容器會(huì)有自己的進(jìn)程樹(shù)养匈,以及進(jìn)程的ID序列都伪,如下圖所示,容器的進(jìn)程樹(shù)隸屬于宿主機(jī)的進(jìn)程樹(shù)陨晶,因此運(yùn)行在容器進(jìn)程其實(shí)有兩個(gè)進(jìn)程ID:
除了進(jìn)程有專屬的進(jìn)程ID序列,每個(gè)容器進(jìn)程還有自己專屬的文件系統(tǒng)的烁。如果你在容器的根目錄運(yùn)行l(wèi)s命令叭爱,你能看到的只有這個(gè)容器鏡像中,以及啟動(dòng)時(shí)掛載的文件和文件夾清單。下面是我們?cè)趛unpan-container中運(yùn)行 ls /命令的結(jié)果:
root@e48ca2f1460d:/# ls /
app.js bin? boot? dev etc? home? lib lib64? media? mnt? opt proc? root? run? sbin? srv? sys? tmp? usr? var
root@e48ca2f1460d:/#
從命令的輸出中杨帽,你看可以找到我們的應(yīng)用程序app.js注盈,以及node:12基礎(chǔ)鏡像中包含的文件和文件夾,這里需要注意的是老客,你無(wú)法從容器中看到宿主機(jī)操作系統(tǒng)上的文件和文件夾胧砰。而這種隔離提供了安全保障,攻擊者就無(wú)法在攻破了node js容器后尉间,獲取到宿主機(jī)上的機(jī)密信息哲嘲。好了,我們通過(guò)到不同的“機(jī)器”上驗(yàn)證了我們前邊推論的精確性眠副。接下來(lái)我們來(lái)看看容器技術(shù)的另外一個(gè)支柱:Cgroups囱怕。
【通過(guò)CGroups限制容器進(jìn)程的資源使用率】
雖然說(shuō)Linux的命名空間機(jī)制可以讓容器進(jìn)程只能訪問(wèn)機(jī)器上的部分資源,但是命名空間并沒(méi)有限制一個(gè)進(jìn)程可以使用多大的資源光涂,舉個(gè)例子來(lái)說(shuō)明一下忘闻。
我們使用網(wǎng)絡(luò)命名空間限制某個(gè)容器進(jìn)程只能使用固定的網(wǎng)絡(luò)接口,但是我們無(wú)法通過(guò)命名空間來(lái)限制容器進(jìn)程能夠使用的網(wǎng)絡(luò)帶寬私恬,進(jìn)一步來(lái)說(shuō),我們也無(wú)法通過(guò)命名空間來(lái)限制容器的CPU和內(nèi)存時(shí)候用量本鸣。如果我們無(wú)法對(duì)容器的CPU和內(nèi)存資源使用量做限制荣德,就會(huì)出現(xiàn)某個(gè)惡意的進(jìn)程,消耗了大量的資源和CPU涮瞻,從而導(dǎo)致正常的業(yè)務(wù)應(yīng)用容器退出署咽,從而導(dǎo)致業(yè)務(wù)中斷,因此我們必須解決“限制”這個(gè)問(wèn)題宁否。
我們接下來(lái)要介紹的Linux內(nèi)核的另外一個(gè)功能叫Linux Control Groups(cgroups)慕匠。通過(guò)cgroups提供的能力,我們就可以約束容器進(jìn)程能夠使用的資源上線冤寿,這樣即便是有惡意的進(jìn)程青伤,由于受到cgoups的限制,業(yè)務(wù)發(fā)使用超過(guò)配置的資源量狠角,整個(gè)系統(tǒng)的穩(wěn)定性和安全性就得到額提升丰歌。
筆者會(huì)在后續(xù)的文章中專題介紹Controls Group的運(yùn)行原理,在簡(jiǎn)單了解了cgroups之后眼溶,我們來(lái)看看如何讓docker限制應(yīng)用可以使用的機(jī)器資源晓勇。Docker提供了豐富的啟動(dòng)選項(xiàng)來(lái)讓我們可以約束容器使用的資源總量灌旧,比如我們希望啟動(dòng)的容器只使用有4顆CPU的機(jī)器上的前兩個(gè)绰筛,就可以使用--cpuset-cpus選項(xiàng):
$ docker run --cpuset-cpus="1,2" ...
除了制定CPU的數(shù)量铝噩,我們還可以從耕細(xì)粒度來(lái)控制,比如約束能夠使用的CPU時(shí)間骏庸,以及CPU的總量等具被,具體的選型可以參考Doker的文檔,筆者就不在這里累述了硬猫。內(nèi)存資源和CPU資源類似啸蜜,如果不加限制辈挂,也會(huì)出現(xiàn)被惡意使用的情況,因此Docker也給我們提供了內(nèi)存約束的選項(xiàng), 比如我們可以使用如下的啟動(dòng)選項(xiàng)來(lái)約束容器只能使用100m內(nèi)存:
$ docker run --memory="100m" ...
本質(zhì)上來(lái)說(shuō),這些啟動(dòng)選項(xiàng)都做了相同的工作,就是配置進(jìn)程的cgroups, 而配置完成之后蜂林,內(nèi)核來(lái)負(fù)責(zé)基于用戶提供的配置來(lái)限制進(jìn)程可以使用的CPU拇泣,內(nèi)存資源的總量。
關(guān)于cgroups提供的功能和用法我們就介紹到這里了睁蕾,希望大家能夠通過(guò)這幾篇文章的閱讀债朵,對(duì)容器的技術(shù)支柱有更加深刻和完整的認(rèn)知。筆者這篇文章一直在聊Linux內(nèi)容提供的隔離機(jī)制臭杰,讓進(jìn)程能夠進(jìn)行“某種程度”的隔離谚中,來(lái)實(shí)現(xiàn)容器中的進(jìn)程和虛擬機(jī)上的進(jìn)程的對(duì)標(biāo)寥枝,而這里其實(shí)有個(gè)問(wèn)題脉顿,因?yàn)樗械娜萜鞫贾皇且粋€(gè)運(yùn)行在宿主機(jī)的特殊進(jìn)程而已点寥,那么其實(shí)從內(nèi)核的角度,這些進(jìn)程之間并沒(méi)有做到徹底的隔離蔽莱。這就會(huì)造成安全問(wèn)題戚长,如果有一個(gè)容器“變節(jié)”,嵌入的惡意代碼修改了內(nèi)核的參數(shù)仪糖,會(huì)導(dǎo)致運(yùn)行在這臺(tái)機(jī)器上的所有容器受到影響迫肖,可能有些同學(xué)覺(jué)得這是危言損聽(tīng),我們來(lái)舉個(gè)例子故爵。
假設(shè)Kubernetes集群的某個(gè)工作節(jié)點(diǎn)上運(yùn)行了3個(gè)容器應(yīng)用隅津,每個(gè)容器實(shí)例都有自己的網(wǎng)絡(luò)命名空間和文件系統(tǒng)伦仍,并且運(yùn)維人員配置了容器可以使用的資源最大值,從表面看呢铆,這個(gè)部署案例不會(huì)因?yàn)橐粋€(gè)容器變節(jié)棺克,對(duì)其他容器的正常運(yùn)行造成危害。但是事情一般都比看起來(lái)要復(fù)雜娜谊,如果變節(jié)的容器修改了這臺(tái)機(jī)器的系統(tǒng)時(shí)間呢纱皆,而系統(tǒng)時(shí)間是內(nèi)核的參數(shù)芭商,因此運(yùn)行在其上的應(yīng)用搀缠,如果業(yè)務(wù)和時(shí)間強(qiáng)相關(guān)艺普,那么就會(huì)出現(xiàn)業(yè)務(wù)異常。
因?yàn)槲覀冃枰鼜?qiáng)的約束手段歧譬,比如限制容器能夠使用的系統(tǒng)調(diào)用瑰步,這就引出了我們今天要給大家介紹的最后一個(gè)Linux內(nèi)核提供的能力,如何通過(guò)設(shè)置提升鏡像的安全性读虏。容器和操作系統(tǒng)之間通過(guò)系統(tǒng)調(diào)用(sys-call)來(lái)交互袁滥,我們無(wú)論是創(chuàng)建進(jìn)程,操作文件和設(shè)備,以及發(fā)送網(wǎng)絡(luò)數(shù)據(jù)睦焕,都需要使用操作系統(tǒng)提供的系統(tǒng)調(diào)用垃喊。有些系統(tǒng)調(diào)用本身是安全的,因此會(huì)開(kāi)發(fā)給所有機(jī)器上的進(jìn)程使用初家,而有些如果誤用會(huì)造成問(wèn)題乌助,因此只開(kāi)發(fā)給具備特定權(quán)限的進(jìn)程使用。
我們以剛才的例子來(lái)討論掖肋,運(yùn)行在工作節(jié)點(diǎn)上的容器實(shí)例可以打開(kāi)文件赏参,但是無(wú)法修改系統(tǒng)時(shí)鐘,以及內(nèi)核的某些參數(shù)從而導(dǎo)致其他的應(yīng)用運(yùn)行故障纫溃,這就是安全限制的作用紊浩。因此對(duì)于開(kāi)發(fā)和運(yùn)維的同學(xué)來(lái)說(shuō),大部分容易都應(yīng)該運(yùn)行的普通權(quán)限下万伤,只有特殊需要的進(jìn)程呜袁,才應(yīng)該給提供更高級(jí)別的權(quán)限。
注:我們可以在docker中使用--previlege選項(xiàng)來(lái)創(chuàng)建有特殊權(quán)限的容器實(shí)例虹钮。
但是依據(jù)最小權(quán)限的原則膘融,我們不應(yīng)該給容器他不需要用到的權(quán)限,這樣可以縮小攻擊面春畔,提升系統(tǒng)的安全性岛都,因此我們需要粒度更細(xì)的權(quán)限控制機(jī)制臼疫。幸運(yùn)的是迈勋,Linux內(nèi)核提供了capabilities這種能力拔创,將操作系統(tǒng)提供的功能分成了不同的組伏蚊,這些組就叫capabilities躏吊。比如Linux操作系統(tǒng)有如下的系統(tǒng)功能分組:
_CAP_NET_ADMIN允許所有的進(jìn)程使用網(wǎng)絡(luò)相關(guān)的能力
_CAP_NET_BIND允許進(jìn)程可以使用小于1024的端口號(hào)(說(shuō)明是系統(tǒng)進(jìn)程)
_CAP_SYS_TIME允許進(jìn)程修改系統(tǒng)時(shí)間等
當(dāng)我們創(chuàng)建容器實(shí)例的時(shí)候,capabilities是可以被顯式的刪除胜卤,Kubernetes默認(rèn)將所啟動(dòng)容器的capabiliteis全部刪除赁项,只留下那些供應(yīng)用程序正常運(yùn)行的悠菜,用戶也可以在啟動(dòng)容器實(shí)例的時(shí)候顯式增加或者去掉某些capabilities。好了悔醋,今天的文章就這么多了,下一遍文章筆者會(huì)通過(guò)一個(gè)Spring Cloud應(yīng)用程序如何部署到Kubernetes集群猾愿,正式拉開(kāi)一系列關(guān)于K8S介紹的文章账阻。