iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 26
0
DevOps

Docker獸 究極進化 ~~ Kubernetes獸系列 第 26

Day-26 瞭解 Kubernetes Storage

前言

從前面的章節中我們知道,Pod是由多個Containers所共用同個環境所生成,並且Pod時時刻刻狀態都有可能變化,也就是說Pod並沒有所謂的持續性,它是暫時的執行個體,也因此我們會遇到一個問題,那就是Pod內、Container內的資料會隨著Pod的消失而一併逝去。

也因此Kubernetes提供了許許多多的Solutions來解決這些問題。

Kubernetes Volume

我們在Docker與Docker-Compose的章節已經學會並運用了volume,也知道volume能夠幫助我們將Container內的資料給掛載出來。那Kubernetes的volume也一樣,它能夠將Container內的資料(創造)掛載到Container外,也就是Pod上。

Example

這邊我們一樣用先前的deployment來說明:

deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ironman
  labels:
    name: ironman
    app: ironman
spec:
  minReadySeconds: 5
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
  selector:
    matchLabels:
      app: ironman
  replicas: 1
  template:
    metadata:
      labels:
        app: ironman
    spec:
      containers:
       - name: ironman
         image: ghjjhg567/ironman:latest
         imagePullPolicy: Always
         ports:
           - containerPort: 8100
         resources:
           limits:
             cpu: "1"
             memory: "2Gi"
           requests:
             cpu: 500m
             memory: 256Mi
         envFrom:
           - secretRef:
               name: ironman-config
         command: ["./docker-entrypoint.sh"]
       - name: redis
         image: redis:4.0
         imagePullPolicy: Always
         ports:
           - containerPort: 6379
       - name: nginx
         image: nginx
         imagePullPolicy: Always
         ports:
           - containerPort: 80
         volumeMounts:
           - mountPath: /etc/nginx/nginx.conf
             name: nginx-conf-volume
             subPath: nginx.conf
             readOnly: true
           - mountPath: /etc/nginx/conf.d/default.conf
             subPath: default.conf
             name: nginx-route-volume
             readOnly: true
         readinessProbe:
           httpGet:
             path: /v1/hc
             port: 80
           initialDelaySeconds: 5
           periodSeconds: 10
      volumes:
        - name: nginx-conf-volume
          configMap:
            name: nginx-config
        - name: nginx-route-volume
          configMap:
            name: nginx-route-volume
  • volumes: 我會create volume並引用已經創建好的configMap

    • volumes.name: volumes名稱
    • configMap: 引用的configMap名稱,若無引用的話則可加上emptyDir,掛載一個空的路徑
    volumes:
      - name: redis-storage
        emptyDir: {}
    
  • volumeMounts: 掛載相關資訊

    • mountPath: 在Pod中,volume掛載出的路徑
    • subPath:掛載的檔案名稱
    • name: volume name
    • readOnly:只可讀取,不可寫入

上述的Volume,雖然已經將Container內的資料給掛載出來,但依然無法避免與Pod共存的命運。也因此我們將介紹PersistentVolume來解決這個困境。

Storage Class

持久性的儲存空間,對用戶設置的PVC 申請屏蔽後端存儲的細節,一方面減輕用戶對於存儲資源細節的關注,另一方面也減輕了管理員手工管理PV 的工作,由系統自動完成PV 的創建和綁定,實現了動態的資源供應。使用基於StorageClass 的動態資源供應模式將逐步成為雲平台的標準存儲配置模式。

Example

ssd storage class

ssd.yaml

kind: StorageClass
apiVersion: storage.k8s.io/v1beta1
metadata:
  name: fast
provisioner: kubernetes.io/gce-pd
parameters:
  type: pd-ssd
  • provisioner: 儲存卷分配器,這裡必需選擇你的儲存卷是由誰分配。若是GKE請使用kubernetes.io/gce-pd,其他請參考下方表格。
  • type: storage的種類,gce有提供pd-ssd、pd-standard兩種
    https://ithelp.ithome.com.tw/upload/images/20201011/20129737B8SorD2sRk.png
