Containerd鏡像lazy-pulling解讀

今天zouyee朋友段全鋒童鞋爲你們帶來《Containerd鏡像lazy-pulling解讀》,其中《kuberneter調度由淺入深:框架》正在編寫中,敬請期待。git

1、背景github

咱們知道,容器運行起來的時間是很是快的,可是若是節點上容器的鏡像不存在,那麼在運行容器時要先拉取鏡像,拉取鏡像在容器啓動的過程當中佔用的時間比較長,這個過程要將容器全部的鏡像層都拉取到本地磁盤中。據統計,拉鏡像操做要佔用容器啓動時間的76%。這在容器數量少的狀況下問題不大,但容器數量比較多而且都是冷啓動的時候會很是的慢。golang

如何解決容器冷啓動過程當中拉取鏡像慢這個問題?有這樣的一種解決思路:在容器啓動過程當中,容器要用的鏡像經過高速網絡按需從鏡像倉庫中讀取,而不是將鏡像全部的層都拉下來。Stargz-snapshotter是containerd下面的一個子項目,以Proxy Plugin的方式擴展containerd的功能,是Containerd的一個remote-snapshotter實現。docker

版本變遷centos

2、使用api

Stargz-snapshotter在kuberentes中使用比較簡單:使用Containerd做爲kuberentes的CRI運行時。在本地起一個stargz-snapshotter的服務,做爲containerd的一個remote snapshotter。緩存

鏡像轉換markdown

在使用前須要將咱們的普通的鏡像轉換成stargz-snapshotter能夠識別的鏡像,使用ctr-remote工具進行轉換,下面示例是將本地一個centos鏡像進行轉換,轉換完成後推送到鏡像倉庫中:網絡

$ ctr-remote image optimize --plain-http --entrypoint='[ "sleep" ]' --args='[ "3000" ]' centos:7 centos:7-eg
複製代碼

對比session

使用crictl工具在本地拉取轉換前和轉換後的鏡像,作一下對比,經過lazy的方式拉取鏡像的速度更快:

# 正常拉取鏡像
$ time crictl pull centos:7
Image is up to date for sha256:ef8f4eaacef5da519df36f0462e280c77f8125870dbb77e85c898c87fdbbea27real  
0m5.967suser  0m0.009ssys  0m0.012s
# 拉取優化後的鏡像
$ time crictl pull centos:7-eg
Image is up to date for sha256:36edf0c0bb4daca572ba284057960f1441b14e0d21d5e497cb47646a22f653d6real  
0m0.624suser  0m0.012ssys  0m0.010s
複製代碼

查看鏡像層

使用crictl建立一個pod,進入容器中,查看/.stargz-snapshotter/目錄下各個鏡像層在本地緩存的狀況:

