Docker檔案系統實戰

網易雲信 發表於 2020-06-30

在本文中,我們來實戰構建一個Docker映象,然後例項化容器,在Docker的生命週期中詳細分析一下Docker的檔案儲存情況和DockerFile優化策略。

enter image description here

在開始實戰之前,我們先介紹一個概念,聯合檔案系統(Union File System)。聯合檔案系統是實現Docker映象的技術基礎,支援對檔案系統的修改作為一次提交來一層層的疊加,同時可以將不同目錄掛載到同一個虛擬檔案系統下。映象的分層儲存和繼承就是基於此特性實現。

下面是Docker官方的一張描述檔案系統的圖片,顯示了一張聯合檔案系統在串聯映象層和容器層起到的作用。 enter image description here

Docker支援多種聯合檔案系統,常見的有aufs,deviceMapper,overlay,overlay2,本文章中使用的系統版本為debian9.1,Docker版本為17.06.2-ce,預設使用是overlay2。

看到這裡如果你已經對Docker檔案系統有了簡單的概念,那麼讓我們開始實戰,來對分層檔案系統的儲存方式進行更加深入的瞭解。

映象層

這是一個雲信私有化專案中基於debian系統映象建立的jdk8基礎映象,為了方便閱讀和分析,我們Dockerfile進行了一些精簡,只保留核心部分內容。

enter image description here

根據構建映象,檢視構建結果,原基礎映象100M,構建後映象體積697M。 enter image description here

映象儲存

現在開始看一下構建映象工作在檔案層儲存情況。首先我們使用Docker history檢視一下剛剛構建映象情況,可以看到基礎映象佔用100M,兩個映象分層佔用194MB和403M。 enter image description here

接下來我們看檢視一下檔案系統中的儲存情況,本環境使用overlay2,Docker映象層儲存預設路徑為/var/lib/Docker/overlay2/,可以看到映象儲存目錄下有4個目錄,其中110M的對應是基礎映象,另外兩個為ADD JDK(186M)和解壓JDK壓縮包的映象分層(389M)。 enter image description here

其中的l目錄包含了所有層的軟連線,軟連結使用短名稱,避免mount時候引數達到頁面大小限制。 enter image description here

下面我們瞭解一下,每個分層中的檔案內容。基礎映象分層包含diff資料夾和link檔案,diff資料夾中存放當前分層內容,link檔案記錄短名稱。 enter image description here

接下來看一下COPY JDK生成的內容,diff資料夾儲存了jdk壓縮包,本層相比基礎映象層,多了lower,merged,work三個檔案/資料夾,其中lower記錄了此層的下層ID(基礎映象層),merged目錄作為提供了統一檢視,在容器層讀寫層被使用,work目錄用於聯合掛載指定的工作目錄,使用過程對使用者不可見。 enter image description here

解壓JDK層的資料夾結構內容和上一層類似,主要關注jdk壓縮包占用空間為0,表示已被刪除。 enter image description here

現在來重點關注一個問題,映象大小等於所有分層相加,在後續分層中被刪除的jdk壓縮包仍然要佔用儲存空間,這並不是我們原本意圖,因此這裡就出現了映象檔案進行優化的點。優化後的Dockerfile如下 enter image description here

借這個優化後的內容,我們再談一下構建Docker映象時在時間和空間可優化的點:

  1. 組合執行語句:合併相同型別構建語句,可以有效減少映象分層;
  2. 利用映象構建快取:時間同步,基礎軟體安裝等固定內容在映象前部分處理,映象重新構建時會使用快取,節省時間;
  3. 清理中間產物:注意安裝過程中使用的軟體和壓縮包在一定要同一層裡清理,否則仍然會佔用映象空間;
  4. 構建語句優化:比如ADD在處理本地檔案時可以直接解壓縮,起到COPY + RUN tar的作用;
  5. 優化基礎映象源:國內高校和大型IT企業都有建立映象站,選擇一個穩定更新及時的映象站可以有效縮短構建時間;

舉例的映象中優化策略涉及1,3條,用curl替代add,與解壓和刪除合併為一層,Dockerfile減少了層數,清理中間過程的jdk安裝包,下圖是優化後映象體積變化: enter image description here

構建映象真的是層數越少越好嗎?當然不是這麼絕對,尤其在早期映象版本不是很穩定或是後續迭代比較頻繁時,合理的映象分層會減少編譯時間,降低出錯概率,也可以讓Dockerfile更具有可讀性。可以再穩定版本形成之後對映象進行二次優化。

映象後設資料

分析一個映象後設資料我們主要關注三個目錄 enter image description here

第一個目錄儲存映象基礎後設資料,第二個目錄儲存映象分層後設資料,第三個是上文提到的分層儲存目錄,儲存實際分層內容。下面就根據實際情況來看一下,後設資料與儲存資訊是如何關聯起來的。

Docker映象的基本資訊儲存在/var/lib/Docker/image/overlay2/imaged/content/sha256/下面,可以根據Docker image ID在此目錄下查詢到對應ID開標頭檔案。此檔案中以json的形式儲存了該映象的分層檔案系統、構建資訊、相關容器等內容。 enter image description here

第二個目錄/var/lib/Docker/image/overlay2/layerdb/sha256/儲存分層後設資料,每一個分層後設資料目錄下有cache-id,diff,size資訊,其中cache-id對應分層儲存層,diff關聯映象基礎後設資料資訊。 enter image description here enter image description here enter image description here

容器層

首先我們來啟動一個容器,掛載宿主機/opt/yunxin目錄到容器/usr/local/yunxin目錄。 enter image description here

建立容器完成之後,在映象儲存目錄/var/lib/Docker/overlay2/會生成容器的初始層和讀寫層,兩者使用相同標識,初始層後面多了-init。初始層中主要儲存初始化容器環境時,與容器相關的環境資訊,如容器主機名,主機host資訊以及域名服務檔案等;讀寫層用於容器的讀寫,Docker容器內的程式只對讀寫層擁有寫許可權,而對其他層檔案內容只擁有讀許可權。 enter image description here

接下來我們進入容器操作進行一系列操作,再根據結果分析一下讀寫層對於檔案的儲存和處理,下面是操作和對應結果以及讀寫層實際檔案儲存情況。 enter image description here enter image description here

讀寫層中的merged資料夾提供了統一檢視,面向使用者展示聯合檔案系統掛載完成的最終形態。 enter image description here

接下來我們再基於同一個映象啟動幾個容器例項,然後來查詢一下Docker容器使用空間,只有第一個容器由於上面修改檔案只佔用154k,新啟動的容器並沒有額外佔用空間。可見基於同一個映象建立容器時,所有的容器共享映象層內容,有效節約了空間。讀寫層只儲存修改內容,如果是操作映象層檔案,Docker採用的是修改時複製策略(copy-on-write)。這時回頭再看一下第一節出現的兩張圖,會對Docker的檔案系統有了更深的體會。 enter image description here

結語

Docker 映象和容器檔案系統相關知識在雲信私有化產品的映象管理和運維儲存管理方面作出理論支撐,但這只是深入瞭解Docker的開始。隨著時間的積澱和雲信旗下IM、音視訊、點播以及眾多相關產品私有化工作的深入,更多的模組和映象,更多的客戶和需求,更復雜的網路和環境都逐漸呈現在我們面前。Docker作為構建雲信私有化服務的基礎,只有更深入的去了解原理才能在使用中去更好的優化產品和開展運維。希望我們能為使用者提供更可靠的雲信私有化服務,也希望能在後續的文章中能與大家分享更多關於Docker的知識。