Type Provisioner
AWS kubernetes.io/aws-ebs
GCE kubernetes.io/gce-pd
Azure disk kubernetes.io/azure-disk
Azure file kubernetes.io/azure-file

..etc

Persistent Volume

PersistentVolume是Kubernetes cluster當中專門用來儲存的物件,PersistentVolume透過API以及抽象化的的Interface讓使用者來操作它。

也就是說Persistent Volume是個提供虛擬化物件的儲存區,在PV中可能有著許多不同type的Volume,像是gcePersistentDisk、awsElasticBlockStore等。

也因為Persistent Volume是有別於Pod生命週期的物件,也因此在使用前必須先創建它。

https://ithelp.ithome.com.tw/upload/images/20201011/20129737y83bzB1vpB.png

那在有了Persistent Volume後,我們要如何在Pod中使用它呢? 這時候就該我們的Persistent Volume Claim登場了!

Persistent Volume Claim

何謂Persistent Volume Claim呢? Persistent Volume Claim有點像是Storage版本的Pod,透過綁定並消耗Persistent Volume資源來進行資料的儲存與使用,就像是Pod消耗Node的資源來運作一樣。

Pod可以請求特定級別的資源(CPU、Memory),PVC同樣也可與PV要求特定大小與訪問模式的PV。

所以大概的架構圖會長這樣:

https://ithelp.ithome.com.tw/upload/images/20201011/20129737Vgp3d63Ygk.png

  • 管理者透過設定來創建一個可用的Persistent Volume。
  • 使用者創建與Persistent Volume能mapping的Persistent Volume Claim。
  • 透過Persistent Volume Claim讓Pod能夠與Persistent Volume溝通。

訪問儲存資源

Kubernetes有著三種訪問儲存資源的方式,分別為直接訪問、靜態PV與動態PV

直接訪問

直接讓Pod得到Volume相關資訊,由Pod透過Node來訪問Volume

靜態PV

叢集管理者設定PV與其Storage type,之後便可以讓使用者透過建立的PVC來訪問Volume,上面所介紹的PV與PVC就是靜態PV訪問。

https://ithelp.ithome.com.tw/upload/images/20201011/20129737UWTSOwq87R.png

動態PV

當管理員所建立的PV全部都無法匹配使用者的PVC時,Cluster可以動態地為PVC建立Volume。此配置基於StorageClass。PVC請求儲存類(StorageClass),並且管理員必須要建立並配置該StorageClass,該StorageClass才能進行動態的建立。

https://ithelp.ithome.com.tw/upload/images/20201011/20129737W2XEDskCnZ.png

PV & PVC Life cycle

https://ithelp.ithome.com.tw/upload/images/20201011/201297378v6uRdtImn.png

  1. 資源供應Provisioning: Kubernetes支持兩種資源的供應模式靜態Static)和動態(Dynamic)。
    1. Static: 集群管理員手工創建許多PV,在定義PV 時需要將後端存儲的特性進行設置。
    2. Dynamic: 集群管理員無需手工創建PV,而是通過StorageClass 的設置對後端存儲進行描述,標記為某種類型Class。而後系統將自動完成PV 的創建以及與PVC 的綁定。PVC 可以亦可以宣告Class為"",說明該PVC 禁止使用動態模式。
  2. 資源綁定Binding: 在用戶定義好PVC 之後,系統將根據PVC 對存儲資源的請求(存儲空間的訪問模式)在已存在的PV 中選擇一個滿足PVC 要求的PV,一旦找到,就將該PV 與用戶定義的PVC 進行綁定,然後用戶的應用就可以使用這個PVC 了。如果系統中沒有滿足PVC 要求的PV,PVC 則會無限期處於Pending 狀態,直到等到系統管理員創建了一個符合其要求的PV。PV 一旦綁定到某個PVC 上就被這個PVC 獨占,不能再與其他PVC 進行綁定了。在這種情況下,當PVC 申請的存儲空間比PV 的少時,整個PV 的空間都能夠為PVC 所使用,可能會造成資源的浪費。如果資源供應使用的是動態模式,則是在為PVC 找到合適的StorageClass 後,將自動創建一個PV 並完成與PVC 的綁定。
  3. 資源使用Using: Pod 使用volume ,將PVC 掛載到容器內的某個路徑進行使用。volume 的類型為persistentVolumeClaim。在容器應用掛載了一個PVC 後,就能被持續獨占使用。不過,多個Pod 可以掛載同一個PVC,應用程序需要考慮多個實例共同訪問一塊存儲空間的問題。
  4. 資源釋放Releasing: 當用戶對存儲資源使用完畢後,用戶可以刪除PVC,與該PVC 綁定的PV 將被標記為已釋放,但還不能立刻與其他PVC 進行綁定。通過之前PVC 寫入的數據可能還留在存儲設備上,只有在清除之後該PV 才能再次使用。
  5. 資源回收Reclaiming: 對於PV,管理員可以設定回收策略(Reclaim Policy),用於設置與之綁定的PVC 釋放資源之後,對於遺留詩句如何處理。只有PV 的存儲空間完全回收,才能供新的PVC 綁定和使用。

