一文讀懂 K8s 持久化儲存流程
作者 | 孫志恆(惠志) 阿里巴巴開發工程師
導讀:眾所周知,K8s 的持久化儲存(Persistent Storage)保證了應用資料獨立於應用生命週期而存在,但其內部實現卻少有人提及。K8s 內部的儲存流程到底是怎樣的?PV、PVC、StorageClass、Kubelet、CSI 外掛等之間的呼叫關係又如何,這些謎底將在本文中一一揭曉。
K8s 持久化儲存基礎
在進行 K8s 儲存流程講解之前,先回顧一下 K8s 中持久化儲存的基礎概念。
1. 名詞解釋
in-tree:程式碼邏輯在 K8s 官方倉庫中;
out-of-tree:程式碼邏輯在 K8s 官方倉庫之外,實現與 K8s 程式碼的解耦;
PV:PersistentVolume,叢集級別的資源,由 叢集管理員 or External Provisioner 建立。PV 的生命週期獨立於使用 PV 的 Pod,PV 的 .Spec 中儲存了儲存裝置的詳細資訊;
PVC:PersistentVolumeClaim,名稱空間(namespace)級別的資源,由 使用者 or StatefulSet 控制器(根據 VolumeClaimTemplate) 建立。PVC 類似於 Pod,Pod 消耗 Node 資源,PVC 消耗 PV 資源。Pod 可以請求特定級別的資源(CPU 和記憶體),而 PVC 可以請求特定儲存卷的大小及訪問模式(Access Mode);
StorageClass:StorageClass 是叢集級別的資源,由叢集管理員建立。SC 為管理員提供了一種動態提供儲存卷的 “類” 模板,SC 中的 .Spec 中詳細定義了儲存卷 PV 的不同服務質量級別、備份策略等等;
CSI:Container Storage Interface,目的是定義行業標準的 “容器儲存介面”,使儲存供應商(SP)基於 CSI 標準開發的外掛可以在不同容器編排(CO)系統中工作,CO 系統包括 Kubernetes、Mesos、Swarm 等。
2. 元件介紹
PV Controller:負責 PV/PVC 繫結及週期管理,根據需求進行資料卷的 Provision/Delete 操作;
AD Controller:負責資料卷的 Attach/Detach 操作,將裝置掛接到目標節點;
Kubelet:Kubelet 是在每個 Node 節點上執行的主要 “節點代理”,功能是 Pod 生命週期管理、容器健康檢查、容器監控等;
Volume Manager:Kubelet 中的元件,負責管理資料卷的 Mount/Umount 操作(也負責資料卷的 Attach/Detach 操作,需配置 kubelet 相關引數開啟該特性)、卷裝置的格式化等等;
Volume Plugins:儲存外掛,由儲存供應商開發,目的在於擴充套件各種儲存型別的卷管理能力,實現第三方儲存的各種操作能力,即是上面藍色操作的實現。Volume Plugins 有 in-tree 和 out-of-tree 兩種;
External Provioner:External Provioner 是一種 sidecar 容器,作用是呼叫 Volume Plugins 中的 CreateVolume 和 DeleteVolume 函式來執行 Provision/Delete 操作。因為 K8s 的 PV 控制器無法直接呼叫 Volume Plugins 的相關函式,故由 External Provioner 通過 gRPC 來呼叫;
External Attacher:External Attacher 是一種 sidecar 容器,作用是呼叫 Volume Plugins 中的 ControllerPublishVolume 和 ControllerUnpublishVolume 函式來執行 Attach/Detach 操作。因為 K8s 的 AD 控制器無法直接呼叫 Volume Plugins 的相關函式,故由 External Attacher 通過 gRPC 來呼叫。
3. 持久卷使用
Kubernetes 為了使應用程式及其開發人員能夠正常請求儲存資源,避免處理儲存設施細節,引入了 PV 和 PVC。建立 PV 有兩種方式:
一種是叢集管理員通過手動方式靜態建立應用所需要的 PV;
另一種是使用者手動建立 PVC 並由 Provisioner 元件動態建立對應的 PV。
下面我們以 NFS 共享儲存為例來看二者區別。
靜態建立儲存卷
靜態建立儲存卷流程如下圖所示:
第一步:叢集管理員建立 NFS PV,NFS 屬於 K8s 原生支援的 in-tree 儲存型別。yaml 檔案如下:
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
nfs:
server: 192.168.4.1
path: /nfs_storage
第二步:使用者建立 PVC,yaml 檔案如下:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
通過 kubectl get pv 命令可看到 PV 和 PVC 已繫結:
[root@huizhi ~]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
nfs-pvc Bound nfs-pv-no-affinity 10Gi RWO 4s
第三步:使用者建立應用,並使用第二步建立的 PVC。
apiVersion: v1
kind: Pod
metadata:
name: test-nfs
spec:
containers:
- image: nginx:alpine
imagePullPolicy: IfNotPresent
name: nginx
volumeMounts:
- mountPath: /data
name: nfs-volume
volumes:
- name: nfs-volume
persistentVolumeClaim:
claimName: nfs-pvc
此時 NFS 的遠端儲存就掛載了到 Pod 中 nginx 容器的 /data 目錄下。
動態建立儲存卷
動態建立儲存卷,要求叢集中部署有 nfs-client-provisioner 以及對應的 storageclass。
動態建立儲存卷相比靜態建立儲存卷,少了叢集管理員的干預,流程如下圖所示:
叢集管理員只需要保證環境中有 NFS 相關的 storageclass 即可:
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: nfs-sc
provisioner: example.com/nfs
mountOptions:
- vers=4.1
第一步:使用者建立 PVC,此處 PVC 的 storageClassName 指定為上面 NFS 的 storageclass 名稱:
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: nfs
annotations:
volume.beta.kubernetes.io/storage-class: "example-nfs"
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Mi
storageClassName: nfs-sc
第二步:叢集中的 nfs-client-provisioner 會動態建立相應 PV。此時可看到環境中 PV 已建立,並與 PVC 已繫結。
[root@huizhi ~]# kubectl get pv
NAME CAPACITY ACCESSMODES RECLAIMPOLICY STATUS CLAIM REASON AGE
pvc-dce84888-7a9d-11e6-b1ee-5254001e0c1b 10Mi RWX Delete Bound default/nfs 4s
第三步:使用者建立應用,並使用第二步建立的 PVC,同靜態建立儲存卷的第三步。
K8s 持久化儲存流程
1. 流程概覽
此處借鑑 @ 郡寶 在雲原生儲存課程中的流程圖
流程如下:
使用者建立了一個包含 PVC 的 Pod,該 PVC 要求使用動態儲存卷;
Scheduler 根據 Pod 配置、節點狀態、PV 配置等資訊,把 Pod 排程到一個合適的 Worker 節點上;
PV 控制器 watch 到該 Pod 使用的 PVC 處於 Pending 狀態,於是呼叫 Volume Plugin(in-tree)建立儲存卷,並建立 PV 物件(out-of-tree 由 External Provisioner 來處理);
AD 控制器發現 Pod 和 PVC 處於待掛接狀態,於是呼叫 Volume Plugin 掛接儲存裝置到目標 Worker 節點上
在 Worker 節點上,Kubelet 中的 Volume Manager 等待儲存裝置掛接完成,並通過 Volume Plugin 將裝置掛載到全域性目錄:/var/lib/kubelet/pods/[pod uid]/volumes/kubernetes.io~iscsi/[PV name](以 iscsi 為例);
Kubelet 通過 Docker 啟動 Pod 的 Containers,用 bind mount 方式將已掛載到本地全域性目錄的卷對映到容器中。
更詳細的流程如下:
2. 流程詳解
不同 K8s 版本,持久化儲存流程略有區別。本文基於 Kubernetes 1.14.8 版本。
從上述流程圖中可看到,儲存卷從建立到提供應用使用共分為三個階段:Provision/Delete、Attach/Detach、Mount/Unmount。
provisioning volumes
PV 控制器中有兩個 Worker:
- ClaimWorker:處理 PVC 的 add / update / delete 相關事件以及 PVC 的狀態遷移;
- VolumeWorker:負責 PV 的狀態遷移。
PV 狀態遷移(UpdatePVStatus):
- PV 初始狀態為 Available,當 PV 與 PVC 繫結後,狀態變為 Bound;
- 與 PV 繫結的 PVC 刪除後,狀態變為 Released;
- 當 PV 回收策略為 Recycled 或手動刪除 PV 的 .Spec.ClaimRef 後,PV 狀態變為 Available;
- 當 PV 回收策略未知或 Recycle 失敗或儲存卷刪除失敗,PV 狀態變為 Failed;
- 手動刪除 PV 的 .Spec.ClaimRef,PV 狀態變為 Available。
PVC 狀態遷移(UpdatePVCStatus):
- 當叢集中不存在滿足 PVC 條件的 PV 時,PVC 狀態為 Pending。在 PV 與 PVC 繫結後,PVC 狀態由 Pending 變為 Bound;
- 與 PVC 繫結的 PV 在環境中被刪除,PVC 狀態變為 Lost;
- 再次與一個同名 PV 繫結後,PVC 狀態變為 Bound。
Provisioning 流程如下(此處模擬使用者建立一個新 PVC):
靜態儲存卷流程(FindBestMatch):PV 控制器首先在環境中篩選一個狀態為 Available 的 PV 與新 PVC 匹配。
DelayBinding:PV 控制器判斷該 PVC 是否需要延遲繫結:1. 檢視 PVC 的 annotation 中是否包含 volume.kubernetes.io/selected-node,若存在則表示該 PVC 已經被排程器指定好了節點(屬於 ProvisionVolume),故不需要延遲繫結;2. 若 PVC 的 annotation 中不存在 volume.kubernetes.io/selected-node,同時沒有 StorageClass,預設表示不需要延遲繫結;若有 StorageClass,檢視其 VolumeBindingMode 欄位,若為 WaitForFirstConsumer 則需要延遲繫結,若為 Immediate 則不需要延遲繫結;
FindBestMatchPVForClaim:PV 控制器嘗試找一個滿足 PVC 要求的環境中現有的 PV。PV 控制器會將所有的 PV 進行一次篩選,並會從滿足條件的 PV 中選擇一個最佳匹配的 PV。篩選規則:1. VolumeMode 是否匹配;2. PV 是否已繫結到 PVC 上;3. PV 的 .Status.Phase 是否為 Available;4. LabelSelector 檢查,PV 與 PVC 的 label 要保持一致;5. PV 與 PVC 的 StorageClass 是否一致;6. 每次迭代更新最小滿足 PVC requested size 的 PV,並作為最終結果返回;
Bind:PV 控制器對選中的 PV、PVC 進行繫結:1. 更新 PV 的 .Spec.ClaimRef 資訊為當前 PVC;2. 更新 PV 的 .Status.Phase 為 Bound;3. 新增 PV 的 annotation : pv.kubernetes.io/bound-by-controller: "yes";4. 更新 PVC 的 .Spec.VolumeName 為 PV 名稱;5. 更新 PVC 的 .Status.Phase 為 Bound;6. 新增 PVC 的 annotation:pv.kubernetes.io/bound-by-controller: "yes" 和 pv.kubernetes.io/bind-completed: "yes";
動態儲存卷流程(ProvisionVolume):若環境中沒有合適的 PV,則進入動態 Provisioning 場景:
Before Provisioning:1. PV 控制器首先判斷 PVC 使用的 StorageClass 是 in-tree 還是 out-of-tree:通過檢視 StorageClass 的 Provisioner 欄位是否包含 "kubernetes.io/" 字首來判斷;2. PV 控制器更新 PVC 的 annotation:claim.Annotations["volume.beta.kubernetes.io/storage-provisioner"] = storageClass.Provisioner;
in-tree Provisioning(internal provisioning):1. in-tree 的 Provioner 會實現 ProvisionableVolumePlugin 介面的 NewProvisioner 方法,用來返回一個新的 Provisioner;2. PV 控制器呼叫 Provisioner 的 Provision 函式,該函式會返回一個 PV 物件;3. PV 控制器建立上一步返回的 PV 物件,將其與 PVC 繫結,Spec.ClaimRef 設定為 PVC,.Status.Phase 設定為 Bound,.Spec.StorageClassName 設定為與 PVC 相同的 StorageClassName;同時新增 annotation:"pv.kubernetes.io/bound-by-controller"="yes" 和 "pv.kubernetes.io/provisioned-by"=plugin.GetPluginName();
out-of-tree Provisioning(external provisioning):1. External Provisioner 檢查 PVC 中的 claim.Spec.VolumeName 是否為空,不為空則直接跳過該 PVC;2. External Provisioner 檢查 PVC 中的 claim.Annotations["volume.beta.kubernetes.io/storage-provisioner"] 是否等於自己的 Provisioner Name(External Provisioner 在啟動時會傳入--provisioner 引數來確定自己的 Provisioner Name);3. 若 PVC 的 VolumeMode=Block,檢查 External Provisioner 是否支援塊裝置;4. External Provisioner 呼叫 Provision 函式:通過 gRPC 呼叫 CSI 儲存外掛的 CreateVolume 介面;5. External Provisioner 建立一個 PV 來代表該 volume,同時將該 PV 與之前的 PVC 做繫結。
deleting volumes
Deleting 流程為 Provisioning 的反操作:
使用者刪除 PVC,刪除 PV 控制器改變 PV.Status.Phase 為 Released。
當 PV.Status.Phase == Released 時,PV 控制器首先檢查 Spec.PersistentVolumeReclaimPolicy 的值,為 Retain 時直接跳過,為 Delete 時:
in-tree Deleting:1. in-tree 的 Provioner 會實現 DeletableVolumePlugin 介面的 NewDeleter 方法,用來返回一個新的 Deleter;2. 控制器呼叫 Deleter 的 Delete 函式,刪除對應 volume;3. 在 volume 刪除後,PV 控制器會刪除 PV 物件;
out-of-tree Deleting:1. External Provisioner 呼叫 Delete 函式,通過 gRPC 呼叫 CSI 外掛的 DeleteVolume 介面;2. 在 volume 刪除後,External Provisioner 會刪除 PV 物件
Attaching Volumes
Kubelet 元件和 AD 控制器都可以做 attach/detach 操作,若 Kubelet 的啟動引數中指定了--enable-controller-attach-detach,則由 Kubelet 來做;否則預設由 AD 控制起來做。下面以 AD 控制器為例來講解 attach/detach 操作。
AD 控制器中有兩個核心變數:
- DesiredStateOfWorld(DSW):叢集中預期的資料卷掛接狀態,包含了 nodes->volumes->pods 的資訊;
- ActualStateOfWorld(ASW):叢集中實際的資料卷掛接狀態,包含了 volumes->nodes 的資訊。
Attaching 流程如下:
AD 控制器根據叢集中的資源資訊,初始化 DSW 和 ASW。
AD 控制器內部有三個元件週期性更新 DSW 和 ASW:
- Reconciler。通過一個 GoRoutine 週期性執行,確保 volume 掛接/摘除完畢。此期間不斷更新 ASW:
in-tree attaching:1. in-tree 的 Attacher 會實現 AttachableVolumePlugin 介面的 NewAttacher 方法,用來返回一個新的 Attacher;2. AD 控制器呼叫 Attacher 的 Attach 函式進行裝置掛接;3. 更新 ASW。
out-of-tree attaching:1. 呼叫 in-tree 的 CSIAttacher 建立一個 VolumeAttachement(VA)物件,該物件包含了 Attacher 資訊、節點名稱、待掛接 PV 資訊;2. External Attacher 會 watch 叢集中的 VolumeAttachement 資源,發現有需要掛接的資料卷時,呼叫 Attach 函式,通過 gRPC 呼叫 CSI 外掛的 ControllerPublishVolume 介面。
- DesiredStateOfWorldPopulator。通過一個 GoRoutine 週期性執行,主要功能是更新 DSW:
findAndRemoveDeletedPods - 遍歷所有 DSW 中的 Pods,若其已從叢集中刪除則從 DSW 中移除; findAndAddActivePods - 遍歷所有 PodLister 中的 Pods,若 DSW 中不存在該 Pod 則新增至 DSW。
- PVC Worker。watch PVC 的 add/update 事件,處理 PVC 相關的 Pod,並實時更新 DSW。
Detaching Volumes
Detaching 流程如下:
當 Pod 被刪除,AD 控制器會 watch 到該事件。首先 AD 控制器檢查 Pod 所在的 Node 資源是否包含"volumes.kubernetes.io/keep-terminated-pod-volumes"標籤,若包含則不做操作;不包含則從 DSW 中去掉該 volume;
AD 控制器通過 Reconciler 使 ActualStateOfWorld 狀態向 DesiredStateOfWorld 狀態靠近,當發現 ASW 中有 DSW 中不存在的 volume 時,會做 Detach 操作:
in-tree detaching:1. AD 控制器會實現 AttachableVolumePlugin 介面的 NewDetacher 方法,用來返回一個新的 Detacher;2. 控制器呼叫 Detacher 的 Detach 函式,detach 對應 volume;3. AD 控制器更新 ASW。
out-of-tree detaching:1. AD 控制器呼叫 in-tree 的 CSIAttacher 刪除相關 VolumeAttachement 物件;2. External Attacher 會 watch 叢集中的 VolumeAttachement(VA)資源,發現有需要摘除的資料卷時,呼叫 Detach 函式,通過 gRPC 呼叫 CSI 外掛的 ControllerUnpublishVolume 介面;3. AD 控制器更新 ASW。
Volume Manager 中同樣也有兩個核心變數:
- DesiredStateOfWorld(DSW):叢集中預期的資料卷掛載狀態,包含了 volumes->pods 的資訊;
- ActualStateOfWorld(ASW):叢集中實際的資料卷掛載狀態,包含了 volumes->pods 的資訊。
Mounting/UnMounting 流程如下:
全域性目錄(global mount path)存在的目的:塊裝置在 Linux 上只能掛載一次,而在 K8s 場景中,一個 PV 可能被掛載到同一個 Node 上的多個 Pod 例項中。若塊裝置格式化後先掛載至 Node 上的一個臨時全域性目錄,然後再使用 Linux 中的 bind mount 技術把這個全域性目錄掛載進 Pod 中對應的目錄上,就可以滿足要求。上述流程圖中,全域性目錄即 /var/lib/kubelet/pods/[pod uid]/volumes/kubernetes.io~iscsi/[PV name]
VolumeManager 根據叢集中的資源資訊,初始化 DSW 和 ASW。
VolumeManager 內部有兩個元件週期性更新 DSW 和 ASW:
- DesiredStateOfWorldPopulator:通過一個 GoRoutine 週期性執行,主要功能是更新 DSW;
- Reconciler:通過一個 GoRoutine 週期性執行,確保 volume 掛載/解除安裝完畢。此期間不斷更新 ASW:
unmountVolumes:確保 Pod 刪除後 volumes 被 unmount。遍歷一遍所有 ASW 中的 Pod,若其不在 DSW 中(表示 Pod 被刪除),此處以 VolumeMode=FileSystem 舉例,則執行如下操作:
- Remove all bind-mounts:呼叫 Unmounter 的 TearDown 介面(若為 out-of-tree 則呼叫 CSI 外掛的 NodeUnpublishVolume 介面);
- Unmount volume:呼叫 DeviceUnmounter 的 UnmountDevice 函式(若為 out-of-tree 則呼叫 CSI 外掛的 NodeUnstageVolume 介面);
- 更新 ASW。
mountAttachVolumes:確保 Pod 要使用的 volumes 掛載成功。遍歷一遍所有 DSW 中的 Pod,若其不在 ASW 中(表示目錄待掛載對映到 Pod 上),此處以 VolumeMode=FileSystem 舉例,執行如下操作:
- 等待 volume 掛接到節點上(由 External Attacher or Kubelet 本身掛接);
- 掛載 volume 到全域性目錄:呼叫 DeviceMounter 的 MountDevice 函式(若為 out-of-tree 則呼叫 CSI 外掛的 NodeStageVolume 介面);
- 更新 ASW:該 volume 已掛載到全域性目錄;
- bind-mount volume 到 Pod 上:呼叫 Mounter 的 SetUp 介面(若為 out-of-tree 則呼叫 CSI 外掛的 NodePublishVolume 介面);
- 更新 ASW。
unmountDetachDevices:確保需要 unmount 的 volumes 被 unmount。遍歷一遍所有 ASW 中的 UnmountedVolumes,若其不在 DSW 中(表示 volume 已無需使用),執行如下操作:
- Unmount volume:呼叫 DeviceUnmounter 的 UnmountDevice 函式(若為 out-of-tree 則呼叫 CSI 外掛的 NodeUnstageVolume 介面);
- 更新 ASW。
總結
本文先對 K8s 持久化儲存基礎概念及使用方法進行了介紹,並對 K8s 內部儲存流程進行了深度解析。在 K8s 上,使用任何一種儲存都離不開上面的流程(有些場景不會用到 attach/detach),環境上的儲存問題也一定是其中某個環節出現了故障。
容器儲存的坑比較多,專有云環境下尤其如此。不過挑戰越多,機遇也越多!目前國內專有云市場在儲存領域也是群雄逐鹿,我們敏捷 PaaS 容器團隊歡迎大俠的加入,一起共創未來!
參考連結
- Kubernetes 社群原始碼
- 【雲原生公開課】Kubernetes 儲存架構及外掛使用(郡寶)
- 【雲原生公開課】應用儲存和持久化資料卷 - 核心知識(至天)
- 【kubernetes-design-proposals】volume-provisioning
- 【kubernetes-design-proposals】CSI Volume Plugins in Kubernetes Design Doc
雲原生應用團隊招人啦!
阿里雲原生應用平臺團隊目前求賢若渴,如果你滿足:
對容器和基礎設施相關領域的雲原生技術充滿熱情,在相關領域如 Kubernetes、Serverless 平臺、容器網路與儲存、運維平臺等雲原生基礎設施其中某一方向有豐富的積累和突出成果(如產品落地,創新技術實現,開源貢獻,領先的學術成果);
優秀的表達能力,溝通能力和團隊協作能力;對技術和業務有前瞻性思考;具備較強的 ownership,以結果為導向,善於決策;
至少熟悉 Java、Golang 中的一項程式語言;
本科及以上學歷、3 年以上工作經驗。
簡歷可投遞至郵箱:huizhi.szh@alibaba-inc.com,如有疑問歡迎加微信諮詢:TheBeatles1994。
雲原生網路研討會邀您參加
“阿里巴巴雲原生關注微服務、Serverless、容器、Service Mesh 等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,做最懂雲原生開發者的公眾號。”
- 加微信實戰群請加微信(註明:實戰群):gocnio
相關文章
- 一文讀懂 Kubernetes 儲存設計
- 一文讀懂容器儲存介面 CSI
- Redis持久化儲存Redis持久化
- K8S中如何使用Glusterfs做持久化儲存?K8S持久化
- Flutter持久化儲存之檔案儲存Flutter持久化
- k8s使用glusterfs實現動態持久化儲存K8S持久化
- k8s使用ceph實現動態持久化儲存K8S持久化
- Flutter持久化儲存之資料庫儲存Flutter持久化資料庫
- Flutter持久化儲存之key-value儲存Flutter持久化
- 一文讀懂瀏覽器儲存與快取機制瀏覽器快取
- scrapy框架持久化儲存框架持久化
- Redis 持久化儲存詳解Redis持久化
- Redis持久化儲存——>RDB & AOFRedis持久化
- (三)Kubernetes---持久化儲存持久化
- Tair持久儲存系列技術解讀AI
- iOS資料持久化儲存-CoreDataiOS持久化
- Kubernetes 持久化資料儲存 StorageClass持久化
- K8S系列第九篇(持久化儲存,emptyDir、hostPath、PV/PVC)K8S持久化
- 如何接入 K8s 持久化儲存?K8s CSI 實現機制淺析K8S持久化
- Kubuesphere部署Ruoyi(三):持久化儲存配置持久化
- tensorflow模型持久化儲存和載入模型持久化
- Kubernetes的故事之持久化儲存(十)持久化
- 一文讀懂搭建線上教育平臺流程
- AOF持久化(儲存的是操作redis命令)持久化Redis
- 利用Kubernetes實現容器的持久化儲存持久化
- 容器雲對接持久化儲存並使用持久化
- Room-資料持久化儲存(入門)OOM持久化
- 一文讀懂k8s之Pod安全策略K8S
- 使用容器化塊儲存OpenEBS在K3s中實現持久化儲存持久化
- 一張圖讀懂軟體定義儲存
- 1.05 docker的持久化儲存和資料共享Docker持久化
- Docker的持久化儲存和資料共享(四)Docker持久化
- 一文讀懂k8s rbac 許可權驗證K8S
- 一文讀懂mavenMaven
- 一文讀懂ServletServlet
- React通過redux-persist持久化資料儲存ReactRedux持久化
- Kubernetes 持久化儲存之 NFS 終極實戰指南持久化NFS
- 探索 Kubernetes 持久化儲存之 Longhorn 初窺門徑持久化