天天看點

docker檢視正在運作的容器_幹貨 | 容器運作時從docker到containerd的遷移

docker檢視正在運作的容器_幹貨 | 容器運作時從docker到containerd的遷移

供稿 | eBay Infrastructure Engineering 蘇菲

翻譯&編輯 | 顧欣怡

本文2634字,預計閱讀時間8分鐘

更多幹貨請關注“eBay技術荟”公衆号

docker檢視正在運作的容器_幹貨 | 容器運作時從docker到containerd的遷移

導讀

目前,docker是kubernetes預設的容器運作時(Container Runtime)。由于docker過于複雜,操作不便,eBay将容器運作時從docker遷移到containerd,并将存儲驅動程式Device Mapper換成Overlayfs。盡管在遷移過程中,我們遇到了不少挑戰,但都一一克服并最終完成了此次遷移。

docker檢視正在運作的容器_幹貨 | 容器運作時從docker到containerd的遷移

容器運作時(Container Runtime),運作于kubernetes(k8s)叢集的每個節點中,負責容器的整個生命周期。其中docker是目前應用最廣的。随着容器雲的發展,越來越多的容器運作時湧現。為了解決這些容器運作時和k8s的內建問題,在k8s 1.5版本中,社群推出了CRI(Container Runtime Interface, 容器運作時接口)(如圖1所示),以支援更多的容器運作時。

Kubelet通過CRI和容器運作時進行通信,使得容器運作時能夠像插件一樣單獨運作。可以說每個容器運作時都有自己的優勢,這就允許使用者更容易選擇和替換自己的容器運作時。

docker檢視正在運作的容器_幹貨 | 容器運作時從docker到containerd的遷移

圖1 CRI在kubernetes中的位置

一、CRI & OCI

docker檢視正在運作的容器_幹貨 | 容器運作時從docker到containerd的遷移

CRI是kubernetes定義的一組gRPC服務。Kubelet作為用戶端,基于gRPC架構,通過Socket和容器運作時通信。它包括兩類服務:鏡像服務(Image Service)和運作時服務(Runtime Service)。鏡像服務提供下載下傳、檢查和删除鏡像的遠端程式調用。運作時服務包含用于管理容器生命周期,以及與容器互動的調用(exec / attach / port-forward)的遠端程式調用。

如圖2所示,dockershim, containerd 和cri-o都是遵循CRI的容器運作時,我們稱他們為高層級運作時(High-level Runtime)。

docker檢視正在運作的容器_幹貨 | 容器運作時從docker到containerd的遷移

圖2 常用的運作時舉例

OCI(Open Container Initiative,開放容器計劃)定義了建立容器的格式和運作時的開源行業标準,包括鏡像規範(Image Specification)和運作時規範(Runtime Specification)。

鏡像規範定義了OCI 鏡像的标準。如圖2所示,高層級運作時将會下載下傳一個OCI 鏡像,并把它解壓成OCI 運作時檔案系統包(filesystem bundle)。

運作時規範則描述了如何從OCI 運作時檔案系統包運作容器程式,并且定義它的配置、運作環境和生命周期。如何為新容器設定命名空間(namepsaces)和控制組(cgroups),以及挂載根檔案系統等等操作,都是在這裡定義的。它的一個參考實作是runC。我們稱其為低層級運作時(Low-level Runtime)。除runC以外,也有很多其他的運作時遵循OCI标準,例如kata-runtime。

docker檢視正在運作的容器_幹貨 | 容器運作時從docker到containerd的遷移

二、Containerd vs Cri-o

目前docker仍是kubernetes預設的容器運作時。那為什麼會選擇換掉docker呢?主要的原因是它的複雜性。

如圖3所示,我們總結了docker, containerd以及cri-o的詳細調用層級。Docker的多層封裝和調用,導緻其在可維護性上略遜一籌,增加了線上問題的定位難度(貌似除了重新開機docker,我們就毫無他法了)。Containerd和cri-o的方案比起docker簡潔很多。是以我們更偏向于選用更加簡單和純粹的containerd和cri-o作為我們的容器運作時。

