天天看點

一文讀懂 K8s 持久化存儲流程K8s 持久化存儲基礎K8s 持久化存儲流程總結參考連結雲原生應用團隊招人啦!雲原生網絡研讨會邀您參加

一文讀懂 K8s 持久化存儲流程K8s 持久化存儲基礎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 共享存儲為例來看二者差別。

靜态建立存儲卷

靜态建立存儲卷流程如下圖所示:

一文讀懂 K8s 持久化存儲流程K8s 持久化存儲基礎K8s 持久化存儲流程總結參考連結雲原生應用團隊招人啦!雲原生網絡研讨會邀您參加

第一步:叢集管理者建立 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。

動态建立存儲卷相比靜态建立存儲卷,少了叢集管理者的幹預,流程如下圖所示:

一文讀懂 K8s 持久化存儲流程K8s 持久化存儲基礎K8s 持久化存儲流程總結參考連結雲原生應用團隊招人啦!雲原生網絡研讨會邀您參加

叢集管理者隻需要保證環境中有 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. 流程概覽

此處借鑒 @郡寶 在 雲原生存儲課程 中的流程圖
一文讀懂 K8s 持久化存儲流程K8s 持久化存儲基礎K8s 持久化存儲流程總結參考連結雲原生應用團隊招人啦!雲原生網絡研讨會邀您參加

流程如下:

  1. 使用者建立了一個包含 PVC 的 Pod,該 PVC 要求使用動态存儲卷;
  2. Scheduler 根據 Pod 配置、節點狀态、PV 配置等資訊,把 Pod 排程到一個合适的 Worker 節點上;
  3. PV 控制器 watch 到該 Pod 使用的 PVC 處于 Pending 狀态,于是調用 Volume Plugin(in-tree)建立存儲卷,并建立 PV 對象(out-of-tree 由 External Provisioner 來處理);
  4. AD 控制器發現 Pod 和 PVC 處于待挂接狀态,于是調用 Volume Plugin 挂接儲存設備到目标 Worker 節點上
  5. 在 Worker 節點上,Kubelet 中的 Volume Manager 等待儲存設備挂接完成,并通過 Volume Plugin 将裝置挂載到全局目錄:**/var/lib/kubelet/pods/[pod uid]/volumes/kubernetes.io~iscsi/[PV

    name]**(以 iscsi 為例);

  6. Kubelet 通過 Docker 啟動 Pod 的 Containers,用 bind mount 方式将已挂載到本地全局目錄的卷映射到容器中。

更詳細的流程如下:

一文讀懂 K8s 持久化存儲流程K8s 持久化存儲基礎K8s 持久化存儲流程總結參考連結雲原生應用團隊招人啦!雲原生網絡研讨會邀您參加

2. 流程詳解

不同 K8s 版本,持久化存儲流程略有差別。本文基于 Kubernetes 1.14.8 版本。

從上述流程圖中可看到,存儲卷從建立到提供應用使用共分為三個階段:Provision/Delete、Attach/Detach、Mount/Unmount。

provisioning volumes

一文讀懂 K8s 持久化存儲流程K8s 持久化存儲基礎K8s 持久化存儲流程總結參考連結雲原生應用團隊招人啦!雲原生網絡研讨會邀您參加

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 操作。
一文讀懂 K8s 持久化存儲流程K8s 持久化存儲基礎K8s 持久化存儲流程總結參考連結雲原生應用團隊招人啦!雲原生網絡研讨會邀您參加

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。

一文讀懂 K8s 持久化存儲流程K8s 持久化存儲基礎K8s 持久化存儲流程總結參考連結雲原生應用團隊招人啦!雲原生網絡研讨會邀您參加

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 舉例,則執行如下操作:

  1. Remove all bind-mounts:調用 Unmounter 的 TearDown 接口(若為 out-of-tree 則調用 CSI 插件的 NodeUnpublishVolume 接口);
  2. Unmount volume:調用 DeviceUnmounter 的 UnmountDevice 函數(若為 out-of-tree 則調用 CSI 插件的 NodeUnstageVolume 接口);
  3. 更新 ASW。

mountAttachVolumes:確定 Pod 要使用的 volumes 挂載成功。周遊一遍所有 DSW 中的 Pod,若其不在 ASW 中(表示目錄待挂載映射到 Pod 上),此處以 VolumeMode=FileSystem 舉例,執行如下操作:

  1. 等待 volume 挂接到節點上(由 External Attacher or Kubelet 本身挂接);
  2. 挂載 volume 到全局目錄:調用 DeviceMounter 的 MountDevice 函數(若為 out-of-tree 則調用 CSI 插件的 NodeStageVolume 接口);
  3. 更新 ASW:該 volume 已挂載到全局目錄;
  4. bind-mount volume 到 Pod 上:調用 Mounter 的 SetUp 接口(若為 out-of-tree 則調用 CSI 插件的 NodePublishVolume 接口);

unmountDetachDevices:確定需要 unmount 的 volumes 被 unmount。周遊一遍所有 ASW 中的 UnmountedVolumes,若其不在 DSW 中(表示 volume 已無需使用),執行如下操作:

  1. Unmount volume:調用 DeviceUnmounter 的 UnmountDevice 函數(若為 out-of-tree 則調用 CSI 插件的NodeUnstageVolume接口);

總結

本文先對 K8s 持久化存儲基礎概念及使用方法進行了介紹,并對 K8s 内部存儲流程進行了深度解析。在 K8s 上,使用任何一種存儲都離不開上面的流程(有些場景不會用到 attach/detach),環境上的存儲問題也一定是其中某個環節出現了故障。

容器存儲的坑比較多,專有雲環境下尤其如此。不過挑戰越多,機遇也越多!目前國内專有雲市場在存儲領域也是群雄逐鹿,我們靈活 PaaS 容器團隊歡迎大俠的加入,一起共創未來!

參考連結

  1. Kubernetes 社群源碼
  2. 【雲原生公開課】Kubernetes 存儲架構及插件使用(郡寶)
  3. 【雲原生公開課】應用存儲和持久化資料卷 - 核心知識(至天)
  4. 【kubernetes-design-proposals】volume-provisioning
  5. 【kubernetes-design-proposals】CSI Volume Plugins in Kubernetes Design Doc

雲原生應用團隊招人啦!

阿裡雲原生應用平台團隊目前求賢若渴,如果你滿足:

  • 對容器和基礎設施相關領域的雲原生技術充滿熱情,在相關領域如 Kubernetes、Serverless 平台、容器網絡與存儲、運維平台等雲原生基礎設施其中某一方向有豐富的積累和突出成果(如産品落地,創新技術實作,開源貢獻,領先的學術成果);
  • 優秀的表達能力,溝通能力和團隊協作能力;對技術和業務有前瞻性思考;具備較強的 ownership,以結果為導向,善于決策;
  • 至少熟悉 Java、Golang 中的一項程式設計語言;
  • 大學及以上學曆、3 年以上工作經驗。

履歷可投遞至郵箱:[email protected],如有疑問歡迎加微信咨詢:TheBeatles1994。

雲原生網絡研讨會邀您參加

點選立即預約直播
一文讀懂 K8s 持久化存儲流程K8s 持久化存儲基礎K8s 持久化存儲流程總結參考連結雲原生應用團隊招人啦!雲原生網絡研讨會邀您參加
阿裡巴巴雲原生 關注微服務、Serverless、容器、Service Mesh 等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,做最懂雲原生開發者的公衆号。”

繼續閱讀