iT邦幫忙

2024 iThome 鐵人賽

DAY 17
1
Kubernetes

都什麼年代了,還在學 Kubernetes系列 第 17

學 Kubernetes 的第十七天 - Storage - PV & PVC

  • 分享至 

  • xImage
  •  

數據的持久性就像是企業運營中的穩固倉庫,確保所有重要資產都能安全存放並隨時可取。Kubernetes 提供了兩個關鍵工具——PersistentVolume (PV)PersistentVolumeClaim (PVC),來高效管理這些“數據倉庫”。

PersistentVolume (PV)

PV 就像是集群中的實際倉庫空間,是 Kubernetes 集群中的一個存儲資源,代表具體的存儲位置,如 NFS、GCE Persistent Disk、AWS EBS 等。PV 由集群管理員創建和管理,並且與 Pod 解耦,意味著它的生命週期獨立於任何單一 Pod。這樣,即使 Pod 被刪除或重新調度,PV 仍然保留其中的數據,確保數據的持久性和安全性。

PersistentVolumeClaim (PVC)

PVC 類似於對倉庫空間的租用請求,描述了所需的存儲容量和訪問模式。具體來說,PVC 是 Pod 對 PV 的存儲需求申請,用戶可以根據應用的需求創建 PVC,例如申請特定大小的存儲空間(如 10Gi)以及訪問模式(如 ReadWriteOnce)。Kubernetes 會自動匹配符合 PVC 要求的 PV,並將兩者綁定在一起,實現存儲資源的動態分配和高效利用。

https://ithelp.ithome.com.tw/upload/images/20241001/201682128iXqpR8NKE.png

為什麼需要 PV & PVC

在 Kubernetes 中,PersistentVolume (PV)PersistentVolumeClaim (PVC) 的設計旨在將存儲資源與 Pod 解耦,從而實現更靈活和高效的存儲管理。這種解耦帶來了以下主要優點:

  1. 數據持久性:PV 可以在 Pod 被刪除或重新調度後保留數據,確保應用程式的數據不會丟失,對於需要持久化數據的應用(如資料庫)尤為重要。
  2. 集中管理:集群管理員可以集中配置和管理存儲資源,用戶僅需通過 PVC 提出存儲需求,簡化了存儲管理流程,提高資源利用率。
  3. 靈活調度:PVC 允許應用程式在不同節點上運行,同時保持對存儲的持續訪問,增強了應用的可用性和可擴展性。

優勢

與其他卷(如 emptyDirhostPath)相比,PV 和 PVC 具有以下顯著優勢:

  1. 持久性與獨立性:PV 與 Pod 分離,數據可在 Pod 重啟或刪除後依然存在,並可在多個 Pod 之間共享或重用。
  2. 多樣性與靈活性:支持多種存儲後端(如 NFS、雲存儲、塊存儲),滿足不同應用的性能和容量需求。
  3. 動態供應:結合 StorageClass,Kubernetes 可根據 PVC 自動創建符合要求的 PV,減少手動配置,提高效率。
  4. 訪問模式控制:支持不同的訪問模式(如 ReadWriteOnceReadOnlyManyReadWriteMany),確保數據的安全性和共享需求。
  5. 數據保護:提供回收策略(RetainRecycleDelete),靈活管理 PVC 刪除後的 PV 行為,保障數據安全。

透過這些優勢,PV 和 PVC 成為 Kubernetes 中管理持久性存儲的核心組件,為應用提供可靠的數據持久化支持,並提升整體系統的靈活性與效率。

應用場景

  1. 資料庫存儲:使用 PV 和 PVC 來管理 MySQL、PostgreSQL 等資料庫的持久性存儲,確保數據的持久性和高可用性。
  2. 內容管理系統:像 WordPress 這樣的應用程式需要持久性存儲來保留用戶上傳的文件和數據。
  3. 資料備份與恢復:PV 可以用來存儲應用程式的備份數據,確保在數據丟失或損壞時能夠恢復。

組態檔案說明

以下是一個簡單的 PV 和 PVC 組態檔範例:

PersistentVolume (PV) 組態檔:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-example
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  nfs:
    path: /exported/path/on/server
    server: nfs-server.example.com
  • spec
    • capacity
      • storage: 10Gi:指定 PV 的存儲容量為 10 GiB。
    • accessModes
      • ReadWriteOnce:該卷可以以讀寫方式掛載,但只能由一個節點掛載。
    • persistentVolumeReclaimPolicy: Retain:當使用該卷的 PersistentVolumeClaim (PVC) 被刪除時,PV 不會被自動刪除,而是會保留數據供後續使用。
    • nfs:使用 NFS 網絡文件系統作為存儲後端。
      • path: /exported/path/on/server:指定 NFS 伺服器上導出的目錄路徑。
      • server: nfs-server.example.com:指定 NFS 伺服器的主機名或 IP 地址。

PersistentVolumeClaim (PVC) 組態檔:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-example
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
  • accessModes
    • ReadWriteOnce:PVC 請求一個可以被單個節點以讀寫模式掛載的 PersistentVolume。
  • resources
    • requests
      • storage: 10Gi:PVC 請求的存儲容量為 10GiB。

當這個 PVC 被創建後,Kubernetes 會自動搜索一個符合要求的 PV(如上面的 pv-example)並將其綁定到該 PVC。這樣,任何使用 pvc-example 的 Pod 都可以掛載到對應的 PV 上,並讀寫數據。

這樣的設計確保了應用程式數據的持久性和高可用性,即使 Pod 被重新調度或重新啟動,數據仍然可以被安全地保留和存取。

實作

在這次練習中,我們會建立 hostPath 類型的 PersistentVolume。也就是使用節點上的檔案或目錄來模擬網路附加儲存。

注意:

在生產環境的叢集中,我們不會也不應該使用 hostPath,而是使用其他網路儲存資源作為 PersistentVolume 的來源,比如 Google Compute Engine 持久盤捲、NFS 共享卷或 Amazon Elastic Block Store 卷等等。

建立 PersistentVolume

組態檔案: pv.yaml

apiVersion: v1
kind: PersistentVolume
metadata:
  name: task-pv-volume
  labels:
    type: local
spec:
  storageClassName: manual
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/mnt/data"
  • 建立 PersistentVolume
kubectl apply -f pv.yaml
  • 查看 PersistentVolume 資訊:
kubectl get pv task-pv-volume
---
NAME             CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
task-pv-volume   10Gi       RWO            Retain           Available           manual         <unset>                          13s

輸出結果顯示該 PersistentVolume 的狀態(STATUS)為 Available。 這意味著它還沒有被繫結給 PersistentVolumeClaim。

建立 PersistentVolumeClaim

我們要建立一個 PersistentVolumeClaim,它請求至少 3 GB 容量的卷, 該卷一次最多可以為一個節點提供讀寫訪問。

組態檔案: pvc.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: task-pv-claim
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 3Gi
  • 建立 PersistentVolumeClaim
kubectl apply -f pvc.yaml

建立 PersistentVolumeClaim 之後,Kubernetes 控制平面將尋找滿足申領要求的 PersistentVolume。 如果控制平面找到具有相同 StorageClass 的適當的 PersistentVolume, 則將 PersistentVolumeClaim 繫結到該 PersistentVolume 上。

  • 查看 PersistentVolume, PersistentVolumeClaim 信息
kubectl get pv,pvc
---
NAME                              CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                   STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
persistentvolume/task-pv-volume   10Gi       RWO            Retain           Bound    default/task-pv-claim   manual         <unset>                          2m32s

NAME                                  STATUS   VOLUME           CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
persistentvolumeclaim/task-pv-claim   Bound    task-pv-volume   10Gi       RWO            manual         <unset>                 12s

PersistentVolume 的狀態 (STATUS) 變為了 Bound,並且 PVC 的卷 (VOLUME) 也指向了那個 PersistentVolume。

在 Pod 掛載 PVC

組態檔案: pod.yaml

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

這個組態檔案定義了兩個 Pod,它們分別名為 foobar。兩個 Pod 使用了相同的 PersistentVolumeClaim(PVC) 來掛載儲存卷,並在容器中使用該儲存卷,並將其掛載到容器內的 /usr/share/nginx/html 路徑上。這樣 foobar 都可以共同代理這個 PVC 內的文件。

  • 查詢 Pod 建立的節點
kubectl get pod -o wide
---
NAME   READY   STATUS    RESTARTS   AGE   IP           NODE              NOMINATED NODE   READINESS GATES
bar    1/1     Running   0          12m   10.244.2.5   wslkind-worker2   <none>           <none>
foo    1/1     Running   0          12m   10.244.2.6   wslkind-worker2   <none>           <none>

可以看到,目前 Pod 都是建立在 wslkind-worker2 的節點上。

  • 檢查該節點上的 /mnt/data 路徑
docker exec -it wslkind-worker2 /bin/sh
# ls /mnt/data
---
#

目前什麼檔案都沒有。

  • 進入 foo Pod,並在 nginx 服務器根目錄建立一個 Html 網頁,裡面寫入系統時間
kubectl exec -it foo -- /bin/sh
# echo `date` > /usr/share/nginx/html/index.html
  • 訪問 foo Pod 的 nginx 伺服器,檢查剛剛寫入的內容
# curl 0.0.0.0
---
Tue Jul 23 09:40:50 UTC 2024
  • bar Pod 中,訪問 nginx 伺服器
