前 言
作為后臺支撐,Kubernetes優(yōu)勢明顯蝇完,具有自動化部署湃密、服務伸縮、故障自我修復四敞、負載均衡等特性泛源。咪付的藍牙過閘系統(tǒng)和全態(tài)識別AI系統(tǒng)的后臺支撐采用了Kubernetes,經(jīng)過線上的長期運行忿危,其狀態(tài)良好運行平穩(wěn)达箍。
藍牙過閘系統(tǒng)和全態(tài)識別AI系統(tǒng)有著不同的數(shù)據(jù)特性,對數(shù)據(jù)的安全要求及運行效率也各不一樣铺厨,因此如何選擇容器的運行時成為了一個重點考慮的因素缎玫。
本文將介紹Kubernetes支持哪些容器運行時以及如何選擇合適的容器運行時。
這篇文章包含以下內(nèi)容:
介紹CRI接口規(guī)范內(nèi)容
介紹不同OCI項目的技術(shù)特點
CRI是如何銜接Kubelet和OCI層及當前CRI格局
Kubernetes如何支持多種容器運行時
如何選擇合適的容器運行時
01
CRI的由來
CRI(Container Runtime Interface)是Kubernetes提出的容器運行時接口規(guī)范解滓。
在Kubernetes體系中赃磨,是由Kubelet組件負責與容器運行時交互的。Kubelet調(diào)用容器運行時的流程如上圖所示洼裤。CRI shim是實現(xiàn)CRI接口的gRPC server服務邻辉,負責連接Kubelet和Container runtime,Container runtime是容器運行時工具腮鞍,它為用戶進程隔離出一個獨立的運行環(huán)境值骇;具體的流程是Kubelet調(diào)用CRI shim接口,CRI shim響應請求移国,然后調(diào)用底層的Container runtime工具運行容器吱瘩。Kubelet、CRI shim和Container runtime都部署在一個Kubernetes worker節(jié)點上迹缀,前兩者是以獨立的守護進程的方式啟動的使碾,而Container runtime不是守護進程,它通常是一個命令行工具祝懂。
Kubernetes在v1.5版本之前是沒有CRI接口的票摇,那時Kubelet源碼內(nèi)部只集成了兩個容器運行時(Docker和rkt)的相關(guān)代碼。這兩種容器運行時并不能滿足所有用戶的使用需求嫂易,在某些業(yè)務場景兄朋,用戶對容器的安全隔離性有著更高的需求掐禁,用戶希望Kubernetes能支持更多種類的容器運行時怜械。因此颅和,Kubernetes在1.5版本推出了CRI接口,各個容器運行時只要實現(xiàn)了CRI接口規(guī)范缕允,就可以接入到Kubernetes平臺為用戶提供容器服務峡扩。
CRI接口帶來的好處,首先它很好的將Kubernetes和容器運行時解耦障本,容器運行時的每次更新迭代教届,都不必對Kubelet工程源碼進行編譯發(fā)布;其次解放了容器運行時更新迭代的步伐驾霜,也能保證Kubernetes的代碼質(zhì)量和平臺的穩(wěn)定案训。
02
OCI是什么
OCI規(guī)范(Open Container Initiative 開放容器標準),該規(guī)范包含兩部分內(nèi)容:容器運行時標準(runtime spec)粪糙、容器鏡像標準(image spec)强霎。其具體內(nèi)容的定義如下圖:
容器運行時標準
runtime spec包含配置文件、運行環(huán)境蓉冈、生命周期三部分內(nèi)容城舞。
▲
- 配置文件 -
config.json,包含容器的配置信息(mounts寞酿、process家夺、hostname、hooks等)伐弹。
▲
- 運行環(huán)境 -
定義運行環(huán)境拉馋,是為了確保應用程序在多個運行時之間,能具有一致的環(huán)境 惨好。
▲
- 容器生命周期 -
定義了運行時的相關(guān)指令及其行為:state椅邓、create、start昧狮、kill景馁、delete、prestart hooks逗鸣、poststart hooks合住、poststop hooks。
容器鏡像標準
通常我們根據(jù)Dockerfile定義的內(nèi)容制作鏡像撒璧,目的是構(gòu)建一個符合OCI標準的鏡像文件透葛,那么OCI標準鏡像文件的內(nèi)容都有哪些呢?在OCI規(guī)范里的image spec對容器鏡像格式做了定義卿樱,它主要包括以下幾塊內(nèi)容:
▲
- 文件系統(tǒng) -
描述了如何以layer的方式疊加成一個完整的文件系統(tǒng)僚害,以及如何用layer去表示對文件作出的改動(增加、刪除繁调、修改)萨蚕。
▲
- config文件 -
保存了文件系統(tǒng)的層級信息(每個層級的 hash 值靶草、歷史信息),以及容器運行時需要的一些信息(比如環(huán)境變量岳遥、工作目錄奕翔、命令參數(shù)、mount 列表等)浩蓉。
▲
- manifest文件 -
記錄鏡像元信息派继,包括Image Config和Image Layers。
▲
- index文件 -
可選的文件捻艳,指向不同平臺的 manifest 文件驾窟,相當于整個鏡像的入口,從這個文件可以獲取整個鏡像依賴的所有文件信息认轨。
OCI項目
下表是兼容OCI規(guī)范的容器運行時項目:
容器運行時對比
按照底層容器運行環(huán)境依托的技術(shù)分類纫普,我們將容器運行時分為以下三類:
OSContainerRuntime(基于進程隔離技術(shù))
HyperRuntime(基于Hypervisor技術(shù))
UnikernelRuntime(基于unikernel)
通過下圖對比它們之間的區(qū)別:
OSContainerRuntime下的Linux Container共享Linux內(nèi)核,使用namespace好渠、cgroup等技術(shù)隔離進程資源昨稼。namespace只包含了六項隔離(UTS、IPC拳锚、PID假栓、Network、Mount霍掺、User)匾荆,并非所有Linux資源都可以通過這些機制控制,比如時間和Keyring杆烁,另外牙丽,容器內(nèi)的應用程序和常規(guī)應用程序使用相同的方式訪問系統(tǒng)資源,直接對主機內(nèi)核進行系統(tǒng)調(diào)用兔魂。因此即使有了很多限制烤芦,內(nèi)核仍然向惡意程序暴露過多的攻擊面。
HyperRuntime下的VM Container容器各自擁有獨立Linux內(nèi)核析校,資源隔離比Linux Container更徹底构罗。但并不是說使用VM容器用戶就可以高枕無憂,只是VM容器的攻擊面比Linux容器小了很多智玻,黑客要逃逸到宿主機就只剩下Hypervisor這個入口遂唧,所以說沒有絕對的安全,相對來說VM容器更安全吊奢。
另一方面盖彭,VM容器的性能比不上Linux容器,因為Hypervisor這一層帶來的性能損耗,在Linux容器這邊是不存在的召边。
UnikernelRuntime下的容器同VM Container一樣有著安全級別很高的運行環(huán)境铺呵,同樣是使用Hypervisor技術(shù)進行容器隔離。
簡單來說Unikernel是一個運行在Hypervisor之上的libOS系統(tǒng)掌实,而libOS是由應用程序和libraries一起構(gòu)建出的操作系統(tǒng)。
unikernel的特點如下:
性能好邦马,應用程序和內(nèi)核在同一地址空間贱鼻,消除了用戶態(tài)和內(nèi)核態(tài)轉(zhuǎn)換以及數(shù)據(jù)復制帶來的開銷。
更精簡的內(nèi)核滋将,去掉了多余的驅(qū)動邻悬、依賴包、服務等随闽,最終打包鏡像更小父丰,啟動速度更快。
完全不可調(diào)試掘宪,在生產(chǎn)環(huán)境中如果遇到問題蛾扇,只能依賴于收集到的日志進行排查,要不就是重啟容器魏滚,原先熟悉的Linux排查方法和工具完全派不上用場镀首。
03
Kubelet CRI架構(gòu)
Kubernetes在引入CRI之后,Kubelet的架構(gòu)如下圖所示:
每一個容器引擎只需要自己實現(xiàn)一個CRI shim鼠次,對CRI請求進行處理更哄,就可以接入Kubelet當中去。
我們所說的容器運行時腥寇,準確來說包含兩部分成翩,一部分是上層容器運行時CRI shim(即容器運行時管理程序,如Containerd赦役、CRI-O)麻敌,另一部分是下層容器運行時Container runtime(即容器運行時命令工具,如runc)掂摔。
04
CRI接口定義
CRI接口分為兩部分庸论,一個是容器運行時服務RuntimeService,負責管理pod和容器的生命周期棒呛;一個是鏡像服務ImageService聂示,負責管理鏡像的生命周期。
05
當前CRI格局
目前實現(xiàn)了CRI的主流項目有:docker簇秒、containerd鱼喉、CRI-O、Frakti、pouch扛禽,它們銜接Kubelet與運行時方式對比如下:
PS:由于rkt容器引擎目前未能完全兼容OCI規(guī)范锋边,所以圖中未將其包含進來。
- Docker -
Docker容器引擎安裝后包含有這些組件:dockerd编曼、Containerd豆巨、runc。
dockershim是內(nèi)置在Kubelet的CRI gRPC server服務掐场,它接收CRI請求后欢伏,通過unix本地套接字調(diào)用dockerd的API接口远豺,再由dockerd請求下游的運行時組件Containerd和runc蜗顽。調(diào)用的鏈路:dockershim => dockerd => Containerd => runc验懊。
在當前CRI的請求鏈路上,dockerd只是簡單接收CRI請求嚷堡,在轉(zhuǎn)換之后調(diào)用Containerd蝗罗,dockerd還集成有其他功能,如network蝌戒、swarm串塑、volume等在Kubernetes平臺下沒有用武之地,可以說在CRI場景下docker顯得笨重北苟。
- Containerd -
Containerd項目是從早期的docker源碼中提煉出來的拟赊,它使用CRI插件來向kubelet提供CRI接口服務。
CRI插件是一個獨立的項目粹淋,在Containerd編譯時吸祟,如果go build命令沒有顯示設(shè)置參數(shù)-tags=no_cri,那么CRI插件將自動編譯集成到Containerd的二進制文件中桃移,然后在配置文件/etc/containerd/config.toml中聲明啟用CRI插件屋匕,就可以在Containerd中啟動CRI shim服務了。
Containerd能支持多運行時借杰,目前它內(nèi)置了runc運行時过吻,其他運行時如果要接入Containerd,則需要實現(xiàn)Containerd shim v2 gRPC接口蔗衡,這樣Containerd就可以通過shim v2調(diào)用其他運行時纤虽。他們的調(diào)用關(guān)系如下:Containerd --> shim v2 --> runtimes
- CRI-O -
CRI-O完整實現(xiàn)CRI接口功能,并且嚴格兼容OCI標準绞惦,CRI-O比Containerd更專注逼纸,它只服務于Kubernetes(而Containerd除支持Kubernetes CRI,還可用于Docker Swarm)济蝉,從官網(wǎng)上我們可以了解到CRI-O項目的功能邊界:
支持多種image格式
支持多種image下載方式
容器鏡像管理
容器生命周期管理
提供CRI要求的監(jiān)控杰刽、日志功能
提供CRI要求的資源隔離功能
CRI-O通過命令行調(diào)用默認運行時runc菠发,所以runc二進制文件必須部署在目錄/usr/bin/runc。CRI-O和Containerd調(diào)用runtime的方式不同贺嫂,前者是通過linux命令調(diào)用滓鸠,后者是通過gRPC服務調(diào)用,所以只要符合OCI規(guī)范的runtime第喳,都能直接接入CRI-O提供運行時服務糜俗,而除runc外的其他運行時要接入Containerd,只能走shim v2接口曲饱,因此我們看到像kata-runtime這樣的運行時項目就是通過shim v2接口來適配Containerd的悠抹。
Frakti
Frakti是基于Hypervisor虛擬機管理程序的容器運行時,它相比其他的容器運行時具有如下這些功能特性:
- PouchContainer -
PouchContainer是阿里開源的容器引擎渔工,它內(nèi)部有一個CRI協(xié)議層和cri-manager模塊锌钮,用于實現(xiàn)CRI shim功能桥温。它的技術(shù)優(yōu)勢包括:
1. 強隔離引矩,包括的安全特性:基于Hypervisor的容器技術(shù)、lxcfs侵浸、目錄磁盤配額旺韭、補丁Linux內(nèi)核等。
2. 基于P2P鏡像分發(fā)掏觉,利用P2P技術(shù)在各節(jié)點間互傳鏡像区端,減小鏡像倉庫的下載壓力,加快鏡像下載速度澳腹。
3. 富容器技術(shù)织盼,PouchContainer的容器中除了運行業(yè)務應用本身之外,還有運維套件酱塔、系統(tǒng)服務沥邻、systemd進程管家等。
06
CRI命令工具
cri-tools是由kubernetes-sigs 開發(fā)維護的CRI命令工具(后簡稱crictl)羊娃,它是為了在Kubernetes node節(jié)點上管理鏡像唐全、管理pod/container、與容器進行交互而設(shè)計的蕊玷,crictl調(diào)用CRI接口獲取相關(guān)信息邮利、或者通過CRI的exec接口與容器交互。
crictl并不是docker cli的完全繼任者垃帅,它僅提供了在Kubernetes node節(jié)點上常用的一些運維功能延届,具備一個較小的功能子集,而docker cli的命令更強大贸诚,事實上很多docker命令在生產(chǎn)環(huán)境并沒有必要使用祷愉,所以crictl相對來說更安全窗宦,它避免運維人員在生產(chǎn)節(jié)點非法使用docker cli(rename、rm二鳄、rmi等子命令)造成一些人為的故障赴涵。所有實現(xiàn)了CRI接口的容器運行時,都可以通過crictl工具對其進行操作订讼。
07
Kubernetes支持多運行時
為什么要支持多運行時呢髓窜?舉個例子,有一個開放的云平臺向外部用戶提供容器服務欺殿,平臺上運行有兩種容器寄纵,一種是云平臺管理用的容器(可信的),一種是用戶部署的業(yè)務容器(不可信)脖苏。在這種場景下程拭,我們希望使用runc運行可信容器(弱隔離但性能好),用runv運行不可信容器(強隔離安全性好)棍潘。面對這種需求恃鞋,Kubernetes也給出了解決方案(使用API對象RuntimeClass支持多運行時)。
多運行時工作原理
Kubelet從apiserver接收到的Pod Spec亦歉,如果Pod Spec中使用runtimeClassName指定了容器運行時恤浪,則在調(diào)用CRI接口的RunPodSandbox()函數(shù)時,會將runtimeClassName信息傳遞給CRI shim肴楷,然后CRI shim根據(jù)runtimeClassName去調(diào)用對應的容器運行時水由,為Pod創(chuàng)建一個隔離的運行環(huán)境。
RuntimeClass配置
Kubernetes在v1.12中增加了RuntimeClass這個新API對象來支持多運行時(目的就是在一個woker節(jié)點上運行多種運行時)赛蔫。
在Kubernetes中啟用RuntimeClass時需要注意砂客,盡量保持當前Kubernetes集群中的節(jié)點在容器運行時方面的配置都是同構(gòu)的,如果是異構(gòu)的呵恢,那么需要借助node Affinity等功能來調(diào)度Pod到已部署有匹配容器運行時的節(jié)點鞠值。
接下來只需要三步配置就可以為pod指定運行時:
1. 在Kubernetes worker節(jié)點配置CRI shim;
2. 創(chuàng)建RuntimeClass資源對象瑰剃;
3. 在pod中指定RuntimeClass齿诉。
▲
- 配置CRI shim -
例如CRI-O運行時的配置,需要在文件/etc/crio/crio.conf定義runtime的handler_name:
[crio.runtime.runtimes.${HANDLER_NAME}]
runtime_path = "${PATH_TO_BINARY}"
▲
- 創(chuàng)建RuntimeClass -
RuntimeClass yaml定義如下:
apiVersion: node.k8s.io/v1beta1 # RuntimeClass is defined in the node.k8s.io API group
kind: RuntimeClass
metadata:
name: myclass # The name the RuntimeClass will be referenced by
# RuntimeClass is a non-namespaced resource
handler: myconfiguration # The name of the corresponding CRI configuration
▲
- 配置pod -
在pod yaml中指定RuntimeClassName:
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
runtimeClassName: myclass
# ...
08
如何選擇合適的容器運行時
在生產(chǎn)環(huán)境中晌姚,我們并不需要docker的鏡像打包粤剧、容器網(wǎng)絡(luò)、文件掛載挥唠、swarm等這些能力抵恋,只需要部署Containerd + runc就可以在node節(jié)點上運行pod。因此在生產(chǎn)環(huán)境中我們可以不安裝docker宝磨,而是安裝CRI shim組件和運行時工具來運行pod弧关。在多個CRI shim和OCI工具之間盅安,我們該如何選擇呢?
首先對比Containerd和CRI-O調(diào)用runc的方式世囊,runc代碼內(nèi)置在Containerd內(nèi)部别瞭,通過函數(shù)調(diào)用;CRI-O是通過linux命令方式調(diào)用runc二進制文件株憾,顯然前者屬于進程內(nèi)的函數(shù)調(diào)用蝙寨,在性能上Containerd更具優(yōu)勢。其次對比runc和runv嗤瞎,這是兩種完全不同的容器技術(shù)墙歪,runc創(chuàng)建的容器進程直接運行在宿主機內(nèi)核上,而runv是運行在由Hypervisor虛擬出來的虛擬機上贝奇,后者占用的資源更多虹菲、啟動速度慢,而且runv容器在調(diào)用底層硬件時(如CPU)掉瞳,中間多了一層虛擬硬件層毕源,計算效率上不如runc容器。
因此建議結(jié)合自身業(yè)務特點菠赚、以及使用場景選擇合適的容器運行時脑豹。在對用戶的隔離沒有很高訴求的情況下郑藏,可以優(yōu)先考慮使用性能更好更輕量的Containerd + runc衡查;在隔離性要求較高的業(yè)務場景下,推薦使用基于Hypervisor 的虛擬化容器運行時Frakti + runv必盖。
轉(zhuǎn)自 https://toutiao.io/posts/eod7r9/preview