容器技術的儲存機制對於應用程式狀態的持久化至關重要。容器本身是無狀態的,資料的持久化需要藉助外部儲存或底層儲存。外部儲存允許資料在容器重新啟動後仍然存在,而底層儲存則負責容器的根檔案系統和容器映像檔。對於需要分享組態、快取或其他資料的應用程式,外部儲存扮演著重要的角色。在容器協調的環境中,儲存機制更需要具備彈性和可行性,類別似於軟體定義儲存的概念,以提供自助式的儲存資源。底層儲存的效能和穩定性也直接影響容器的管理效率。
為容器實作儲存機制
技術需求
在開始本章的教學和範例之前,需要一台安裝了 Podman 的機器。如同第三章「執行第一個容器」所述,本文中的所有範例都是在 Fedora 34 或更高版本的系統上執行,但讀者可以在自己選擇的作業系統上重現這些範例。
此外,對於第四章「管理執行中的容器」中涵蓋的主題有良好的理解,將有助於輕鬆掌握有關 OCI 映像檔和容器執行的概念。
為什麼儲存對容器很重要?
在繼續本章之前,我們需要區分兩種容器的儲存機制:
- 外部儲存附加到執行中的容器,以儲存資料,使其在容器重新啟動時保持持久
- 底層儲存用於容器的根檔案系統和容器映像檔
談到外部儲存,如同第一章「容器技術簡介」所述,容器是無狀態、短暫的,通常具有唯讀的檔案系統。這是因為該技術背後的理論指出,容器應該用於產生可擴充套件和分散式的應用程式,這些應用程式應該水平擴充套件而不是垂直擴充套件。
水平擴充套件應用程式意味著,如果我們需要為正在執行的服務提供額外的資源,我們不會增加單個執行中容器的 CPU 或 RAM,而是啟動一個新的容器,與現有的容器一起處理傳入的請求。這是公共雲中採用的眾所周知的正規化。容器原則上應該是短暫的,因為現有容器映像檔的任何額外副本都可以在任何時候執行,以增強現有的執行中服務。
當然,存在例外情況,可能執行中的容器無法水平擴充套件,或者它只需要分享組態、快取或在啟動時或執行期間與相同容器映像檔的其他副本相關的其他資料。
實務範例
讓我們透過一個實際的例子來理解這一點。使用分享汽車服務在城市內每個目的地獲得一輛新車,是一種既實用又聰明的方式,可以在不必擔心停車費、燃料和其他事情的情況下出行。然而,與此同時,這項服務不允許你在停放的汽車內儲存或留下你的物品。因此,當使用分享汽車服務時,我們可以在進入汽車後取出我們的物品,但必須在離開汽車前將其封裝。對於容器來說也是如此,我們必須為它們附加一些儲存,以便我們的容器能夠寫入資料,但一旦我們的容器停止,我們就應該分離那個儲存,以便全新的容器可以在需要時使用它。
另一個更技術性的例子是:讓我們考慮一個標準的三層應用程式,具有網頁、後端和資料函式庫服務。該應用程式的每一層可能需要儲存,它將以多種方式使用。網頁服務可能需要一個地方來儲存快取、儲存呈現的網頁、在執行時間內自定義圖片等。後端服務將需要一個地方來儲存組態和同步資料(如果有的話)在其他執行的後端服務之間等等。資料函式庫服務肯定需要一個地方來儲存資料函式庫資料。
儲存通常與底層基礎架構相關聯,但在容器中,儲存甚至對於開發人員來說也變得重要,他們應該計劃在哪裡附加儲存,以及他們的應用程式所需的特性。
如果我們將主題擴充套件到容器協調,儲存就會繼承一個戰略角色,因為它應該像我們可能使用的 Kubernetes 協調器一樣具有彈性和可行性。在這種情況下,容器儲存應該變得更像是軟體定義的儲存——能夠以自助方式為開發人員和容器提供儲存資源。
雖然本文將討論本地儲存,但需要注意的是,這對於 Kubernetes 協調器來說是不夠的,因為容器應該根據定義的可用性和擴充套件規則從一個主機移植到另一個主機。這就是軟體定義的儲存可以成為解決方案的地方!
正如我們從前面的例子中可以推斷的那樣,外部儲存對於容器來說很重要。使用方式可能會根據容器內執行的應用程式而有所不同,但它是必需的。同時,另一個關鍵角色是由底層容器儲存驅動的,它負責正確處理容器和容器映像檔根檔案系統的儲存。選擇正確、穩定和高效能的底層本地儲存將確保更好地正確管理我們的容器。
因此,讓我們首先探討一些容器儲存的理論,然後討論如何與它一起工作。
容器儲存功能實作
在探討實際範例和應用案例之前,我們需要先了解容器儲存(container storage)與容器儲存介面(Container Storage Interface, CSI)之間的主要差異。
容器儲存與CSI的區別
容器儲存主要負責處理根據Copy-on-Write(COW)檔案系統的容器映像。容器映像需要被傳輸和移動,直到容器引擎被指示執行它們。因此,我們需要一種方式來儲存這些映像,直到它們被執行。這就是容器儲存的作用。
當我們開始使用諸如Kubernetes之類別的協調器時,CSI則負責為容器提供所需的區塊或檔案儲存,以便容器能夠寫入資料。
容器儲存組態
Podman引入了一個創新的專案——containers/storage,它提供了一種分享的底層方法,用於存取主機上的容器儲存。透過這個專案,我們現在可以同時使用多種工具來分析和管理工作容器的儲存。
這個低階軟體的組態對於Podman以及其他相關工具至關重要,可以透過其組態檔案/etc/containers/storage.conf進行檢查和編輯。
儲存驅動程式
組態檔案提供了選擇預設的COW容器儲存驅動程式的選項。目前支援的COW驅動程式包括:
- overlay
- vfs
- devmapper
- aufs
- btrfs
- zfs
這些驅動程式也被稱為圖形驅動程式,因為大多數驅動程式都以圖形結構組織所處理的層。
在Fedora 34或更高版本上使用Podman時,容器的儲存組態檔案預設使用overlay作為驅動程式。
Overlay Union檔案系統
Overlay union檔案系統自Linux核心版本3.18以來就已經存在。它通常預設啟用,並在掛載時動態啟用。
這個檔案系統背後的機制非常簡單卻強大——它允許將一個目錄樹疊加在另一個目錄樹上,只儲存差異,但顯示最新的更新、壓縮的目錄樹。
通常,在容器的世界中,我們首先使用唯讀檔案系統,新增一層或多層(同樣是唯讀),直到執行的容器將這些壓縮的層用作其根檔案系統。這時,將建立最後一個讀寫層作為其他層的疊加層。
實際操作範例
讓我們看看使用Podman提取新的容器映像時會發生什麼:
# podman pull quay.io/centos7/httpd-24-centos7:latest
Trying to pull quay.io/centos7/httpd-24-centos7:latest...
Getting image source signatures
Copying blob 5f2e13673ac2 done
Copying blob 8dd5a5013b51 done
Copying blob b2cc5146c9c7 done
Copying blob e17e89f32035 done
Copying blob 1b6c93aa6be5 done
Copying blob 6855d3fe68bc done
Copying blob f974a2323b6 done
Copying blob d620f14a5a76 done
Copying config 3b964f33a2 done
Writing manifest to image destination
Storing signatures
3b964f33a2bf66108d5333a541d376f63e0506aba8ddd4813f9d4e104271d9f0
從輸出中可以看到,多個層已經被下載。這是因為我們提取的容器映像是由多個層組成的。
檢視下載的層
首先,我們需要找到正確的目錄。我們可以使用podman info命令來檢視相關資訊:
# podman info | grep -A19 "store:"
store:
configFile: /etc/containers/storage.conf
containerStore:
number: 0
paused: 0
running: 0
stopped: 0
graphDriverName: overlay
graphOptions:
overlay.mountopt: nodev,metacopy=on
graphRoot: /var/lib/containers/storage
graphStatus:
Backing Filesystem: btrfs
Native Overlay Diff: "false"
Supports d_type: "true"
Using metacopy: "true"
imageStore:
number: 1
runRoot: /run/containers/storage
volumePath: /var/lib/containers/storage/volumes
從輸出中可以看到,使用的驅動程式是overlay,而根目錄是/var/lib/containers/storage。對於無根容器,相應的目錄是$HOME/.local/share/containers/storage。
讓我們檢視該目錄下的內容:
# cd /var/lib/containers/storage
# ls
libpod mounts overlay overlay-containers overlay-images overlay-layers storage.lock tmp userns.lock
程式碼解密:
上述範例展示瞭如何使用Podman提取容器映像,以及如何檢視相關的層資訊。以下是一些關鍵點:
- 提取容器映像:使用
podman pull命令提取指定的容器映像。 - 檢視層資訊:使用
podman info命令檢視容器的儲存組態資訊,包括使用的驅動程式和根目錄。 - 檢視根目錄內容:進入根目錄並檢視其內容,瞭解容器的儲存結構。
這些步驟展示了Podman如何管理和儲存容器映像,以及如何檢視相關的組態資訊。
容器資料儲存實作詳解
容器技術的儲存機制是其核心功能之一,瞭解其運作原理對於有效管理和最佳化容器應用至關重要。本文將探討容器資料的儲存方式,解析相關目錄結構及其作用。
關鍵儲存目錄解析
容器系統中存在多個重要目錄,用於儲存和管理容器映像檔及其相關資料。以下三個目錄尤其值得注意:
- overlay-images:儲存容器映像檔的後設資料
- overlay-layers:存放容器映像檔各層的壓縮檔案
- overlay:包含已解壓縮的容器映像檔層內容
overlay-images 目錄詳解
檢視 overlay-images 目錄內容:
# ls -l overlay-images/
total 8
drwx
---
---
. 1 root root 630 15 oct 18.36 3b964f33a2bf66108d5333a541d376f63e0506aba8ddd4813f9d4e10427
1d9f0
-rw
---
-
---
. 1 root root 1613 15 oct 18.36 images.json
-rw-r--r--. 1 root root 64 15 oct 18.36 images.lock
該目錄包含已下載容器映像檔的後設資料,具體包括:
- 以長ID命名的目錄,內含映像檔的manifest檔案
images.json:儲存映像檔的詳細資訊images.lock:用於同步存取的鎖設定檔案
overlay-layers 目錄解析
overlay-layers 目錄內容如下:
# ls -l overlay-layers/
total 1168
-rw
---
-
---
. 1 root root 2109 15 oct 18.35 0099baae6cd3ca0ced38d658d7871548b32bd0e42118b788d818b76131ec
8e75.tar-split.gz
...
-rw
---
-
---
. 1 root root 3716 15 oct 18.36 layers.json
-rw-r--r--. 1 root root 64 15 oct 19.06 layers.lock
該目錄存放了容器映像檔各層的壓縮檔案,以及相關的JSON後設資料和鎖設定檔案。
overlay 目錄結構分析
檢視 overlay 目錄內容:
# ls -l overlay
total 0
drwx
---
---
. 1 root root 46 15 oct 18.35 0099baae6cd3ca0ced38d658d7871548b32bd0e42118b788d818b
76131ec8e75
...
drwx
---
---
. 1 root root 416 15 oct 18.36 l
該目錄包含已解壓縮的容器映像檔層內容,以及一個特殊的 l 目錄。
層目錄結構詳解
進一步檢視某個層目錄的內容:
# ls -la overlay/0099baae6cd3ca0ced38d658d7871548b32bd0e42118b788d818b76131ec8e75/
total 8
drwx
---
---
. 1 root root 46 15 oct 18.35 .
drwx
---
---
. 1 root root 1026 15 oct 18.36 ..
dr-xr-xr-x. 1 root root 24 15 oct 18.35 diff
-rw-r--r--. 1 root root 26 15 oct 18.35 link
-rw-r--r--. 1 root root 86 15 oct 18.35 lower
drwx
---
---
. 1 root root 0 15 oct 18.35 merged
drwx
---
---
. 1 root root 0 15 oct 18.35 work
各子目錄/檔案的作用
- diff:儲存對該層所做的變更
- lower:記錄下層掛載點,按從上到下的順序排列
- merged:overlay掛載點,呈現完整的檔案系統檢視
- work:用於內部操作的工作目錄
- link:包含該層的唯一識別字串
「l」目錄的功能解析
l 目錄包含指向各層 diff 目錄的符號連結:
# ls -la overlay/l/
total 32
drwx
---
---
. 1 root root 416 15 oct 18.36 .
drwx
---
---
. 1 root root 1026 15 oct 18.36 ..
lrwxrwxrwx. 1 root root 72 15 oct 18.35 A4ZYMM4AK5NM6JYJA7EK2DLTGA -> ../74fa1495774e94d5cdb579f9bae4a16bd90616024a6f4b1ffd13344c367df1f6/diff
...
這些符號連結與 lower 檔案中的下層參照相對應,建立了層之間的關聯。
容器儲存實作詳解:Overlay 儲存驅動程式深度解析
容器映像層級結構分析
在探討容器儲存機制時,我們首先需要了解容器映像的組成結構。一個典型的容器映像是由多個層級(layers)所構成,這些層級透過Overlay檔案系統進行堆積疊。以下是一個範例的容器映像描述檔(manifest)內容:
{
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"config": {
"mediaType": "application/vnd.docker.container.image.v1+json",
"size": 16212,
"digest": "sha256:3b964f33a2bf66108d5333a541d376f63e0506aba8ddd4813f9d4e104271d9f0"
},
"layers": [
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 75867345,
"digest": "sha256:b2cc5146c9c7855cb298ca8b77ecb153d37e3e5c69916ef423613a46a70c0503"
}
]
}
內容解密:
schemaVersion: 定義了映像描述檔的版本結構,目前主要使用版本2。mediaType: 指定了描述檔的媒體型別,表明這是一個Docker v2格式的描述檔。config: 描述了容器組態檔的相關資訊,包括媒體型別、大小和摘要(digest)。layers: 列出了組成容器映像的所有層級,每個層級都有其媒體型別、大小和摘要。
Overlay 儲存驅動程式運作機制
Overlay檔案系統是目前容器技術中最常用的儲存驅動程式之一。它透過將多個唯讀層級與一個可寫層級結合,實作了高效的儲存空間利用和快速的容器啟動。
層級驗證與結構分析
當我們下載一個容器映像後,可以透過檢查overlay-layers/layers.json檔案來驗證層級的完整性:
# cat overlay-layers/layers.json | jq | grep -B3 -A10 "sha256:b2cc5"
輸出結果顯示了層級的詳細資訊,包括其ID、建立時間、壓縮摘要等。
內容解密:
id: 層級的唯一識別碼。compressed-diff-digest: 層級壓縮後的摘要,用於驗證資料完整性。compressed-size: 層級壓縮後的大小。diff-digest和diff-size: 分別表示層級的差異資料摘要和大小。
進一步檢視特定層級的目錄結構:
# ls -l overlay/53498d66ad83a29fcd7c7bcf4abbcc0def4fc912772aa8a4483b51e232309aee/
內容解密:
diff目錄: 存放該層級相對於其父層級的差異檔案。empty目錄: 當層級沒有父層級時,Overlay系統會建立一個空目錄作為下層目錄。merged和work目錄: 分別用於存放合併後的檔案系統和Overlay的工作目錄。
執行中的容器儲存變化
當我們執行一個容器並在其檔案系統中建立新檔案時,Overlay驅動程式會在可寫層級中記錄這些變化。
首先,執行容器並進入其shell環境:
# podman run -d quay.io/centos7/httpd-24-centos7
# podman exec -ti bd0eef7cd50760dd52c24550be51535bc11559e52eea7d782a1fa69 /bin/bash
在容器內建立一個新檔案:
bash-4.2$ echo "this is my NOT persistent data" > tempfile.txt
分析容器的掛載點
透過檢視容器的掛載點資訊,可以找到可寫層級(upperdir)的路徑:
bash-4.2$ mount | head
輸出結果顯示了Overlay掛載的詳細資訊,包括lowerdir、upperdir和workdir。
內容解密:
upperdir: 可寫層級的目錄路徑,存放了容器執行時產生的資料變化。lowerdir: 多個唯讀層級的路徑列表,透過冒號分隔。
最後,檢視可寫層級中的檔案內容:
# ls -la /var/lib/containers/storage/overlay/b71e4bea5380ca233bf6b0c7a1c276179b841e263ee293e987c6cc54af516f23/diff/opt/app-root/src/
# cat /var/lib/containers/storage/overlay/b71e4bea5380ca233bf6b0c7a1c276179b841e263ee293e987c6cc54af516f23/diff/opt/app-root/src/tempfile.txt