一文读懂 K8s 持久化存储流程
作者 | 孫志恒(惠志)? 阿里巴巴開發(fā)工程師
**導讀:**眾所周知,K8s 的持久化存儲(Persistent Storage)保證了應(yīng)用數(shù)據(jù)獨立于應(yīng)用生命周期而存在,但其內(nèi)部實現(xiàn)卻少有人提及。K8s?內(nèi)部的存儲流程到底是怎樣的?PV、PVC、StorageClass、Kubelet、CSI 插件等之間的調(diào)用關(guān)系又如何,這些謎底將在本文中一一揭曉。
K8s 持久化存儲基礎(chǔ)
在進行 K8s 存儲流程講解之前,先回顧一下 K8s 中持久化存儲的基礎(chǔ)概念。
1. 名詞解釋
-
in-tree:代碼邏輯在 K8s 官方倉庫中;
-
out-of-tree:代碼邏輯在 K8s 官方倉庫之外,實現(xiàn)與 K8s 代碼的解耦;
-
PV:PersistentVolume,集群級別的資源,由 集群管理員 or External Provisioner 創(chuàng)建。PV 的生命周期獨立于使用 PV 的 Pod,PV 的 .Spec 中保存了存儲設(shè)備的詳細信息;
-
PVC:PersistentVolumeClaim,命名空間(namespace)級別的資源,由 用戶 or StatefulSet 控制器(根據(jù)VolumeClaimTemplate) 創(chuàng)建。PVC 類似于 Pod,Pod 消耗 Node 資源,PVC 消耗 PV 資源。Pod 可以請求特定級別的資源(CPU 和內(nèi)存),而 PVC 可以請求特定存儲卷的大小及訪問模式(Access Mode);
-
StorageClass:StorageClass 是集群級別的資源,由集群管理員創(chuàng)建。SC 為管理員提供了一種動態(tài)提供存儲卷的“類”模板,SC 中的 .Spec 中詳細定義了存儲卷 PV 的不同服務(wù)質(zhì)量級別、備份策略等等;
-
CSI:Container Storage Interface,目的是定義行業(yè)標準的“容器存儲接口”,使存儲供應(yīng)商(SP)基于 CSI 標準開發(fā)的插件可以在不同容器編排(CO)系統(tǒng)中工作,CO 系統(tǒng)包括 Kubernetes、Mesos、Swarm 等。
2. 組件介紹
-
PV Controller:負責 PV/PVC 綁定及周期管理,根據(jù)需求進行數(shù)據(jù)卷的?**Provision/Delete?**操作;
-
AD Controller:負責數(shù)據(jù)卷的?**Attach/Detach?**操作,將設(shè)備掛接到目標節(jié)點;
-
Kubelet:Kubelet 是在每個 Node 節(jié)點上運行的主要 “節(jié)點代理”,功能是 Pod 生命周期管理、容器健康檢查、容器監(jiān)控等;
-
Volume Manager:Kubelet 中的組件,負責管理數(shù)據(jù)卷的?**Mount/Umount?**操作(也負責數(shù)據(jù)卷的?**Attach/Detach?**操作,需配置 kubelet 相關(guān)參數(shù)開啟該特性)、卷設(shè)備的格式化等等;
-
Volume Plugins:存儲插件,由存儲供應(yīng)商開發(fā),目的在于擴展各種存儲類型的卷管理能力,實現(xiàn)第三方存儲的各種操作能力,即是上面藍色操作的實現(xiàn)。Volume Plugins 有?in-tree?和 out-of-tree 兩種;
-
External Provioner:External Provioner 是一種?sidecar 容器,作用是調(diào)用 Volume Plugins 中的?CreateVolume 和?DeleteVolume 函數(shù)來執(zhí)行?**Provision/Delete?**操作。因為 K8s 的 PV 控制器無法直接調(diào)用 Volume Plugins 的相關(guān)函數(shù),故由 External Provioner 通過 gRPC 來調(diào)用;
-
External Attacher:External Attacher 是一種?sidecar 容器,作用是調(diào)用 Volume Plugins 中的?ControllerPublishVolume 和?ControllerUnpublishVolume 函數(shù)來執(zhí)行?**Attach/Detach?**操作。因為 K8s 的 AD 控制器無法直接調(diào)用 Volume Plugins?的相關(guān)函數(shù),故由 External Attacher 通過 gRPC 來調(diào)用。
3. 持久卷使用
Kubernetes 為了使應(yīng)用程序及其開發(fā)人員能夠正常請求存儲資源,避免處理存儲設(shè)施細節(jié),引入了 PV 和 PVC。創(chuàng)建 PV 有兩種方式:
-
一種是集群管理員通過手動方式靜態(tài)創(chuàng)建應(yīng)用所需要的 PV;
-
另一種是用戶手動創(chuàng)建 PVC 并由 Provisioner 組件動態(tài)創(chuàng)建對應(yīng)的 PV。
下面我們以 NFS 共享存儲為例來看二者區(qū)別。
靜態(tài)創(chuàng)建存儲卷
靜態(tài)創(chuàng)建存儲卷流程如下圖所示:
第一步:集群管理員創(chuàng)建 NFS PV,NFS 屬于 K8s 原生支持的 in-tree 存儲類型。yaml 文件如下:
apiVersion: v1 kind: PersistentVolume metadata:name: nfs-pv spec:capacity:storage: 10GiaccessModes:- ReadWriteOncepersistentVolumeReclaimPolicy: Retainnfs:server: 192.168.4.1path: /nfs_storage第二步:用戶創(chuàng)建 PVC,yaml 文件如下:
apiVersion: v1 kind: PersistentVolumeClaim metadata:name: nfs-pvc spec:accessModes:- ReadWriteOnceresources: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第三步:用戶創(chuàng)建應(yīng)用,并使用第二步創(chuàng)建的 PVC。
apiVersion: v1 kind: Pod metadata:name: test-nfs spec:containers:- image: nginx:alpineimagePullPolicy: IfNotPresentname: nginxvolumeMounts:- mountPath: /dataname: nfs-volumevolumes:- name: nfs-volumepersistentVolumeClaim:claimName: nfs-pvc此時 NFS 的遠端存儲就掛載了到 Pod 中 nginx 容器的 /data 目錄下。
動態(tài)創(chuàng)建存儲卷
動態(tài)創(chuàng)建存儲卷,要求集群中部署有?**nfs-client-provisioner?**以及對應(yīng)的?storageclass。
動態(tài)創(chuàng)建存儲卷相比靜態(tài)創(chuàng)建存儲卷,少了集群管理員的干預,流程如下圖所示:
集群管理員只需要保證環(huán)境中有 NFS 相關(guān)的 storageclass 即可:
kind: StorageClass apiVersion: storage.k8s.io/v1 metadata:name: nfs-sc provisioner: example.com/nfs mountOptions:- vers=4.1第一步:用戶創(chuàng)建 PVC,此處 PVC 的 storageClassName 指定為上面 NFS 的 storageclass 名稱:
kind: PersistentVolumeClaim apiVersion: v1 metadata:name: nfsannotations:volume.beta.kubernetes.io/storage-class: "example-nfs" spec:accessModes:- ReadWriteManyresources:requests:storage: 10MistorageClassName: nfs-sc第二步:集群中的?nfs-client-provisioner 會動態(tài)創(chuàng)建相應(yīng) PV。此時可看到環(huán)境中 PV 已創(chuàng)建,并與 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第三步:用戶創(chuàng)建應(yīng)用,并使用第二步創(chuàng)建的 PVC,同靜態(tài)創(chuàng)建存儲卷的第三步。
K8s 持久化存儲流程
1. 流程概覽
此處借鑒 @郡寶 在云原生存儲課程中的流程圖
流程如下:
用戶創(chuàng)建了一個包含 PVC 的 Pod,該 PVC 要求使用動態(tài)存儲卷;
**Scheduler?**根據(jù) Pod 配置、節(jié)點狀態(tài)、PV 配置等信息,把 Pod 調(diào)度到一個合適的 Worker 節(jié)點上;
**PV 控制器?**watch 到該 Pod 使用的 PVC 處于 Pending 狀態(tài),于是調(diào)用?Volume Plugin(in-tree)創(chuàng)建存儲卷,并創(chuàng)建 PV 對象(out-of-tree 由 External Provisioner 來處理);
AD 控制器發(fā)現(xiàn) Pod 和 PVC 處于待掛接狀態(tài),于是調(diào)用?**Volume Plugin?**掛接存儲設(shè)備到目標 Worker 節(jié)點上
在 Worker 節(jié)點上,Kubelet 中的 Volume Manager?等待存儲設(shè)備掛接完成,并通過?Volume Plugin?將設(shè)備掛載到全局目錄:/var/lib/kubelet/pods/[pod?uid]/volumes/kubernetes.io~iscsi/[PV
name](以 iscsi 為例);
**Kubelet?**通過 Docker 啟動?Pod?的 Containers,用?bind mount?方式將已掛載到本地全局目錄的卷映射到容器中。
更詳細的流程如下:
2. 流程詳解
不同 K8s 版本,持久化存儲流程略有區(qū)別。本文基于 Kubernetes 1.14.8?版本。
從上述流程圖中可看到,存儲卷從創(chuàng)建到提供應(yīng)用使用共分為三個階段:Provision/Delete、Attach/Detach、Mount/Unmount。
provisioning volumes
PV 控制器中有兩個 Worker:
- ClaimWorker:處理 PVC 的 add / update / delete 相關(guān)事件以及 PVC 的狀態(tài)遷移;
- VolumeWorker:負責 PV 的狀態(tài)遷移。
PV 狀態(tài)遷移(UpdatePVStatus):
- PV 初始狀態(tài)為?Available,當 PV 與 PVC 綁定后,狀態(tài)變?yōu)?Bound;
- 與 PV 綁定的 PVC 刪除后,狀態(tài)變?yōu)?Released;
- 當 PV 回收策略為 Recycled 或手動刪除 PV 的 .Spec.ClaimRef 后,PV 狀態(tài)變?yōu)?Available;
- 當 PV 回收策略未知或 Recycle 失敗或存儲卷刪除失敗,PV 狀態(tài)變?yōu)?Failed;
- 手動刪除 PV 的 .Spec.ClaimRef,PV 狀態(tài)變?yōu)?Available。
PVC 狀態(tài)遷移(UpdatePVCStatus):
- 當集群中不存在滿足 PVC 條件的 PV 時,PVC 狀態(tài)為?Pending。在 PV 與 PVC 綁定后,PVC 狀態(tài)由?Pending?變?yōu)?Bound;
- 與 PVC 綁定的 PV?在環(huán)境中被刪除,PVC 狀態(tài)變?yōu)?Lost;
- 再次與一個**同名 PV?**綁定后,PVC 狀態(tài)變?yōu)?Bound。
Provisioning 流程如下(此處模擬用戶創(chuàng)建一個新 PVC):
靜態(tài)存儲卷流程(FindBestMatch):PV 控制器首先在環(huán)境中篩選一個狀態(tài)為 Available 的 PV 與新 PVC匹配。
-
DelayBinding:PV 控制器判斷該 PVC 是否需要**延遲綁定:**1.?查看 PVC 的 annotation 中是否包含volume.kubernetes.io/selected-node,若存在則表示該 PVC 已經(jīng)被調(diào)度器指定好了節(jié)點(屬于?ProvisionVolume),故不需要延遲綁定;2.?若 PVC 的 annotation 中不存在?volume.kubernetes.io/selected-node,同時沒有?StorageClass,默認表示不需要延遲綁定;若有 StorageClass,查看其?VolumeBindingMode 字段,若為 WaitForFirstConsumer 則需要延遲綁定,若為 Immediate 則不需要延遲綁定;
-
FindBestMatchPVForClaim:PV 控制器嘗試找一個滿足 PVC 要求的環(huán)境中現(xiàn)有的 PV。PV 控制器會將**所有的 PV?**進行一次篩選,并會從滿足條件的 PV 中選擇一個最佳匹配的PV。篩選規(guī)則:1.?VolumeMode 是否匹配;2. PV 是否已綁定到 PVC 上;3. PV 的 .Status.Phase 是否為 Available;4. LabelSelector 檢查,PV 與 PVC 的 label 要保持一致;5. PV 與?PVC?的 StorageClass 是否一致;6.?每次迭代更新最小滿足 PVC requested size 的 PV,并作為最終結(jié)果返回;
-
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”;
**動態(tài)存儲卷流程(ProvisionVolume):**若環(huán)境中沒有合適的 PV,則進入動態(tài) 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 會實現(xiàn) ProvisionableVolumePlugin 接口的 NewProvisioner 方法,用來返回一個新的 Provisioner;2.?PV 控制器調(diào)用?Provisioner 的 Provision 函數(shù),該函數(shù)會返回一個 PV 對象;3.?PV 控制器創(chuàng)建上一步返回的 PV 對象,將其與 PVC 綁定,Spec.ClaimRef 設(shè)置為 PVC,.Status.Phase 設(shè)置為 Bound,.Spec.StorageClassName?設(shè)置為與 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 參數(shù)來確定自己的?Provisioner Name);3.?若 PVC 的?VolumeMode=Block,檢查?External?Provisioner?是否支持塊設(shè)備;4.?External?Provisioner 調(diào)用?Provision 函數(shù):通過 gRPC 調(diào)用?CSI 存儲插件的?CreateVolume?接口;5.?External?Provisioner 創(chuàng)建一個 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 會實現(xiàn)?DeletableVolumePlugin?接口的?NewDeleter?方法,用來返回一個新的 Deleter;2.?控制器調(diào)用?Deleter?的?Delete?函數(shù),刪除對應(yīng) volume;3. 在 volume 刪除后,PV 控制器會刪除 PV 對象;
-
**out-of-tree Deleting:**1.?External?Provisioner 調(diào)用?Delete 函數(shù),通過 gRPC 調(diào)用?CSI?插件的?DeleteVolume?接口;2.?在 volume 刪除后,External?Provisioner?會刪除 PV 對象
Attaching Volumes
Kubelet 組件和 AD 控制器都可以做 attach/detach 操作,若?Kubelet?的啟動參數(shù)中指定了–enable-controller-attach-detach,則由?Kubelet?來做;否則默認由 AD 控制起來做。下面以 AD 控制器為例來講解 attach/detach 操作。
AD 控制器中有兩個核心變量:
- DesiredStateOfWorld(DSW):集群中預期的數(shù)據(jù)卷掛接狀態(tài),包含了 nodes->volumes->pods 的信息;
- ActualStateOfWorld(ASW):集群中實際的數(shù)據(jù)卷掛接狀態(tài),包含了 volumes->nodes 的信息。
Attaching 流程如下:
AD 控制器根據(jù)集群中的資源信息,初始化?DSW 和?ASW。
AD 控制器內(nèi)部有三個組件周期性更新?DSW 和?ASW:
- **Reconciler。**通過一個 GoRoutine 周期性運行,確保 volume 掛接/摘除完畢。此期間不斷更新 ASW:
**in-tree attaching:**1.?in-tree 的 Attacher 會實現(xiàn)?AttachableVolumePlugin?接口的 NewAttacher 方法,用來返回一個新的?Attacher;2. AD 控制器調(diào)用?Attacher?的?Attach?函數(shù)進行設(shè)備掛接;3.?更新 ASW。
**out-of-tree?attaching:**1.?調(diào)用 in-tree 的 CSIAttacher 創(chuàng)建一個?**VolumeAttachement(VA)**對象,該對象包含了 Attacher 信息、節(jié)點名稱、待掛接 PV 信息;2.?External?Attacher 會 watch 集群中的?VolumeAttachement 資源,發(fā)現(xiàn)有需要掛接的數(shù)據(jù)卷時,調(diào)用?Attach 函數(shù),通過 gRPC 調(diào)用?CSI 插件的?ControllerPublishVolume?接口。
- DesiredStateOfWorldPopulator。通過一個?GoRoutine?周期性運行,主要功能是更新 DSW:
findAndRemoveDeletedPods - 遍歷所有 DSW 中的 Pods,若其已從集群中刪除則從 DSW 中移除;
findAndAddActivePods - 遍歷所有 PodLister 中的 Pods,若 DSW 中不存在該 Pod 則添加至 DSW。
- **PVC Worker。**watch PVC 的 add/update 事件,處理 PVC 相關(guān)的 Pod,并實時更新 DSW。
Detaching Volumes
Detaching 流程如下:
-
當 Pod 被刪除,AD 控制器會 watch 到該事件。首先 AD 控制器檢查 Pod 所在的 Node 資源是否包含"volumes.kubernetes.io/keep-terminated-pod-volumes"標簽,若包含則不做操作;不包含則從 DSW 中去掉該 volume;
-
AD 控制器通過?**Reconciler?**使 ActualStateOfWorld 狀態(tài)向 DesiredStateOfWorld 狀態(tài)靠近,當發(fā)現(xiàn) ASW 中有 DSW 中不存在的 volume 時,會做 Detach 操作:
**in-tree detaching:**1.?AD 控制器會實現(xiàn) AttachableVolumePlugin 接口的 NewDetacher 方法,用來返回一個新的 Detacher;2. 控制器調(diào)用?Detacher?的?Detach?函數(shù),detach 對應(yīng) volume;3. AD 控制器更新 ASW。
**out-of-tree?detaching:**1.?AD 控制器調(diào)用 in-tree 的 CSIAttacher 刪除相關(guān) VolumeAttachement 對象;2. External?Attacher 會 watch 集群中的?VolumeAttachement(VA)資源,發(fā)現(xiàn)有需要摘除的數(shù)據(jù)卷時,調(diào)用?Detach 函數(shù),通過 gRPC 調(diào)用?CSI 插件的 ControllerUnpublishVolume 接口;3.?AD 控制器更新 ASW。
**Volume Manager?**中同樣也有兩個核心變量:
- DesiredStateOfWorld(DSW):集群中預期的數(shù)據(jù)卷掛載狀態(tài),包含了 volumes->pods 的信息;
- ActualStateOfWorld(ASW):集群中實際的數(shù)據(jù)卷掛載狀態(tài),包含了 volumes->pods 的信息。
Mounting/UnMounting 流程如下:
全局目錄(global mount path)存在的目的:塊設(shè)備在 Linux 上只能掛載一次,而在 K8s 場景中,一個 PV 可能被掛載到同一個 Node 上的多個 Pod 實例中。若塊設(shè)備格式化后先掛載至 Node 上的一個臨時全局目錄,然后再使用 Linux 中的 bind mount 技術(shù)把這個全局目錄掛載進 Pod 中對應(yīng)的目錄上,就可以滿足要求。上述流程圖中,全局目錄即?/var/lib/kubelet/pods/[pod uid]/volumes/kubernetes.io~iscsi/[PV
name]
VolumeManager?根據(jù)集群中的資源信息,初始化?DSW 和?ASW。
VolumeManager?內(nèi)部有兩個組件周期性更新?DSW 和?ASW:
- DesiredStateOfWorldPopulator:通過一個?GoRoutine?周期性運行,主要功能是更新 DSW;
- Reconciler:通過一個 GoRoutine 周期性運行,確保 volume 掛載/卸載完畢。此期間不斷更新 ASW:
unmountVolumes:確保 Pod 刪除后 volumes 被 unmount。遍歷一遍所有 ASW 中的 Pod,若其不在 DSW 中(表示 Pod 被刪除),此處以 VolumeMode=FileSystem 舉例,則執(zhí)行如下操作:
mountAttachVolumes:確保 Pod 要使用的 volumes 掛載成功。遍歷一遍所有 DSW 中的 Pod,若其不在 ASW 中(表示目錄待掛載映射到 Pod 上),此處以 VolumeMode=FileSystem 舉例,執(zhí)行如下操作:
unmountDetachDevices:確保需要 unmount?的 volumes 被 unmount。遍歷一遍所有 ASW?中的?UnmountedVolumes,若其不在 DSW 中(表示 volume 已無需使用),執(zhí)行如下操作:
總結(jié)
本文先對?K8s?持久化存儲基礎(chǔ)概念及使用方法進行了介紹,并對?K8s 內(nèi)部存儲流程進行了深度解析。在 K8s 上,使用任何一種存儲都離不開上面的流程(有些場景不會用到 attach/detach),環(huán)境上的存儲問題也一定是其中某個環(huán)節(jié)出現(xiàn)了故障。
容器存儲的坑比較多,專有云環(huán)境下尤其如此。不過挑戰(zhàn)越多,機遇也越多!目前國內(nèi)專有云市場在存儲領(lǐng)域也是群雄逐鹿,我們敏捷 PaaS 容器團隊歡迎大俠的加入,一起共創(chuàng)未來!
參考鏈接
云原生應(yīng)用團隊招人啦!
阿里云原生應(yīng)用平臺團隊目前求賢若渴,如果你滿足:
-
對容器和基礎(chǔ)設(shè)施相關(guān)領(lǐng)域的云原生技術(shù)充滿熱情,在相關(guān)領(lǐng)域如 Kubernetes、Serverless 平臺、容器網(wǎng)絡(luò)與存儲、運維平臺等云原生基礎(chǔ)設(shè)施其中某一方向有豐富的積累和突出成果(如產(chǎn)品落地,創(chuàng)新技術(shù)實現(xiàn),開源貢獻,領(lǐng)先的學術(shù)成果);
-
優(yōu)秀的表達能力,溝通能力和團隊協(xié)作能力;對技術(shù)和業(yè)務(wù)有前瞻性思考;具備較強的 ownership,以結(jié)果為導向,善于決策;
-
至少熟悉 Java、Golang 中的一項編程語言;
-
本科及以上學歷、3 年以上工作經(jīng)驗。
簡歷可投遞至郵箱:huizhi.szh@alibaba-inc.com,如有疑問歡迎加微信咨詢:TheBeatles1994。
云原生網(wǎng)絡(luò)研討會邀您參加
點擊立即預約直播
“阿里巴巴云原生關(guān)注微服務(wù)、Serverless、容器、Service Mesh 等技術(shù)領(lǐng)域、聚焦云原生流行技術(shù)趨勢、云原生大規(guī)模的落地實踐,做最懂云原生開發(fā)者的公眾號?!?/p>
總結(jié)
以上是生活随笔為你收集整理的一文读懂 K8s 持久化存储流程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 重磅合集 | 31 篇技术文章,带你从零
- 下一篇: 重磅 | Dragonfly 晋升成为