Example

Persistent Volume

pv.yaml

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: fast
  mountOptions:
    - hard
    - nfsvers=4.1
  nfs:
    path: /tmp
    server: 172.17.0.2
  • spec.capacity: 宣告該PV的儲存容量
  • spec.volumeMode: 所有的 volume plugin 都會自動在 PV 上建立 file system,並且為預設值。若選擇Block,則PV會被以raw block device使用。
  • spec.accessModes: 讀寫規則,有ReadWriteOnce、ReadOnlyMany與ReadWriteMany三選項。
  • spec.persistentVolumeReclaimPolicy: 回收策略,有Retain, Recycle與Delete三選項。
  • spec.storageClassName: PV所設定的類別名稱,只可讓帶有同種類別名稱的PVC做連結使用。
  • **spec.mountOptions:**不同volume type有著不同的mountOptions,詳請參考https://kubernetes.io/docs/concepts/storage/persistent-volumes/#mount-options

Persistent Volume Claim

pvc.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 5Gi
  storageClassName: fast

這邊的Key都與PV相似,就只說幾個要重點注意的地方

  1. spec.storageClassName:透過class name來找到相互連結的PV,由於GKE預設已經有了 standard PV,這邊我們就直接使用它

Pod

deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ironman
  labels:
    name: ironman
    app: ironman
spec:
  minReadySeconds: 5
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
  selector:
    matchLabels:
      app: ironman
  replicas: 1
  template:
    metadata:
      labels:
        app: ironman
    spec:
      containers:
       - name: ironman
         image: ghjjhg567/ironman:latest
         imagePullPolicy: Always
         ports:
           - containerPort: 8100
         resources:
           limits:
             cpu: "1"
             memory: "2Gi"
           requests:
             cpu: 500m
             memory: 256Mi
         envFrom:
           - secretRef:
               name: ironman-config
         command: ["./docker-entrypoint.sh"]
       - name: redis
         image: redis:4.0
         imagePullPolicy: Always
         ports:
           - containerPort: 6379
       - name: nginx
         image: nginx
         imagePullPolicy: Always
         ports:
           - containerPort: 80
         volumeMounts:
           - mountPath: /etc/nginx/nginx.conf
             name: nginx-conf-volume
             subPath: nginx.conf
             readOnly: true
           - mountPath: /etc/nginx/conf.d/default.conf
             subPath: default.conf
             name: nginx-route-volume
             readOnly: true
           - mountPath: "/var/www/html"
             name: mypd
         readinessProbe:
           httpGet:
             path: /v1/hc
             port: 80
           initialDelaySeconds: 5
           periodSeconds: 10
      volumes:
        - name: nginx-conf-volume
          configMap:
            name: nginx-config
        - name: nginx-route-volume
          configMap:
            name: nginx-route-volume
        - name: mypd
          persistentVolumeClaim:
            claimName: pvc

Deployment

  • deploy pv.yaml
$ kubectl apply -f pv.yaml
persistentvolume/pv created

