映像檔系統:設定 Kubernetes 將容器儲存在獨立的檔案系統上

在執行/操作 Kubernetes 叢集中,一個常見的問題是磁碟空間不足。當節點佈建完成時,您應該目標擁有足夠的儲存空間來存放您的容器映像檔和執行中的容器。容器執行期通常會寫入 /var。這可以位於單獨的分割區或根檔案系統上。預設情況下,CRI-O 將其容器和映像檔寫入 /var/lib/containers,而 containerd 將其容器和映像檔寫入 /var/lib/containerd

在這篇網誌文章中,我們希望讓大家注意到您可以設定容器執行期,使其內容與預設分割區分開儲存的方法。
這允許在設定 Kubernetes 時具有更大的彈性,並支援為容器儲存新增更大的磁碟,同時保持預設檔案系統不受影響。

一個需要更多解釋的領域是 Kubernetes 正在寫入磁碟的位置/內容。

了解 Kubernetes 磁碟使用量

Kubernetes 具有持久性資料和臨時性資料。kubelet 和本機 Kubernetes 特定儲存空間的基礎路徑是可設定的,但通常假設為 /var/lib/kubelet。在 Kubernetes 文件中,這有時被稱為根或節點檔案系統。此資料的大部分可以分為

  • 臨時性儲存空間
  • 日誌
  • 和容器執行期

這與大多數 POSIX 系統不同,因為根/節點檔案系統不是 /,而是 /var/lib/kubelet 所在的磁碟。

臨時性儲存空間

Pod 和容器可能需要暫時或瞬時的本機儲存空間才能運作。臨時性儲存空間的生命週期不會超出個別 Pod 的生命週期,而且臨時性儲存空間無法在 Pod 之間共用。

日誌

預設情況下,Kubernetes 將每個執行中容器的日誌儲存為 /var/log 中的檔案。這些日誌是臨時性的,並由 kubelet 監控,以確保它們在 Pod 執行時不會變得太大。

您可以自訂每個節點的 日誌輪換 設定,以管理這些日誌的大小,並設定日誌傳輸 (使用第三方解決方案) 以避免依賴節點本機儲存空間。

容器執行期

容器執行期具有兩個不同的儲存區域,用於存放容器和映像檔。

  • 唯讀層:映像檔通常被標示為唯讀層,因為它們在容器執行時不會被修改。唯讀層可以由多個層組成,這些層組合為單個唯讀層。容器頂部有一個薄層,如果容器正在寫入檔案系統,則為容器提供臨時性儲存空間。

  • 可寫入層:根據您的容器執行期,本機寫入可能會實作為分層寫入機制 (例如,Linux 上的 overlayfs 或 Windows 上的 CimFS)。這稱為可寫入層。本機寫入也可以使用可寫入的檔案系統,該檔案系統使用容器映像檔的完整複製初始化;這用於某些基於 Hypervisor 虛擬化的執行期。

容器執行期檔案系統包含唯讀層和可寫入層。這在 Kubernetes 文件中被視為 imagefs

容器執行期設定

CRI-O

CRI-O 使用 TOML 格式的儲存設定檔,讓您可以控制容器執行期如何儲存持久性和臨時性資料。CRI-O 利用 儲存程式庫
某些 Linux 發行版本具有儲存空間的手動條目 (man 5 containers-storage.conf)。儲存空間的主要設定位於 /etc/containers/storage.conf 中,並且可以控制臨時性資料和根目錄的位置。
根目錄是 CRI-O 儲存持久性資料的位置。

[storage]
# Default storage driver
driver = "overlay"
# Temporary storage location
runroot = "/var/run/containers/storage"
# Primary read/write location of container storage 
graphroot = "/var/lib/containers/storage"
  • graphroot
    • 從容器執行期儲存的持久性資料
    • 如果啟用 SELinux,則這必須與 /var/lib/containers/storage 相符
  • runroot
    • 容器的臨時性讀/寫存取
    • 建議將其放在臨時性檔案系統上

以下是重新標記您的 graphroot 目錄以符合 /var/lib/containers/storage 的快速方法

semanage fcontext -a -e /var/lib/containers/storage <YOUR-STORAGE-PATH>
restorecon -R -v <YOUR-STORAGE-PATH>

containerd

containerd 執行期使用 TOML 設定檔來控制持久性和臨時性資料的儲存位置。設定檔的預設路徑位於 /etc/containerd/config.toml

containerd 儲存空間的相關欄位為 rootstate

  • root
    • containerd metadata 的根目錄
    • 預設值為 /var/lib/containerd
    • 如果您的作業系統需要,Root 也需要 SELinux 標籤
  • state
    • containerd 的臨時性資料
    • 預設值為 /run/containerd

Kubernetes 節點壓力驅逐