docker檢視正在運作的容器_幹貨 | 容器運作時從docker到containerd的遷移

圖3 容器運作時調用層級

我們對containerd和cri-o進行了一組性能測試,包括建立、啟動、停止和删除容器,以比較它們所耗的時間。如圖4所示,containerd在各個方面都表現良好,除了啟動容器這項。從總用時來看,containerd的用時還是要比cri-o要短的。

docker檢視正在運作的容器_幹貨 | 容器運作時從docker到containerd的遷移

圖4 containerd和crio的性能比較

如圖5所示,從功能性來講,containerd和cri-o都符合CRI和OCI的标準。從穩定性來說,單獨使用containerd和cri-o都沒有足夠的生産環境經驗。但慶幸的是,containerd一直在docker裡使用,而docker的生産環境經驗可以說比較充足。可見在穩定性上containerd略勝一籌。是以我們最終選用了containerd。

docker檢視正在運作的容器_幹貨 | 容器運作時從docker到containerd的遷移

圖5 containerd和cri-o的綜合比較

docker檢視正在運作的容器_幹貨 | 容器運作時從docker到containerd的遷移

三、Device Mapper vs. Overlayfs

容器運作時使用存儲驅動程式(storage driver)來管理鏡像和容器的資料。目前我們生産環境選用的是Device Mapper。然而目前Device Mapper在新版本的docker中已經被棄用,containerd也放棄對Device Mapper的支援。

當初選用Device Mapper,也是有曆史原因的。我們大概是在2014年開始k8s這個項目的。那時候Overlayfs都還沒合進kernel。當時我們評估了docker支援的存儲驅動程式,發現Device Mapper是最穩定的。是以我們選用了Device Mapper。但是實際使用下來,由Device Mapper引起的docker問題也不少。是以我們也借這個契機把Device Mapper給換掉,換成現在containerd和docker都預設的Overlayfs。

從圖6的測試結果來看,Overlayfs的IO性能比Device Mapper好很多。Overlayfs的IOPS大體上能比Device Mapper高20%,和直接操作主機路徑差不多。

docker檢視正在運作的容器_幹貨 | 容器運作時從docker到containerd的遷移

圖6 後端存儲檔案系統性能比較

四、遷移方案

docker檢視正在運作的容器_幹貨 | 容器運作時從docker到containerd的遷移

最終,我們選用了containerd,并以Overlayfs作為存儲後端的檔案系統,替換了原有的docker加Device Mapper的搭配。那遷移前後的性能是否得到提升呢?我們在同一個節點上同時起10,30,50和80的pod,然後再同時删除,去比較遷移前後建立和删除的用時。從圖7和圖8可知,containerd用時明顯優于docker。

docker檢視正在運作的容器_幹貨 | 容器運作時從docker到containerd的遷移

圖7 建立pod的用時比較

docker檢視正在運作的容器_幹貨 | 容器運作時從docker到containerd的遷移

圖8 删除pod的用時比較

五、遷移挑戰

docker檢視正在運作的容器_幹貨 | 容器運作時從docker到containerd的遷移

從docker+Device Mapper到containerd+ Overlayfs,容器運作時的遷移并非易事。這個過程中需要删除Device Mapper的thin_pool,全部重新下載下傳使用者的容器鏡像,全新重建使用者的容器。

如圖9所示,遷移過程看似簡單,但是這對于已運作了5年且擁有100K+光怪陸離的應用程式的叢集而言,如何将使用者的影響降到最低才是最難的。Containerd在我們生産環境中是否會出現“重大”問題也未可知。

docker檢視正在運作的容器_幹貨 | 容器運作時從docker到containerd的遷移

圖9 具體的遷移步驟

針對這些挑戰,我們也從下面幾個方面做出了優化,來保證我們遷移過程的順利進行。

01

多樣的遷移政策

最基本的是以容錯域(Fault Domain, fd)為單元遷移。針對我們叢集,是以rack(機架)為單元(rack by rack)遷移。針對雲原生(cloud-native)且跨容錯域部署的應用程式,此更新政策最為安全有效。針對非雲原生的應用程式,我們根據其特性和部署拓撲,定制了專屬他們的更新政策,例如針對Cassini的叢集,我們采用了jenga(層層疊)的更新政策,保證應用程式0當機。

