網路太難,短時間理解不完 QQ
先來寫別的題目
當 Pod (App) 在運行時是需要保存狀態的,就需要考慮資料保存的問題。例如資料庫要保存寫入的資料,總不能 Pod 關掉後這些資料就不見。另外有時可能會需要多個 Container 使用同一份檔案。這篇會提一些跟 Storage 相關的元件:Volume、Persistent Volume、Persistent Volume Claim & Storage Class。
直接從 Pod spec 來理解好了,前面幾篇文章應該也有用過
apiVersion: v1
kind: Pod
metadata:
	name: random-number-generator
spec:
	containers:
	- image: alpine
		name: alpine
		command: ["/bin/sh","-c"]
		args: ["shuf -i 0-100 -n 1 >> /opt/number.out;"]
		volumeMounts:
		- mountPath: /opt # directory in the container
			name: data-volume # what volume to be mounted
	volumes:
	- name: data-volume # the name of the volume
		hostPath:
			path: /data
			type: Directory
volumes 可分為 ephemeral volumes & persistent volumes。如字面意思,一種是暫時性的儲存,一種是持久性的儲存。當 Pod 消失的時候,ephemeral volumes 也會跟著被砍掉,但 persistent volumes 會留著,可以讓重啟後的 Pod 繼續使用。
上面的 spec 中,Pod level 可定義不同的 volumes,可以定義多個 volumes。在 Container level,可以將外邊定義好的 volumes 掛載進 Container 中。這邊就是定義了 data-volume 這個 volume,它的種類是 host directory,實際位置位於宿主機的 /data 位置。然後在 volumeMounts 這邊定義說要把 data-volume 這個 volume 掛載到 Container 中的 /opt 位置。
Kubernetes 支持多個種類的 volumes,不過要注意版本,有些 volume type 可能不被支援了。
下面列幾個目前我自己練習有使用過的 -
ConfigMap 前面提到可以設定 key-value,作為設置檔、環境變數定義給 Pod 使用。使用方式可以利用 Volume 將 ConfigMap 掛進 Container 中,變成 Container direcotry 中的一個檔案。
例如:
apiVersion: v1
kind: Pod
metadata:
  name: configmap-pod
spec:
  containers:
    - name: test
      image: busybox:1.28
      command: ['sh', '-c', 'echo "The app is running!" && tail -f /dev/null']
      volumeMounts:
        - name: config-vol
          mountPath: /etc/config
  volumes:
    - name: config-vol
      configMap:
        name: log-config
        items:
          - key: log_level
            path: log_level
在 test container 中,進去 /etc/config 這個位置,可看到 log_level 這個檔案 (檔名或是說 path 來自於 items.path ),其中的內容會是 ConfigMap 中 key = log_level 對應的 value。
這邊要注意在使用上必須要先建立 ConfigMap,才能在 Volume 使用它。以及 ConfigMap 掛進去會是 readOnly 。
之前的文章:Day 9 ConfigMap & Secret
如同名字,這個類型的 volume 一開始會是空的,當 Pod 被放到某個 Node 上運行時 emptyDir volume 會被建立,當 Pod 還在這個 Node 上運行時會一直存在。但是 Pod 如果被刪除,emptyDir 中的資料也會被刪除。不過如果只是 Container Crash,Pod 沒有被移除時 emptyDir 不會被影響。
apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: registry.k8s.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir:
      sizeLimit: 500Mi
之前的文章:Day 8 Multi Container & Init Container
這種類型的 volume 是將 host node 的檔案或目錄掛進 Pod 之中,不過正式環境不太會這樣使用。
使用 hostPath 還能指定不同的 type ,例如:
Directory
DirectoryOrCreate
File
FileOrCreate
使用上會有一些事項要注意,像是不同的 Node 可能文件跟目錄跟想像中的不同,因 Pod 有可能被分配到不同節點上,有可能會導致不一致。另外要寫入文件需要 root 權限,這時必須讓 Container 運行時使用 root 或是更改檔案的權限讓非 root 用戶能更改這個檔案。
apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: registry.k8s.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-pd
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      path: /data       # directory location on host 
      type: Directory   # this field is optional
不過在本機上練習還是使用 hostPath 比較好觀察。
這應該是目前主流使用的方式。PersistentVolumeClaim 會與 PersistentVolume 綁定,PersistentVolume 背後可能是 Google 的 Persistent Disk 或是別種儲存系統,但 Pod 這邊不需要知道細節,Pod 在使用上只要指定使用哪一個 PersistentVolumeClaim 即可。
這邊只提一點點概念,檔案儲存有多種類型,Kubernetes 原生有支持一些類型。但這些設置是寫在 Kubernetes 核心程式碼之中的。也就是說假設有什麼新的儲存系統要加進去,會需要更新 Kubernetes 的 source code。要使用也必須更新 Kubernetes 版本。
Out-of-Tree (程式碼邏輯不在 Kubernetes source code 之中) 套件則可以讓第三方的 storage vendors 去發展他們自己的程式碼,不用跟 Kubernetes 綁在一起。但這邊需要有一個標準化的介面 - CSI,讓各式的儲存系統能夠讓容器編排系統(Container Orchestration Systems, e.g. Kubernetes, Swam) 能夠使用。
Persistent Volume 是 Cluster level 的 object,如名稱所表示,這是個持久性的 Volume。可以想成這是一個先預先配置好的 Volume,等 Pod 需要使用的時候就能使用 (透過 PersistentVolumeClaim)。為了減少打字字數,下面 PersistentVolume 會以 PV 簡稱、PVC 則是指 PersistentVolumeClaim。
PV 與 Pod 的生命週期是各自獨立的,如果 Pod 死掉,PV 還是會存在。PV 的建立可以分成動態及靜態兩種。這邊先說靜態的,也就是 Cluster administrator 來建立一些 PV,讓 Cluster user 可以使用。一樣使用 yaml -
apiVersion: v1
kind: PersistentVolume
metadata:
	name: pv
spec:
	accessModes:
		- ReadWriteOnce
	capacity:
		storage: 1Gi
	persistentVolumeReclaimPolicy: Retain
	hostPath:
		path: /tmp/data
上面有些關鍵字,先來看 accessModes,這是定義這個 volume 在 host 中是以什麼樣的方式掛載,直接看可能的 value 比較好懂
*: 沒有驗證過,不確定多個 nodes 使用及多個 Pods 使用的情形
官網上有列出各式不同的 Plugin 支援的 Access Modes - https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes
下一個比較特殊的關鍵字:persistentVolumeReclaimPolicy
這是指 與 PV binding 的 PVC 被刪除 的時候,PV 中的資料該怎麼處理。
有可能有 3 種值 -
persistentVolumeReclaimPolicy
整理一下不同的 Reclaim Policy,PV 使用本機的 direcotry,刪除 PVC 的話 -
| persistentVolumeReclaimPolicy | PV | mounted directory | 
|---|---|---|
| Retain | 不會被刪 | 不變 | 
| Delete | 跟著被刪 | 不變 | 
| Recycle | 不會被刪, Status=Release | directory 內的檔案被刪掉 | 
PVC 用來跟 PV bind 的 Object。這樣的設計可以提供更靈活、可管理和可擴展的儲存管理解決方案,也讓儲存這塊與應用程式解藕。
使用上會是
先來看 spec -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: myclaim
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 8Gi
  storageClassName: slow
  selector:
    matchLabels:
      release: "stable"
    matchExpressions:
      - {key: environment, operator: In, values: [dev]}
如果是以上的 PVC,在 Pod 使用 PVC 時,Kubernetes 會去找符合 spec 的 PV,例如
如果沒有配對到符合的 PV,PVC 會處在 Pending 的狀態。或是說,如果上層的 Storage Class 定義說 volume binding mode = WaitForFirstConsumer,那 PVC 會等到被某個 Pod 使用時才跟 PV binding。
另外補充一下如果 PVC 不指定 storageClassName ,在 k3d cluster 中會有預設的 storage class, storageClassName = local-path ,這會跟預設的 StorageClass object 的設置相關。(https://kubernetes.io/docs/concepts/storage/persistent-volumes/#class-1)
Pod 如果要使用,PVC 必須跟 Pod 在同一個 Namespace → PVC 是 namespace level objects。
apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
    - name: myfrontend
      image: nginx
      volumeMounts:
      - mountPath: "/var/www/html"
        name: mypd
  volumes:
    - name: mypd
      persistentVolumeClaim:
        claimName: myclaim
Storage Class 這個 object 可以用來動態配置 PersistentVolume,就不需要手動配置了,在管理上會方便許多。
Storage Class 可以定義不同的後端儲存配置,例如不同的 Cloud provider、不同的儲存設備。
(來源: https://www.udemy.com/course/certified-kubernetes-administrator-with-practice-tests/)
使用上是
storageClassName
spec 如下 -
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: google-storage
provisioner: kubernetes.io/gce-pd
parameters:
	type: pd-standard # [ pd-standard | pd-ssd ]
	replication-type: none # [ none | regional-pd ]
reclaimPolicy: Retain
volumeBindingMode: WaitForFirstConsumer
provisioner : 定義說要用哪種 volume plugin,一定要填的 fieldparameters : 根據不同的 provisioner 會有不同的配置reclaimPolicy : 預設會是 Delete,另外一個可配置的值是 Retain,這邊對應到 PV 的 persistentVolumeReclaimPolicy
volumeBindingMode : 這邊的 volume binding mode 設成 WaitForFirstConsumer,Kubernetes 不會馬上為 PVC 建立 PV,而是等到 Pod 使用相對應的 PVC,才建立 PersistentVolume 並綁定 PVC & PV。另一個可能的值為 Immediate
底下是小疑問
如果 Storage Class provisioner 設為 local,不支援動態配置 PV。
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
但是在本機 k3d 實驗
apiVersion: v1
kind: PersistentVolume
metadata:
  name: prac-pv
spec:
  storageClassName: prac
  accessModes:
    - ReadWriteOnce
  hostPath:
    path:
      "/tmp/k8s_storage_test"
    type: "DirectoryOrCreate"
  capacity:
    storage: 500Mi
  persistentVolumeReclaimPolicy: Retain
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: prac-pvc
  namespace: prac
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: prac
  resources:
    requests:
      storage: 100Mi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: prac
  name: nginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      name: nginx-pod
  template:
    metadata:
      name: nginx-pod
      labels:
        name: nginx-pod
    spec:
      containers:
        - name: nginx-container
          image: nginx
          volumeMounts:
            - name: mount-prac-pv
              mountPath: /test
      volumes:
        - name: mount-prac-pv
          persistentVolumeClaim:
            claimName: prac-pvc
(須先建立 prac namespace)
最後總結一下 -
Reference
https://kubernetes.io/docs/concepts/storage/volumes/
https://kubernetes.io/docs/concepts/storage/persistent-volumes/
https://kubernetes.io/docs/tasks/configure-pod-container/configure-persistent-volume-storage/
https://kubernetes.io/docs/tasks/administer-cluster/change-pv-reclaim-policy/
https://kubernetes.io/docs/concepts/storage/storage-classes/
https://kubernetes.io/docs/concepts/storage/dynamic-provisioning/