$ cat /.stargz-snapshotter/*{"digest":"sha256:857949cb596c96cc9e91156bf7587a105a2e1bc1e6db1b9507596a24a80f351a","size":80005845,"fetchedSize":3055845,"fetchedPercent":3.819527185794988}{"digest":"sha256:8c57b1a6bef1480562bc27d145f6d371955e1f1901ebdea590f38bfedd6e17d0","size":33614550,"fetchedSize":64550,"fetchedPercent":0.19202993941611593}
複製代碼

3、原理

上圖是stargz-snapshotter的實現概覽,一般的咱們在拉取鏡像時,要將鏡像的每一層拉取下來,而使用stargz-snapshotter後containerd再也不是拉取鏡像的層,而是爲存儲在鏡像倉庫中鏡像的每一層在容器運行節點上建立一個目錄,經過遠程掛載的方式掛到各個目錄上。容器啓動前再將各個目錄作overlay掛載,爲容器提供一個rootfs。當須要讀取某個文件時,經過網絡讀取鏡像倉庫中鏡像層中的文件。

下面再看一下鏡像層是怎麼遠程掛載和如何從鏡像層中按需讀取文件的。

用戶態文件系統

Stargz-snapshotter使用FUSE實現了用戶態的文件系統。FUSE(Filesystem in userspace)框架是一個內核模塊,可以讓用戶在用戶空間實現文件系統而且掛載到某個目錄,就像在內核實現文件系統同樣。

如上圖所示,stargz-snapshotter是一個實現了用戶態文件系統的程序(golang語言,使用go-fuse做爲實現的依賴)。當有拉取鏡像的操做發生時,stargz-snapshotter會爲鏡像的每一層在${stargz-root}/snapshotter/snapshots/下建立一個目錄,執行實現一個文件系統的邏輯,並將這個文件系統掛載到剛建立的目錄上,例如圖中的/dcos/snapshotter/snapshots/1。當有用戶讀取目錄下的文件時,請求的流向是這樣的:

① 操做請求經VFS到FUSE

② FUSE內核模塊根據請求類型,調用stargz-snapshotter的邏輯,stargz-snapshotter從鏡像倉庫中讀取該層中的文件

③ Stargz-snapshotter將文件的內容經過VFS返回給系統調用

(e)stargz格式

a. stargz格式

一般存放在鏡像倉庫中的鏡像層都是使用gzip壓縮過的,咱們不能從這個壓縮後的文件中提取單個文件。那stargz-snapshotter是怎麼作到從單個鏡像層中讀取單個文件的呢?

Stargz-snapshotter使用了另外一種壓縮鏡像層的格式,它也是gzip包,一種可seekable的gzip包,圖3是targz和stargz的對比。壓縮包裏的文件能夠被檢索和抽取,但還是zip格式的文件;鏡像層中的每一個文件都會被打成一個zip包,最後再組成一個大的zip包;整個zip包中有一個TOC文件,它記錄了包中每一個文件的偏移量;Footer佔最後47個字節,記錄了TOC在整個zip包中的偏移量。

這樣就能夠經過鏡像層最後47個字節的Footer,找到TOC的偏移量,而後讀取TOC的內容就能獲得整個鏡像層中有哪些文件,每一個文件的偏移量是多少。Stargz-snapshotter就是經過這個TOC文件去按需檢索整個鏡像層中文件的。

b. estartgz格式

默認狀況下,將鏡像的某一層遠程掛載到目標主機後,stargz-snapshotter默認會建立一個後臺任務去緩存鏡像層。在容器啓動過程當中,若是容器啓動須要的文件沒有在本地緩存那麼stargz-snapshotter就須要經過網絡去鏡像倉庫中讀取,這會致使容器啓動速度比較慢。

estargz是對stargz格式進行了優化,如上圖所示。它多了一個landmark文件,這個文件將鏡像層中的文件分紅了兩類:一類是容器運行時最有可能用到的,另外一類是可能用不到的。這樣後臺任務會優先去緩存那些容器運行時須要的文件,這樣會增長本地緩存的命中率,加快容器的啓動速度。

下圖是三種鏡像層的對比狀況,legacy是普通鏡像層,stargz是stargz格式的鏡像層,estargz是優化後stargz格式的鏡像層。

c. 分層拉取鏡像

鏡像層使用estargz格式能夠作到從壓縮包中檢索文件,那stargz是如何從鏡像倉庫中按照分片獲取文件所有或者部分數據的?

在OCI規範中有關於如何從倉庫中獲取部分數據的描述,而docker registry也有對應接口實現。

Registry中獲取鏡像層部署數據的接口以下:

其中,name就是目標repository的名稱,digest就是鏡像層blob的digest的值,Host就是鏡像倉庫的地址,Range描述的就是要獲取的blob分片。

返回的響應以下:

其中,start是開始的字節,end是結束的字節,size是層大小,length是本次請求的層分片。

lazy-pulling流程

Containerd使用stargz-snapshotter拉取鏡像的流程以下:

① 根據鏡像名稱和tag解析出鏡像manifest的digest的值

② 根據鏡像manifest的digest的值,從鏡像倉庫中下載manifest,保存在content store中

③ 根據manifest的內容獲取鏡像config的digest的值,從鏡像倉庫中下載config,保存在content store中

④ 解析鏡像的每一層,建立snapshot,若是containerd使用的stargz-snapshotter,它會返回一個snapshot已經存在的錯誤。Stargz-snapshotter PrepareSnapshot的邏輯就是爲當前層準備文件系統並掛載到本地的一個過程。

⑤ 全部鏡像層解析完成後會保存鏡像的元數據

4、小結

建立容器時,拉取鏡像過程在容器啓動時間的佔比高,一般咱們會使用多種方法去製做儘可能小一點的鏡像,或者經過P2P網絡去分發鏡像。鏡像lazy pull是另外一種提升鏡像分發速度的方式。

使用stargz-snapshotter在鏡像拉取時,僅將鏡像的manifest和config下載下來,並鏡像每一層經過遠程掛載的方式掛到當前主機上,容器運行時達到按需讀取文件的效果。而傳統方式是將鏡像的每一層都下載到本地進行解壓。相比而言前者能加快鏡像的拉取速度,加快容器冷啓動的速度。但須要注意,文件是按需加載的,它依賴於一個比較好的網絡環境。

後續相關內容,請查看公衆號:

參考資料

1. www.usenix.org/conference/…

2. github.com/google/crfs

3. github.com/opencontain…

4. docs.docker.com/registry/sp…

相關文章
相關標籤/搜索