和 Docker 類似,Kubernetes 中也提供了 Volume 來實現資料卷掛載,但 Kubernetes 中 Volume 是基於 Pod,而不是容器,它可被 Pod 中多個容器共享,另外 Kubernetes 中提供比較豐富的 Volume 型別,如:emptyDir、hostPath、nfs、persistentVolumeClaim、downwardAPI、secret、configMap 等,每種型別都有其特點及使用場景。
下面將介紹幾種常用 Volume 型別的使用方式,在這之前先在 k8sdemo .NET Core 服務中新增以下兩個介面(映象版本升級為 1.2.0),以方便後面效果演示。
[HttpGet]public string GetConfig([FromQuery]string key){ // ......}[HttpGet]public string GetVolumeData(){ // ......}複製程式碼
GetConfig:通過傳入配置檔案 appsettings.json 的 key 獲取對應值; GetVolumeData:獲取容器內 /Data/data.txt 的檔案內容;
emptyDiremptyDir 的初始狀態是一個沒有任何內容的 Volume,其生命週期與 Pod 一致,當 Pod 中的容器掛掉時,emptyDir Volume 中的內容不會被清除,容器重啟後資料依然可見。只有當整個 Pod 從叢集中被刪除,emptyDir Volume 中的內容才會被清除。如下:emptyDir Volume 位於 Pod 內。
通過以下配置檔案建立的 Pod 中將包含 k8sdemo 和 busybox 兩個 container,busybox 是一個集成了一些常用 linux 命令的映象,這裡將使用它在 Pod 內進行檔案內容修改。k8sdemo 容器的 /app/Data/ 目錄檔案與 busybox 容器的 /data/ 目錄檔案將通過 emptyDir Volume 進行共享。
apiVersion: apps/v1kind: Deploymentmetadata: name: emptydir-demospec: replicas: 1 selector: matchLabels: name: emptydir-demo template: metadata: labels: name: emptydir-demo spec: containers: - name: k8sdemo image: beckjin/k8sdemo:1.2.0 volumeMounts: - mountPath: /app/Data/ name: share ports: - containerPort: 80 - name: busybox image: busybox command: - "/bin/sh" - "-c" - "sleep 3600" volumeMounts: - mountPath: /data/ name: share volumes: - name: share emptyDir: {}---apiVersion: v1kind: Servicemetadata: name: emptydir-demo-servicespec: selector: name: emptydir-demo type: NodePort ports: - port: 80 targetPort: 80複製程式碼
執行命令 kubectl exec -it emptydir-demo-746f49b55b-p6pzz -c busybox -- /bin/sh 進入 busybox 容器,然後執行 echo 'emptyDir Volume' > /data/data.txt,最後訪問 k8sdemo 服務的 GetVolumeData 介面獲取檔案內容:
hostPathhostPath 型別是掛載宿主機上的檔案或目錄到 Pod 中,與 Pod 所在的 Node 是強關聯的,所以當 Pod 因重啟被重新排程時,一定要確保所在主機的相關檔案或目錄的正確性,如下:
如下配置中 replicas 欄位設定為 2 ,正常情況下 Pod 將會在 node1 和 node2 上分別被建立,另外 hostPath 欄位中的 path 指定了 /data/k8sdemo/ 目錄掛載到容器內的 /app/Data/,所以分別在 node1 和 node2 建立 /data/k8sdemo/data.txt ,內容為 node1 hostPath Volume 和 node2 hostPath Volume。
kind: Deploymentmetadata: name: hostpath-demospec: replicas: 2 selector: matchLabels: name: hostpath-demo template: metadata: labels: name: hostpath-demo spec: containers: - name: k8sdemo image: beckjin/k8sdemo:1.2.0 volumeMounts: - mountPath: /app/Data/ name: share ports: - containerPort: 80 volumes: - name: share hostPath: path: /data/k8sdemo type: Directory---apiVersion: v1kind: Servicemetadata: name: hostpath-demo-servicespec: selector: name: hostpath-demo type: NodePort ports: - port: 81 targetPort: 80複製程式碼
訪問 k8sdemo 服務的 GetVolumeData 介面獲取檔案內容,當路由到不同 Pod(即不同的 node) 返回內容將不一樣,如下:
nfsNFS(network file system) 網路檔案系統,類似 Windows 中的資料夾共享。首先在 Kubernetes 叢集外搭建一個 NFS Server,然後指定檔案目錄進行共享,最終與 Pod 內的容器關聯,實現資料卷掛載,如下:
NFS Server 搭建在機器上安裝依賴元件(叢集外的機器 192.168.1.13,並關閉防火牆)yum install -y nfs-utils rpcbind 複製程式碼將主機上的 /share 目錄作為共享目錄,如果多個目錄可以新增多行[root@localhost ~]# vim /etc/exports /share 192.168.1.0/24(insecure,rw,no_root_squash) 複製程式碼啟動 NFSsystemctl start rpcbind.service systemctl enable rpcbind.service systemctl start nfs.service systemctl enable nfs.service 複製程式碼Kubernetes 叢集內各節點安裝 nfs-utils,方便使用 showmountyum install -y nfs-utils 複製程式碼完成以上步驟後,在 Kubernetes 叢集中任意節點執行 showmount -e 192.168.1.13 驗證是否正常:
如下配置中 volumes 指定了 nfs 欄位配置,即將 NFS Server 中的 /share 目錄掛載到容器內的 /app/Data/,與 hostPath Volume 型別的主要區別是依賴單獨的 NFS Server,和 node 本身並不耦合。
apiVersion: apps/v1kind: Deploymentmetadata: name: nfs-demospec: replicas: 2 selector: matchLabels: name: nfs-demo template: metadata: labels: name: nfs-demo spec: containers: - name: k8sdemo image: beckjin/k8sdemo:1.2.0 volumeMounts: - mountPath: /app/Data name: share ports: - containerPort: 80 volumes: - name: share nfs: server: 192.168.1.13 path: /share---apiVersion: v1kind: Servicemetadata: name: nfs-demo-servicespec: selector: name: nfs-demo type: NodePort ports: - port: 82 targetPort: 80複製程式碼
在 NFS Server 中執行 echo 'nfs Volume' > /share/data.txt,然後訪問 k8sdemo 服務的 GetVolumeData 介面獲取檔案內容,如下:
persistentVolumeClaimPersistentVolumeClaim(PVC) 與 PersistentVolume(PV) 在使用上是一對密不可分的組合,PV 主要是資源物件定義,PVC 主要是對應資源物件的引用,PV 支援 多種外掛型別 進行實現,以下將繼續使用 NFS 來作為 PV 外掛。
如下圖:首先基於 PV 外掛在 Kubernetes 叢集中建立各種資源規格的 PV,根據 Pod 需要儲存卷資源建立 PVC,Kubernetes 將符合資源規格要求且消耗資源最小的 PV 繫結到 PVC,PV 和 PVC 是一對一的關係,如果找不到符合條件的 PV,PVC 會一直處於未繫結狀態,PVC 繫結成功後可被 Pod 內的容器引用。
NFS Server 新增 mount 目錄
修改 NFS Server /etc/exports 並生效 ,在 Kubernetes 叢集中任意節點執行 showmount -e 192.168.1.13 驗證是否正常:
建立 PV以下配置將會建立3個 PV,storage 分別為 500M、1G、2G。
apiVersion: v1kind: PersistentVolumemetadata: name: pv-share-aspec: nfs: path: /share_a server: 192.168.1.13 accessModes: - ReadWriteMany capacity: storage: 500Mi---apiVersion: v1kind: PersistentVolumemetadata: name: pv-share-bspec: nfs: path: /share_b server: 192.168.1.13 accessModes: - ReadWriteMany capacity: storage: 1Gi---apiVersion: v1kind: PersistentVolumemetadata: name: pv-share-cspec: nfs: path: /share_c server: 192.168.1.13 accessModes: - ReadWriteMany capacity: storage: 2Gi複製程式碼
建立 PVC
apiVersion: v1kind: PersistentVolumeClaimmetadata: name: pvc-k8sdemospec: accessModes: - ReadWriteMany resources: requests: storage: 1Gi複製程式碼
PVC 建立成功後,pv-share-b 的 STATUS 會變為 Bound,同時 CLAIM 屬性會顯示相關的 PVC,從上圖也可以看出使用的是最小符合資源規格的 PV,並不會將 pv-share-c 繫結到當前 PVC。更多關於 PV 和 PVC 屬性說明可參考:persistent-volumes。
建立 Pod如下配置中 volumes 指定了 persistentVolumeClaim 欄位配置,這裡只需要設定 claimName 為前面建立的 PVC 名稱 pvc-k8sdemo 即可,使用上比較簡單。
apiVersion: apps/v1kind: Deploymentmetadata: name: pvc-demospec: replicas: 2 selector: matchLabels: name: pvc-demo template: metadata: labels: name: pvc-demo spec: containers: - name: k8sdemo image: beckjin/k8sdemo:1.2.0 volumeMounts: - mountPath: /app/Data name: share ports: - containerPort: 80 volumes: - name: share persistentVolumeClaim: claimName: pvc-k8sdemo---apiVersion: v1kind: Servicemetadata: name: pvc-demo-servicespec: selector: name: pvc-demo type: NodePort ports: - port: 83 targetPort: 80複製程式碼
在 NFS Server 中執行 echo 'pvc Volume share_a' > /share_a/data.txt,share_b、share_c 類似,然後訪問 k8sdemo 服務的 GetVolumeData 介面獲取檔案內容,如下:
configMapconfigMap 主要使映象和配置檔案解耦,以便實現映象的可移植性和可複用性,configMap 是配置資訊的集合,可直接注入到 Pod 的容器中使用,扮演著配置中心的角色。configMap 可以以資料卷的形式掛載,也可以基於環境變數的形式注入到 Pod 容器中使用。另外 secret 是一種相對安全的 configMap,它預設會將配置資訊進行 base64 編碼,使配置不是明文直接儲存在 configMap 中,起到一定的保護作用。
下面主要介紹 configMap 以資料卷掛載方式的使用,如下圖,在 Kubernetes 叢集中建立一個 configMap 資源型別,然後供 Pod 內的容器使用。
如下,建立一個數據卷形式的 ConfigMap,appsettings.json 是 .NET Core 程式內使用的配置檔案。
apiVersion: v1kind: ConfigMapmetadata: name: configmap-k8sdemodata: appsettings.json: |- { "ServiceName": "k8sdemo" }複製程式碼
如下配置中 volumes 指定了 configMap 資源的名稱為以上建立的 configMap 物件:configmap-k8sdemo。
apiVersion: apps/v1kind: Deploymentmetadata: name: configmap-demospec: replicas: 2 selector: matchLabels: name: configmap-demo template: metadata: labels: name: configmap-demo spec: containers: - name: k8sdemo image: beckjin/k8sdemo:1.2.0 volumeMounts: - name: configfile mountPath: /app/appsettings.json subPath: appsettings.json ports: - containerPort: 80 volumes: - name: configfile configMap: name: configmap-k8sdemo items: - key: appsettings.json path: appsettings.json---apiVersion: v1kind: Servicemetadata: name: configmap-demo-servicespec: selector: name: configmap-demo type: NodePort ports: - port: 84 targetPort: 80複製程式碼
通過訪問 k8sdemo 服務的 GetConfig 介面獲取指定 key 的值: