極簡容器化交付 | 0命令行完成鏡像上傳
今天要為大家介紹的是用戶0命令行,通過WEB界面實現鏡像的上傳及實現原理剖析。
我們從這個最為常用並極為簡單的docker push功能開始講,為什麽呢?由於我們在與客戶交流過程中發現,大多數都未接觸過容器化管理系統,甚至鏡像,對後端操作不熟悉的他們,對頁面操作是有一定需求的。目前主流的PaaS平臺基本都支持通過頁面操作構建鏡像、創建集群、創建應用等等,它們都在不斷地封裝底層集群管理系統(如kubernetes)的接口,設計一款對於雲下用戶友好的前端頁面,讓盡可能多的後端復雜操作可以通過鼠標的幾次點擊完成。
我們可以將這個趨勢解釋為,用戶的業務雲化的成本(包括金錢成本和時間成本)越低,上雲的傾向也就越大。如今,我們支持用戶在頁面上完成構建、部署等操作,如果可以實現鏡像上傳下載都在頁面上完成,用戶就可以在嘗試雲化的早期盡可能避開後端操作,將盡可能多的時間成本花在業務調試上,普通運維人員不需要熟悉docker命令,也可以從內網或者第三方鏡像倉庫下載鏡像,上傳並完成升級操作。
接下來,我們從鏡像上傳邏輯和鏡像結構開始講起,闡述如何去實現頁面上傳鏡像的功能。
後端上傳鏡像流程分析
我們的目的是實現另一種鏡像上傳方式,首先要了解原生的鏡像上傳流程是怎樣的。
上傳鏡像層
docker push時,最先被上傳的是鏡像層文件。如下面的busybox,每一行的short ID都表示著一個鏡像層的sha256值,它有兩個鏡像層:
上傳元數據文件
由於層之間有順序依賴關系,我們可以想到,上傳的層文件是不足以完備地描述整個鏡像的。除了鏡像層文件外,docker push的時候還額外會上傳一個鏡像的元數據文件。該文件主要保存了鏡像的環境變量、層結構、構建信息等等,並且它的sha256值就是鏡像的ID。由於字段太多,在此不詳細列出各字段的含義,感興趣的朋友可以使用docker inspect命令查看,參閱docker官方文檔了解一下。
上傳manifest
你們是否註意到,每個鏡像在上傳結束之後,屏幕上都會多一行xxx: digest: xxx size: xxx
,最後一行信息的打印,標識著鏡像最後一部分數據上傳完成,這部分數據就是manifest,而digest後面的長ID,就是manifest的sha256值。
manifest主要是負責關聯鏡像的元數據文件和鏡像層。在所有層都上傳結束後,它才被傳到倉庫端的,用於校驗是否所有實體文件都上傳完成。通過抓包或者查閱官方文檔,我們可以得知,manifest的結構是這樣的:
由上述分析可知,要完備地描述一個鏡像,需要存儲如下數據:
鏡像層
元數據文件
Manifest
我們接下來分析一下,從docker save生成的鏡像包裏,我們是否能獲取到這些數據。
鏡像壓縮包結構分析
通過docker save保存鏡像壓縮包,解壓開之後,可以發現,它的文件結構是比較有序的。
根目錄下有這三個文件:
此外,包內還有多個以長ID命名的目錄,每個目錄下均有如下三個文件:
這裏,有兩個較為普遍的誤區需要澄清一下:
誤區一:manifest.json就是manifest
manifest裏描述的是元數據文件名稱,以及各個層的sha256值,此外,還有它們的大小。
而manifest.json裏存放的不是完整的manifest信息,它僅僅記錄了元數據文件的全路徑名稱,以及各個鏡像層的全路徑名稱,沒有記錄各個層的sha256值和大小。
誤區二:各個層所在的目錄名就是鏡像層的sha256值
其實目錄名是用各個層的鏈ID(chain ID)和關聯父層的鏈ID聯合計算出來的一個特殊sha256值。這個特殊的sha256值,我們可以稱之為v1 ID,它被設計於兼容較早版本(1.10之前)的docker鏡像,早期版本,一個鏡像中可能存在多個sha256值相同的層(如空層)。
順帶提一下,上面的鏈ID是docker daemon使用遞歸的方式將每一層與依賴的所有父層聯合算出sha256得到的,它可以有效解決層相同導致目錄重名的問題,具體計算方式在此就不贅述了。
明白了這兩點之後,我們可以發現,鏡像壓縮包裏是可以獲取到與docker push同樣完備的鏡像數據的。其中,鏡像層和元數據文件可以通過解壓直接獲取,而manifest則需要我們通過補充manifest.json獲得。接下來我們看一看華為雲容器鏡像服務是怎麽實現這一過程的。
頁面上傳是怎麽實現的
解壓並校驗
鏡像壓縮包傳至後端時,先對壓縮包裏的文件類型校驗(普通文件、軟鏈接、目錄),確認無誤之後,解壓至臨時目錄並進行大小校驗(前端上傳目前有大小限制)。
此外,有一類鏡像需要被過濾:通過docker save image_id > image.tar
命令生成的鏡像包。這類鏡像是沒有有效的鏡像倉庫和版本號信息的,我們無法判斷要將其歸於哪個倉庫下,因此,這樣的鏡像可以認為是不合法的。對於頁面上傳而言,合法的鏡像壓縮包裏必須有鏡像倉庫和版本號信息(如使用docker save repository:tag > image.tar
的方式生成的鏡像)。
保存實體文件
接下來,通過臨時目錄下的manifest.json,找到對應的元數據文件xxxx.json和各個目錄下的鏡像層文件進行存儲。保存之前,通過元數據文件xxxx.json中各個層的sha256值,對實際鏡像文件進行校驗,保存過程中,我們在manifest.json的基礎上,補充各個鏡像層和元數據文件的sha256值、大小等信息,得到manifest。
在這裏有個需要註意的地方,層文件一般都是普通文件,但是個別情況下(如docker1.10之前的版本),層文件可能是軟鏈接,指向同鏡像壓縮包裏的的另一個層文件,如果要兼容老版本,需要識別出這一部分特殊文件,跳過實體文件的保存。
保存元數據
最後,將鏡像層元數據列表和manifest元數據在同一事務裏存進數據庫,保證鏡像元數據的存儲是一個原子操作,則鏡像所有數據保存完成。該鏡像可以通過docker pull的方式正常下載。
這只是華為雲容器鏡像服務基於優化用戶體驗的目的而開發的特性之一,我們一直致力於降低雲容器技術的檻和使用成本,推進軟件行業容器化的進程,希望有興趣的朋友可以來體驗一下,並提供你們寶貴的意見。
除此之外,我們最近還新上線了容器持續交付的工具,可以將您的源碼快速編譯、構建成鏡像,省去本地編寫Dockerfile、鏡像制作、發布和部署的繁瑣過程,後面文章我們將詳細為您介紹。
極簡容器化交付 | 0命令行完成鏡像上傳