天天看點

Kubernetes 持久卷

Volume 卷

Container 中的檔案在磁盤上是臨時存放的,這給 Container 中運作的較重要的應用程式帶來一些問題:

  • 1.當容器崩潰時,kubelet 會重新啟動容器,但容器會以幹淨的狀态重新開機,造成檔案的丢失。
  • 2.Pod 中運作多個容器時,希望能在多個容器中共享檔案。

是以 Kubernetes 使用了卷(Volume) 這一抽象概念能夠來解決這兩個問題。Kubernetes 支援下列類型的卷:

  • hostpath:将主機節點檔案系統上的檔案或目錄挂載到你的 Pod 中。
  • emptyDir: 當 Pod 分派到某個 Node 上時,emptyDir 卷會被建立,并且在 Pod 在該節點上運作期間,卷一直存在。就像其名稱表示的那樣,卷最初是空的。Pod 中的多個容器可以共享 emptyDir 卷中的檔案。當 Pod 因為某些原因被從節點上删除時,emptyDir 卷中的資料也會被永久删除。容器崩潰并不會導緻 Pod 被從節點上移除,是以容器崩潰期間 emptyDir 卷中的資料是安全的。
  • Persistent Volume:persistentVolumeClaim 卷用來将持久卷(PersistentVolume) 挂載到 Pod 中。持久卷申領(PersistentVolumeClaim)是使用者在不知道特定雲環境細節的情況下"申領"持久存儲 (例如 NFS,iSCSI)的一種方法。

以上隻是列舉了其中一小部分支援的卷,具體可以檢視:

https://kubernetes.io/zh/docs/concepts/storage/volumes/#persistentvolumeclaim

Persistent Volume 持久卷

