10個(gè)小技巧提高 Kubernetes 容器效率
近年來(lái),容器以及 Kubernetes 成為開發(fā)者以及企業(yè)用戶重點(diǎn)關(guān)注的技術(shù)趨勢(shì),本文總結(jié)了構(gòu)建和管理容器的十個(gè)重要技巧來(lái)優(yōu)化 IT 成本并提高效率。
容器是 Kubernetes 中應(yīng)用程序的核心載體。當(dāng)創(chuàng)建 Kubernetes 工作負(fù)載,例如創(chuàng)建用于調(diào)度、擴(kuò)容或者升級(jí)應(yīng)用程序的規(guī)則時(shí),首先需要?jiǎng)?chuàng)建一個(gè)容器鏡像,然后通過(guò)該鏡像來(lái)運(yùn)行服務(wù)或 Kubernetes 工作負(fù)載。在完成對(duì)鏡像的測(cè)試并與應(yīng)用程序其余代碼整合后,用戶通常會(huì)將鏡像推送到容器注冊(cè)中心。但在推送之前,仍然有很多實(shí)戰(zhàn)技巧可以幫助構(gòu)建和管理容器。
正文
通過(guò) Kubernetes,用戶可以自動(dòng)擴(kuò)展業(yè)務(wù),而整個(gè)過(guò)程很少出現(xiàn)甚至零宕機(jī),從而優(yōu)化 IT 成本并提高系統(tǒng)可靠性。
1、使用Kubernetes 模式
隨著 Kubernetes 不斷推出新功能,其應(yīng)用模式也在逐步改變。為了確保 Kubernetes 集群遵循當(dāng)前 Kubernetes 的應(yīng)用模式,用戶需要定期查閱Kubernetes 官方文檔以及每一個(gè)版本的發(fā)布說(shuō)明。
2、復(fù)用基礎(chǔ)鏡像以節(jié)省時(shí)間
在 Kubernetes 集群中創(chuàng)建應(yīng)用容器時(shí),用戶需要構(gòu)建一個(gè) Docker 基礎(chǔ)鏡像,然后在此鏡像基礎(chǔ)上構(gòu)建部分或全部應(yīng)用容器。有很多應(yīng)用共享依賴項(xiàng)、庫(kù)和配置,因此可以在基礎(chǔ)鏡像完成對(duì)共享部分進(jìn)行配置,從而實(shí)現(xiàn)復(fù)用。
Docker Hub和Google Container 注冊(cè)中心有數(shù)千個(gè)可供下載的基礎(chǔ)鏡像,這些鏡像已經(jīng)預(yù)先完成應(yīng)用配置,隨時(shí)可以投入使用,這可以節(jié)省大量時(shí)間。

3、不要輕易相信任何鏡像
盡管使用預(yù)先構(gòu)建的鏡像很方便,但要格外小心并確保對(duì)其運(yùn)行特定漏洞掃描。
一些開發(fā)人員會(huì)從 Docker Hub 中獲取一個(gè)其他用戶創(chuàng)建的基礎(chǔ)鏡像,然后將這個(gè)容器推送到生產(chǎn)環(huán)境,而這一切只是因?yàn)檎б豢催@個(gè)鏡像包含了所需要的包。
這里有很多錯(cuò)誤:鏡像中的代碼版本可能不正確;這些代碼可能有漏洞;或者更糟糕的情形是該項(xiàng)目可能已經(jīng)被故意綁定了惡意軟件。
為了緩解上述問(wèn)題,用戶可以通過(guò) Snyder 或 Twistlock 來(lái)運(yùn)行靜態(tài)分析,然后將其整合到 CI/CD(持續(xù)集成和持續(xù)交付)管道中,進(jìn)而掃描所有容器漏洞。
一般來(lái)說(shuō),一旦在基礎(chǔ)鏡像中發(fā)現(xiàn)漏洞,用戶就應(yīng)該重新構(gòu)建整個(gè)鏡像,而不是僅僅修復(fù)漏洞。容器應(yīng)該是不變的,因此,需要引入補(bǔ)丁重新構(gòu)建和部署鏡像。
4、優(yōu)化基礎(chǔ)鏡像
從最精簡(jiǎn)、最可行的基礎(chǔ)鏡像開始,然后在此基礎(chǔ)上構(gòu)建軟件包。通過(guò)這種方式,可以準(zhǔn)確掌握容器中的全部?jī)?nèi)容。
較小的基礎(chǔ)鏡像也可以減少開銷。例如,應(yīng)用可能只有 5MB 大小,如果要添加一個(gè)現(xiàn)成的 Node.js 鏡像,然后再安裝所有的庫(kù),整個(gè)鏡像很可能會(huì)變成 600MB 大小,但實(shí)際上并不需要這些額外的庫(kù)。
因此,盡量保持最精簡(jiǎn)的鏡像可以使:
- 構(gòu)建更快
- 存儲(chǔ)空間更小
- 鏡像拉取更快
- 潛在威脅面更小
5、確保容器只運(yùn)行一個(gè)進(jìn)程
同保持基礎(chǔ)鏡像最小化類似的是,確保每個(gè)容器只有一個(gè)進(jìn)程。容器的生命周期與它托管的應(yīng)用程序相同,這意味著每個(gè)容器應(yīng)該只包含一個(gè)父進(jìn)程。