$ kubectl exec -it bar -- /bin/sh
# curl 0.0.0.0
---
Tue Jul 23 09:50:34 UTC 2024

顯示出跟 foo Pod 一樣的時間,代表剛剛在 foo 寫入的內容是存在的。

  • 回到 wslkind-worker2 節點內,檢查 /mnt/data 路徑
docker exec -it wslkind-worker2 /bin/sh
# cat /mnt/data/index.html
---
Tue Jul 23 09:50:34 UTC 2024

剛剛寫入的檔案也在這裡。

驗證 PV 的永久性

接下來我們要將 Pod 刪除重建,來驗證 PV 的內容是永久性的。

  • 刪除 Pod
kubectl delete -f pod.yaml
---
pod "foo" deleted
pod "bar" deleted
  • 刪除 PVC
kubectl apply -f pvc.yaml
---
persistentvolumeclaim/task-pv-claim created
  • 檢查 PV 的狀態
kubectl get pv,pvc
---
NAME                              CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS     CLAIM                   STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
persistentvolume/task-pv-volume   10Gi       RWO            Retain           Released   default/task-pv-claim   manual         <unset>                          103m

現在 PV 狀態變成 release,代表它已經被解除 PVC 綁定,等待回收。

  • 回到 wslkind-worker2 節點內,檢查 /mnt/data 路徑
docker exec -it wslkind-worker2 /bin/sh
# cat /mnt/data/index.html
---
Tue Jul 23 09:50:34 UTC 2024

可以發現儘管我們刪除了 Pod 和 PVC,檔案依然保留在其中。

由於我們之前已經使用 PVC 申請了 PV,通常是需要回收才能再次使用。不過我們可以透過修改 PV 組態內容的 claimRef 欄位,重置 PV 的狀態。

  • 透過 patch 複寫 PV 的組態內容
kubectl patch pv task-pv-volume -p '{"spec":{"claimRef": null}}'
  • 檢查 PV 的狀態
kubectl get pv
---
NAME             CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
task-pv-volume   10Gi       RWO            Retain           Available           manual         <unset>                          105m
  • 重新建立 PVC 和 Pod
kubectl apply -f pvc.yaml
---
persistentvolumeclaim/task-pv-claim created
kubectl get pv,pvc
---
NAME                              CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                   STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
persistentvolume/task-pv-volume   10Gi       RWO            Retain           Bound    default/task-pv-claim   manual         <unset>                          106m

NAME                                  STATUS   VOLUME           CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
persistentvolumeclaim/task-pv-claim   Bound    task-pv-volume   10Gi       RWO            manual         <unset>                 2s
kubectl apply -f pod.yaml
---
pod/foo created
pod/bar created
  • foo Pod 中,訪問 nginx 伺服器
$ kubectl exec -it foo -- /bin/sh
# curl 0.0.0.0
---
Tue Jul 23 09:50:34 UTC 2024

StorageClass 的補充

PV 對象的創建,是由運維人員完成的。但是,在大規模的生產環境裡,這其實是一個非常麻煩的工作。這是因為,一個大規模的 Kubernetes 集群裡很可能有成千上萬個 PVC,這就意味著運維人員必須得事先創建出成千上萬個 PV。更麻煩的是,隨著新的 PVC 不斷被提交,運維人員就不得不繼續添加新的、能滿足條件的 PV,否則新的 Pod 就會因為 PVC 綁定不到 PV 而失敗。在實際操作中,這幾乎沒辦法靠人工做到。

為了進一步提升存儲管理的靈活性和自動化,Kubernetes 引入了 StorageClass。它定義了存儲的類型和配置,指定了如何動態地供應 PV

  • 動態供應:當用戶創建 PVC 並指定某個 StorageClass,Kubernetes 會根據該類型自動創建相應配置的 PV,無需手動介入。
  • 定義存儲屬性StorageClass 可以設置存儲後端的具體屬性,如性能(IOPS)、冗餘(RAID)、備份策略等,以滿足不同應用的需求。

簡單來說,StorageClass 對象的作用,其實就是創建 PV 的模板。用戶只需關注 PVC 的需求描述,StorageClass 便負責具體的存儲資源配置和管理,進一步簡化了存儲管理流程。

小結

透過 PersistentVolumePersistentVolumeClaimStorageClass ,Kubernetes 成功地將存儲資源與應用容器解耦,提供了靈活且高效的數據持久化管理方案。這不僅確保了應用數據在 Pod 重啟或重新調度後的持久性,還簡化了存儲資源的管理,使集群管理員能夠集中控制存儲資源,而用戶則專注於描述他們的存儲需求。


參考


上一篇
學 Kubernetes 的第十六天 - Storage - Secret
下一篇
學 Kubernetes 的第十八天 - Workloads - StatefulSet
系列文
都什麼年代了,還在學 Kubernetes33
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言