$ kubectl get pv
NAME     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM         STORAGECLASS   REASON   AGE
pv       5Gi        RWO            Recycle          Bound       default/pvc   fast                    9m4s
  • deploy pvc.yaml
$ kubectl apply -f pvc.yaml
persistentvolumeclaim/pvc created
  • 並且由下方command可以看到,Kubernetes自動binding最合適的persistent volume,也就是剛剛我們建立的pv
$ kubectl get
NAME        STATUS        VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc         Bound         pv                                         5Gi        RWO            fast           3s
  • deploy deployment.yaml
$ kubectl apply -f deployment.yaml
deployment.apps/ironman created

  • 並且確認deployment正常運作且有binding pvc後,代表我們大功告成。
$ kubectl describe deployment ironman
Name:                   ironman
Namespace:              default
CreationTimestamp:      Sun, 11 Oct 2020 13:08:59 +0800
Labels:                 app=ironman
                        name=ironman
Annotations:            deployment.kubernetes.io/revision: 1
                        kubectl.kubernetes.io/last-applied-configuration:
                          {"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"labels":{"app":"ironman","name":"ironman"},"name":"ironman","nam...
Selector:               app=ironman
Replicas:               1 desired | 1 updated | 1 total | 0 available | 1 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        5
RollingUpdateStrategy:  1 max unavailable, 1 max surge
Pod Template:
  Labels:  app=ironman
  Containers:
   ironman:
    Image:      ghjjhg567/ironman:latest
    Port:       8100/TCP
    Host Port:  0/TCP
    Command:
      ./docker-entrypoint.sh
    Limits:
      cpu:     1
      memory:  2Gi
    Requests:
      cpu:     500m
      memory:  256Mi
    Environment Variables from:
      ironman-config  Secret  Optional: false
    Environment:      <none>
    Mounts:           <none>
   redis:
    Image:        redis:4.0
    Port:         6379/TCP
    Host Port:    0/TCP
    Environment:  <none>
    Mounts:       <none>
   nginx:
    Image:        nginx
    Port:         80/TCP
    Host Port:    0/TCP
    Readiness:    http-get http://:80/v1/hc delay=5s timeout=1s period=10s #success=1 #failure=3
    Environment:  <none>
    Mounts:
      /etc/nginx/conf.d/default.conf from nginx-route-volume (ro,path="default.conf")
      /etc/nginx/nginx.conf from nginx-conf-volume (ro,path="nginx.conf")
      /var/www/html from mypd (rw)
  Volumes:
   nginx-conf-volume:
    Type:      ConfigMap (a volume populated by a ConfigMap)
    Name:      nginx-config
    Optional:  false
   nginx-route-volume:
    Type:      ConfigMap (a volume populated by a ConfigMap)
    Name:      nginx-route-volume
    Optional:  false
   mypd:
    Type:       PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
    ClaimName:  pvc
    ReadOnly:   false
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
  Progressing    True    ReplicaSetUpdated
OldReplicaSets:  ironman-6d598f45dd (1/1 replicas created)
NewReplicaSet:   <none>
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  5m3s  deployment-controller  Scaled up replica set ironman-6d598f45dd to 1

Github Repo

本篇章所有程式碼將放在下面的github project當中的branch day-26

後記

這章節我們學習了Kubernetes用來持久性儲存的工具與空間,也就是StorageClass、PV以及PVC,那在下章節將介紹先前提及的StatefulSet,到時候大家也會了解為何要在StatefulSet前介紹Kubernetes Storage相關工具的理由。

https://ithelp.ithome.com.tw/upload/images/20201011/20129737CZfbfRWwkE.png

https://ithelp.ithome.com.tw/upload/images/20201011/20129737yOgqECAXNg.png

Reference

https://itw01.com/VT2ZPED.html

https://wiki.shileizcc.com/confluence/display/KUB/Kubernetes+PV+and+PVC+Life+Cycle


上一篇
Day-25 認識DaemonSet
下一篇
Day-27 使用StatefulSet
系列文
Docker獸 究極進化 ~~ Kubernetes獸30

尚未有邦友留言

立即登入留言