本文主要介紹持久卷的使用。Kubernetes 為了使開發人員能夠在請求存儲資源時,避免處理存儲設施細節,引入了持久卷(PersistentVolume,PV) 和 持久卷申領(PersistentVolumeClaim,PVC):

  • 持久卷(PersistentVolume,PV 是叢集中的一塊存儲,可以由管理者事先供應,或者 使用存儲類(Storage Class)來動态供應。持久卷是叢集資源,就像節點也是叢集資源一樣。PV 持久卷和普通的 Volume 一樣,也是使用 卷插件來實作的,隻是它們擁有獨立于任何使用 PV 的 Pod 的生命周期。此 API 對象中記述了存儲的實作細節,無論其背後是 NFS、iSCSI 還是特定于雲平台的存儲系統。
  • 持久卷申領(PersistentVolumeClaim,PVC 表達的是使用者對存儲的請求。概念上與 Pod 類似。Pod 會耗用節點資源,而 PVC 申領會耗用 PV 資源。Pod 可以請求特定數量的資源(CPU 和記憶體);同樣 PVC 申領也可以請求特定的大小和通路模式 (例如,可以要求 PV 卷能夠以 ReadWriteOnce、ReadOnlyMany 或 ReadWriteMany 模式之一來挂載)。

建立 PV 有兩種方式:

  • 靜态供應:一種是叢集管理者通過手動方式靜态建立應用所需要的 PV。
  • 動态供應:
    • 方式一:使用者手動建立 PVC 并由 Provisioner 元件動态建立對應的 PV。
    • 方式二:在建立 Pod 的時候使用 volumeClaimTemplates 聲明。

PV 回收政策

  • 保留(Retain) 保留政策允許手動回收資源,當删除PVC的時候,PV仍然存在,變為Realease狀态,需要使用者手動通過以下步驟回收卷(隻有hostPath和nfs支援Retain回收政策):
    • 1.删除PV。
    • 2.手動清理存儲的資料資源。
  • 回收(Resycle) 該政策已廢棄,推薦使用dynamic provisioning,回收政策會在 volume上執行基本擦除(rm -rf /thevolume/*),可被再次聲明使用。
  • 删除(Delete)
    • 當發生删除操作的時候,會從 Kubernetes 叢集中删除 PV 對象,并執行外部存儲資源的删除操作(根據不同的provisioner 定義的删除邏輯不同,有的是重命名而不是删除)。
    • 動态配置的卷繼承其 StorageClass 的回收政策,預設為Delete,即當使用者删除 PVC 的時候,會自動執行 PV 的删除政策。

通路模式

通路模式有:

  • ReadWriteOnce -- 卷可以被一個節點以讀寫方式挂載;
  • ReadOnlyMany -- 卷可以被多個節點以隻讀方式挂載;
  • ReadWriteMany -- 卷可以被多個節點以讀寫方式挂載。

在指令行接口(CLI)中,通路模式也使用以下縮寫形式:

  • RWO -- ReadWriteOnce
  • ROX -- ReadOnlyMany
  • RWX -- ReadWriteMany

每個卷隻能同一時刻隻能以一種通路模式挂載,即使該卷能夠支援 多種通路模式。例如,一個 GCEPersistentDisk 卷可以被某節點以 ReadWriteOnce 模式挂載,或者被多個節點以 ReadOnlyMany 模式挂載,但不可以同時以兩種模式挂載。

卷綁定模式

volumeBindingMode 字段控制了 PVC 和 PV 在什麼時候進行綁定。

  • Immediate:表示一旦建立了 PVC 也就完成了卷綁定和動态制備。對于由于拓撲限制而非叢集所有節點可達的存儲後端,PV 會在不知道 Pod 排程要求的情況下綁定或者制備。
  • WaitForFirstConsumer:該模式将延遲 PV 的綁定和制備,直到使用該 PVC 的 Pod 被建立。PV 會根據 Pod 排程限制指定的拓撲來選擇或制備,包括但不限于 資源需求、 節點篩選器、 pod 親和性和互斥性、 以及污點和容忍度。

靜态供應

靜态供應需要管理者手動建立 PV,然後建立 PVC 綁定 PV,最後建立 Pod 聲明使用 PVC。

建立 PV

apiVersion: v1
kind: PersistentVolume
metadata:
  name: task-pv-volume
  labels:
    type: local
spec:
  storageClassName: manual #靜态供應,名字可以任意取
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/mnt/data"  #在建立pod的節點上會建立該目錄      

建立 PVC

fapiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: task-pv-claim
spec:
  storageClassName: manual #storageClassName要和PV中的一緻
  accessModes:
    - ReadWriteOnce  #accessMode要和PV中的一緻
  resources:
    requests:
      storage: 3Gi #申請3G容量,申請就近原則,如果有一個10G的和一個20G的PV滿足要求,那麼使用10G的PV      

建立 Pod 挂載 PV

apiVersion: v1
kind: Pod
metadata:
  name: task-pv-pod
spec:
  volumes:
    - name: task-pv-storage
      persistentVolumeClaim:
        claimName: task-pv-claim  #使用的PVC的名字
  containers:
    - name: task-pv-container
      image: nginx
      ports:
        - containerPort: 80
          name: "http-server"
      volumeMounts:
        - mountPath: "/usr/share/nginx/html" #容器中挂載的目錄
          name: task-pv-storage      

在主控端上的目錄建立一個檔案:

root@worker01:~# cd /mnt/data/
root@worker01:/mnt/data# echo "volume nginx" > index.html      

嘗試通路 Pod 的服務,可以看到 nginx 的 index.html 檔案已經被修改:

root@master01:~/yaml/volume# kubectl get pod -o wide                                   
NAME          READY   STATUS    RESTARTS   AGE   IP             NODE       NOMINATED NODE   READINESS GATES
task-pv-pod   1/1     Running   0          11m   192.168.5.17   worker01   <none>           <none>
root@master01:~/yaml/volume# curl 192.168.5.17
volume nginx      

删除要按照 Pod-->PVC-->PV 的順序删除,如果先删除了 PVC 會等 Pod 删除掉,才會删除 PVC ,如果先删除了 PV,會等 pod 和 PVC 删除了才會删除 PV。

動态供應

動态卷供應允許按需建立存儲卷。如果沒有動态供應,叢集管理者必須手動地聯系他們的雲或存儲提供商來建立新的存儲卷, 然後在 Kubernetes 叢集建立 PersistentVolume 對象來表示這些卷。動态供應功能消除了叢集管理者預先配置存儲的需要。相反,它在使用者請求時自動供應存儲。

安裝 NFS

安裝NFS服務端

root@master01:/# apt-get -y install nfs-kernel-server 
root@master01:/# systemctl enable nfs-kernel-server.service && systemctl restart nfs-kernel-server.service      

安裝NFS用戶端

在 worker 節點安裝NFS用戶端:

root@worker01:~# apt-get -y install nfs-common      

配置存儲插件

配置RBAC

apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: default
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: nfs-client-provisioner-runner
rules:
  - apiGroups: [""]
    resources: ["persistentvolumes"]
    verbs: ["get", "list", "watch", "create", "delete"]
  - apiGroups: [""]
    resources: ["persistentvolumeclaims"]
    verbs: ["get", "list", "watch", "update"]
  - apiGroups: ["storage.k8s.io"]
    resources: ["storageclasses"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: run-nfs-client-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    # replace with namespace where provisioner is deployed
    namespace: default
roleRef:
  kind: ClusterRole
  name: nfs-client-provisioner-runner
  apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: default
rules:
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: default
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    # replace with namespace where provisioner is deployed
    namespace: default
roleRef:
  kind: Role
  name: leader-locking-nfs-client-provisioner
  apiGroup: rbac.authorization.k8s.io      

部署存儲插件

動态卷供應的實作基于 storage.k8s.io API 組中的 StorageClass API 對象。叢集管理者可以根據需要定義多個 StorageClass 對象,每個對象指定一個存儲插件(又名 provisioner),存儲插件以 Pod 的形式存在于 Kubernetes 叢集中:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-client-provisioner
  labels:
    app: nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: default
spec:
  replicas: 1
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: nfs-client-provisioner
  template:
    metadata:
      labels:
        app: nfs-client-provisioner
    spec:
      serviceAccountName: nfs-client-provisioner
      containers:
        - name: nfs-client-provisioner
          image: quay.io/external_storage/nfs-client-provisioner:latest
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
           # 指定辨別插件的值
            - name: PROVISIONER_NAME
              value: fuseim.pri/ifs    #比對StorageClass的provisioner
            - name: NFS_SERVER
              value: 10.0.1.31  #NFS伺服器的ip位址
            - name: NFS_PATH
              value: /storage  #NFS伺服器的路徑
      volumes:
        - name: nfs-client-root
          nfs:
            server: 10.0.1.31   #NFS伺服器的ip位址
            path: /storage    #NFS伺服器的路徑      

方式一:建立 PVC 自動申請 PV

配置 StorageClass

StorageClass 聲明存儲插件,用于自動建立 PV,provisioner 參數和存儲插件的辨別對應上才能動态供應卷 :

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: managed-nfs-storage
provisioner: fuseim.pri/ifs #要比對nfs deployment env PROVISIONER_NAME的值,預設不支援nfs存儲需要添加插件辨別
parameters:
  archiveOnDelete: "false"      

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nginx-pv-storage
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: managed-nfs-storage
  resources:
    requests:
      storage: 1Gi      

檢視建立的 PVC 和 PV,可以看到我們隻建立了 PVC,PV 是存儲插件自動配置的

root@master01:~/yaml/storageClass# kubectl get pvc
NAME         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS          AGE
nginx-pv-storage   Bound    pvc-e52ac960-182a-4065-a6e8-6957f5c93b8a   1Gi        RWX            managed-nfs-storage   3s
root@master01:~/yaml/storageClass# kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                STORAGECLASS          REASON   AGE
pvc-e52ac960-182a-4065-a6e8-6957f5c93b8a   1Gi        RWX            Delete           Bound    default/nginx-test   managed-nfs-storage            11s      

Pod 使用 PVC 申請 Volume:

apiVersion: v1
kind: Pod
metadata:
  name: nginx-pv-pod
spec:
  volumes:
    - name: nginx-pv-storage
      persistentVolumeClaim:
        claimName: nginx-test
  containers:
    - name: nginx-pv-container
      image: nginx
      ports:
        - containerPort: 80
          name: "nginx-server"
      volumeMounts:
        - mountPath: "/usr/share/nginx/html"
          name: nginx-pv-storage      

方式二:volumeClaimTemplates

除了上面建立 PVC 自動建立 PV,然後 Pod 再聲明使用 PVC 的方式以外,還有一個更簡便的方法,就是使用 volumeClaimTemplates 直接指定 StorageClass 和 申請存儲的大小,動态建立 PVC 和 PV:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx
  serviceName: "nginx"
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: nginx-disk-ssd
          mountPath: /data
  volumeClaimTemplates:
  - metadata:
      name: nginx-disk-ssd
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "managed-nfs-storage"  #storageClass的名字
      resources:
        requests:
          storage: 10Gi      

繼續閱讀