Kubernetes 將自動偵測容器檔案系統是否與節點檔案系統分開。當分開檔案系統時,Kubernetes 負責監控節點檔案系統和容器執行期檔案系統。Kubernetes 文件將節點檔案系統和容器執行期檔案系統稱為 nodefs 和 imagefs。如果 nodefs 或 imagefs 的磁碟空間不足,則整體節點會被視為具有磁碟壓力。Kubernetes 將首先透過刪除未使用的容器和映像檔來回收空間,然後它將訴諸驅逐 Pod。在具有 nodefs 和 imagefs 的節點上,kubelet 將在 imagefs 上 垃圾收集 未使用的容器映像檔,並從 nodefs 中移除已終止的 Pod 及其容器。如果只有 nodefs,則 Kubernetes 垃圾收集包括已終止的容器、已終止的 Pod 和未使用的映像檔。

Kubernetes 允許更多設定來判斷您的磁碟是否已滿。
kubelet 中的驅逐管理器有一些設定,可讓您控制相關的閾值。對於檔案系統,相關的測量值為 nodefs.availablenodefs.inodesfreeimagefs.availableimagefs.inodesfree。如果沒有容器執行期的專用磁碟,則會忽略 imagefs。

使用者可以使用現有的預設值

  • memory.available < 100MiB
  • nodefs.available < 10%
  • imagefs.available < 15%
  • nodefs.inodesFree < 5% (Linux 節點)

Kubernetes 允許您在 kubelet 設定檔中的 EvictionHardEvictionSoft 中設定使用者定義的值。

EvictionHard
定義限制;一旦超出這些限制,Pod 將被驅逐,而不會有任何寬限期。
EvictionSoft
定義限制;一旦超出這些限制,Pod 將被驅逐,並具有可以針對每個訊號設定的寬限期。

如果您為 EvictionHard 指定值,它將取代預設值。
這表示在您的設定中設定所有訊號非常重要。

例如,可以使用以下 kubelet 設定來設定 驅逐訊號 和寬限期選項。

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
address: "192.168.0.8"
port: 20250
serializeImagePulls: false
evictionHard:
    memory.available:  "100Mi"
    nodefs.available:  "10%"
    nodefs.inodesFree: "5%"
    imagefs.available: "15%"
    imagefs.inodesFree: "5%"
evictionSoft:
    memory.available:  "100Mi"
    nodefs.available:  "10%"
    nodefs.inodesFree: "5%"
    imagefs.available: "15%"
    imagefs.inodesFree: "5%"
evictionSoftGracePeriod:
    memory.available:  "1m30s"
    nodefs.available:  "2m"
    nodefs.inodesFree: "2m"
    imagefs.available: "2m"
    imagefs.inodesFree: "2m"
evictionMaxPodGracePeriod: 60s

問題

Kubernetes 專案建議您使用驅逐的預設設定,或設定驅逐的所有欄位。您可以使用預設設定或指定您自己的 evictionHard 設定。如果您遺漏訊號,則 Kubernetes 將不會監控該資源。管理員或使用者可能遇到的常見錯誤設定是將新的檔案系統掛載到 /var/lib/containers/storage/var/lib/containerd。Kubernetes 將偵測到單獨的檔案系統,因此您需要確保在執行此操作後檢查 imagefs.inodesfreeimagefs.available 是否符合您的需求。

另一個令人困惑的領域是,如果您為節點定義映像檔檔案系統,則臨時性儲存空間報告不會變更。映像檔檔案系統 (imagefs) 用於儲存容器映像檔層;如果容器寫入其自己的根檔案系統,則本機寫入不會計入容器映像檔的大小。容器執行期儲存這些本機修改的位置是執行期定義的,但通常是映像檔檔案系統。如果 Pod 中的容器正在寫入檔案系統支援的 emptyDir 磁碟區,則這會使用來自 nodefs 檔案系統的空間。kubelet 始終根據 nodefs 代表的檔案系統報告臨時性儲存空間容量和配置;當臨時性寫入實際上進入映像檔檔案系統時,這可能會造成混淆。

未來工作

為了修正臨時性儲存空間報告限制,並為容器執行期提供更多設定選項,SIG Node 正在開發 KEP-4191。在 KEP-4191 中,Kubernetes 將偵測可寫入層是否與唯讀層 (映像檔) 分開。這將允許我們將所有臨時性儲存空間 (包括可寫入層) 放在與映像檔相同的磁碟上,並允許為映像檔使用單獨的磁碟。

參與其中

如果您想參與其中,可以加入 Kubernetes 節點特殊興趣小組 (SIG)。

如果您想分享意見回饋,可以在我們的 #sig-node Slack 頻道上進行。如果您還不是該 Slack 工作區的成員,可以造訪 https://slack.k8s.io/ 以取得邀請。

特別感謝所有提供出色評論、分享寶貴見解或建議主題想法的貢獻者。

  • Peter Hunt
  • Mrunal Patel
  • Ryan Phillips
  • Gaurav Singh