按照Google Cloud的說(shuō)法,把容器當(dāng)作虛擬機(jī)并同時(shí)運(yùn)行多個(gè)進(jìn)程是一個(gè)常見的錯(cuò)誤。雖然容器可以實(shí)現(xiàn)這種方式,但這樣就無(wú)法使用 Kubernetes 的自我修復(fù)屬性。
通常,容器和應(yīng)用應(yīng)該同時(shí)啟動(dòng);同樣,當(dāng)應(yīng)用停止時(shí),容器也應(yīng)該停止。如果在一個(gè)容器中有多個(gè)進(jìn)程,可能會(huì)出現(xiàn)應(yīng)用程序狀態(tài)混雜的情形,這將導(dǎo)致 Kubernetes 無(wú)法確定一個(gè)容器是否健康。
6、正確處理 Linux 信號(hào)
容器通過(guò) Linux 信號(hào)來(lái)控制其內(nèi)部進(jìn)程的生命周期。為了將應(yīng)用的生命周期與容器聯(lián)系起來(lái),需要確保應(yīng)用能夠正確處理 Linux 信號(hào)。
Linux 內(nèi)核使用了諸如 SIGTERM、SIGKILL 和 SIGINIT 等信號(hào)來(lái)終止進(jìn)程。但是,容器內(nèi)的 Linux 會(huì)使用不同的方式來(lái)執(zhí)行這些常見信號(hào),如果執(zhí)行結(jié)果同信號(hào)默認(rèn)結(jié)果不符,將會(huì)導(dǎo)致錯(cuò)誤和中斷發(fā)生。
創(chuàng)建專門的 init 系統(tǒng)有助于解決此問(wèn)題,比如專門針對(duì)容器的Linux Tini系統(tǒng)。這個(gè)工具正確注冊(cè)了信號(hào)處理程序(比如 PID),容器化應(yīng)用可以正確執(zhí)行 Linux 信號(hào),從而正常關(guān)閉孤立進(jìn)程和僵尸進(jìn)程,完成內(nèi)存回收。
7、充分利用 Docker 的緩存構(gòu)建機(jī)制
容器鏡像由一系列鏡像層組成,這些鏡像層通過(guò)模板或 Dockerfile 中的指令生成。這些層以及構(gòu)建順序通常被容器平臺(tái)緩存。例如,Docker 就有一個(gè)可以被不同層復(fù)用的構(gòu)建緩存。這個(gè)緩存可以使構(gòu)建更快,但是要確保當(dāng)前層的所有父節(jié)點(diǎn)都保存了構(gòu)建緩存,并且這些緩存沒(méi)有被改變過(guò)。簡(jiǎn)單來(lái)講,需要把不變的層放在前面,而把頻繁改變的層放在后面。
例如,假設(shè)有一個(gè)包含步驟 X、Y 和 Z 的構(gòu)建文件,對(duì)步驟 Z 進(jìn)行了更改,構(gòu)建文件可以在緩存中重用步驟 X 和 Y,因?yàn)檫@些層在更改 Z 之前就已經(jīng)存在,這樣可以加速構(gòu)建過(guò)程。但是,如果改變了步驟 X,緩存中的層就不能再被復(fù)用。
雖然這是一種方便的行為,可以節(jié)省時(shí)間,但是必須確保所有鏡像層都是當(dāng)前的,而不是從舊的、過(guò)時(shí)的緩存構(gòu)建而成。
8、使用類似 Helm 的包管理器
Helm作為 Kubernetes 的非官方軟件包管理器,可以幫助安裝和更新集群中運(yùn)行的共同負(fù)載和容器。Helm 可以使用Chart聲明自定義應(yīng)用程序依賴項(xiàng),并提供滾動(dòng)升級(jí)和回滾工具。
用戶可以通過(guò)現(xiàn)有基礎(chǔ)鏡像為 Kubernetes 集群提供通用服務(wù),如數(shù)據(jù)庫(kù)或 Web 服務(wù);也可以為內(nèi)部應(yīng)用程序創(chuàng)建自定義基礎(chǔ)鏡像,創(chuàng)建自定義的 Charts 可以簡(jiǎn)化部署,減少開發(fā)團(tuán)隊(duì)的工作負(fù)擔(dān)和重復(fù)性工作。
9、使用標(biāo)簽和語(yǔ)義化版本號(hào)
作為基本原則,用戶不應(yīng)該使用:latest標(biāo)記。對(duì)大多數(shù)開發(fā)人員來(lái)說(shuō),這是顯而易見的。如果不為容器添加自定義標(biāo)簽,它將嘗試從鏡像倉(cāng)庫(kù)中拉取當(dāng)前版本,而容器可能并沒(méi)有包括需要的更改。
在創(chuàng)建自定義鏡像時(shí),使用鏡像標(biāo)簽和語(yǔ)義化版本號(hào)來(lái)追蹤對(duì) Docker 容器的更改。當(dāng)它們?cè)?Kubernetes 集群中運(yùn)行時(shí),Kubernetes 通過(guò)鏡像標(biāo)簽確定應(yīng)該運(yùn)行哪個(gè)版本。在選擇 Docker 鏡像版本機(jī)制時(shí),應(yīng)該同時(shí)考慮生產(chǎn)負(fù)載和開發(fā)流程兩種情況,這樣才能在 Kubernetes 中獲得更好的效果。
10、安全
在很多情況下,當(dāng)構(gòu)建 Docker 鏡像時(shí),需要讓容器內(nèi)的應(yīng)用程序訪問(wèn)敏感數(shù)據(jù),例如 API 令牌、私鑰和數(shù)據(jù)庫(kù)連接字符串等。
將這些秘密信息嵌入到容器中并不是一個(gè)安全的解決方案,即使只是保存到一個(gè)私有容器鏡像中。將未加密的隱私數(shù)據(jù)作為 Docker 鏡像的一部分進(jìn)行處理會(huì)面臨無(wú)數(shù)額外的安全風(fēng)險(xiǎn),包括網(wǎng)絡(luò)和鏡像注冊(cè)表的安全性,而 Docker 架構(gòu)本身也決定了無(wú)法對(duì)容器中未加密的敏感數(shù)據(jù)進(jìn)行優(yōu)化。
相反,用戶可以通過(guò)使用Kubernetes Secrets 對(duì)象將隱私信息存儲(chǔ)在容器外面,這樣更簡(jiǎn)單、安全。
Kubernetes 提供了一個(gè) Secrets 抽象,允許在 Docker 鏡像或 Pod 定義之外存儲(chǔ)隱私數(shù)據(jù)。用戶可以通過(guò)掛載卷或環(huán)境變量的方式把這些信息加載到容器中。更新時(shí),只需更換相關(guān)服務(wù)的 Pod 并使用新的證書即可。用戶也可以通過(guò) Hashicorp Vault 以及Bitnami Sealed Secrets來(lái)保存隱私數(shù)據(jù)。