02

自動化的遷移過程

以rack by rack的政策為例,需要等到一個rack遷移完成以後且客戶應用程式恢複到遷移前的狀态,才能進行下一個rack的遷移。是以我們對遷移控制器(Controller)進行了加強,利用控制平面(Control Plane)的監控名額(Metrics)和資料平面(Data Plane, 即應用程式)的告警(Alerts),實作典型問題的自動幹預和修複功能,詳見圖10。如果問題不能被修複,錯誤率達到門檻值,遷移才會被暫停。對于大叢集,實作了人為的0幹預。

docker檢視正在運作的容器_幹貨 | 容器運作時從docker到containerd的遷移

圖10 自動化遷移流程

03

高可用的鏡像倉庫

一個rack共有76台機器。假設每個機器上隻有50個pod,就可能最多有3800個鏡像需要下載下傳。這對鏡像倉庫的壓力是非常大的。除了使用本地倉庫,這次遷移過程中還使用了基于gossip協定的鏡像本地緩存的功能,來減少遠端服務端的壓力,具體參見圖11。

docker檢視正在運作的容器_幹貨 | 容器運作時從docker到containerd的遷移

圖11 鏡像倉庫架構

04

可逆的遷移過程

雖然我們對containerd的問題修複是有信心的,但是畢竟缺少生産環境經驗,得做好随時回退的準備。一旦發現遷移後,存在極大程度影響叢集的可靠性和可用性的問題,我們就要換回docker。雖然遷移後,線上上的确發現了鏡像不能成功下載下傳,容器不能啟動和删除等問題,但是我們都找到了根本原因,并修複。是以令人慶幸的是,這個回退方法并未發揮其作用。

docker檢視正在運作的容器_幹貨 | 容器運作時從docker到containerd的遷移

六、使用者體驗

容器運作時是kubernetes的後端服務。容器運作時的遷移不會改變任何的使用者體驗。但是有一個Overlayfs的問題需要特别說明一下。如果容器的基礎鏡像(Base Image)是centos6,利用Dockerfile去建立鏡像時,如果用yum去安裝包,或者在運作的centos6容器中用yum安裝包的,會報以下錯誤:

docker檢視正在運作的容器_幹貨 | 容器運作時從docker到containerd的遷移

因為yum在安裝包的過程中,會先以隻讀模式,然後再以寫模式去打開rmpdb檔案。

docker檢視正在運作的容器_幹貨 | 容器運作時從docker到containerd的遷移

如圖12所示,對于Overlayfs來說,以隻讀模式打開一個檔案的話,檔案直接在下層(lower layer)被打開,我們得到一個fd1。當我們再以寫模式打開,就會觸發一個copy_up。rmpdb就會拷貝到上層(upper layer)。檔案就會在上層打開得到fd2。這兩個fd本來是想打開同一個檔案,事實卻并非如此。

docker檢視正在運作的容器_幹貨 | 容器運作時從docker到containerd的遷移

圖12

docker檢視正在運作的容器_幹貨 | 容器運作時從docker到containerd的遷移

圖13

解決方案就是在執行yum指令之前先裝一個yum-plugin-ovl插件。這個插件就是去做一個初始的copy_up,如圖13所示。将rpmdb先拷貝到上層,參考Dockerfile如下:

docker檢視正在運作的容器_幹貨 | 容器運作時從docker到containerd的遷移

如果基礎鏡像是centos7,則沒有這個問題,因為centos7的基礎鏡像已經有這個規避方法了。

七、總結

docker檢視正在運作的容器_幹貨 | 容器運作時從docker到containerd的遷移

目前我們50個叢集,20K+的節點已經全部遷到containerd,曆時2個月(非執行時間)。從目前情況來看,還比較穩定。雖然遷移過程中也出了不少問題,但經過各個小組的不懈努力,此次遷移終于順利完成了。

docker檢視正在運作的容器_幹貨 | 容器運作時從docker到containerd的遷移

↓點選

 eBay大量優質職位,等的就是你!