和 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 的文件內容;
emptyDir
emptyDir 的初始狀態是一個沒有任何內容的 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/v1
kind: Deployment
metadata:
name: emptydir-demo
spec:
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: v1
kind: Service
metadata:
name: emptydir-demo-service
spec:
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 接口獲取文件內容:
hostPath
hostPath 類型是掛載宿主機上的文件或目錄到 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: Deployment
metadata:
name: hostpath-demo
spec:
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: v1
kind: Service
metadata:
name: hostpath-demo-service
spec:
selector:
name: hostpath-demo
type: NodePort
ports:
- port: 81
targetPort: 80
訪問 k8sdemo 服務的 GetVolumeData 接口獲取文件內容,當路由到不同 Pod(即不同的 node) 返回內容將不一樣,如下:
nfs
NFS(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)
- 啓動 NFS
systemctl start rpcbind.service systemctl enable rpcbind.service systemctl start nfs.service systemctl enable nfs.service
- Kubernetes 集羣內各節點安裝 nfs-utils,方便使用 showmount
yum 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/v1
kind: Deployment
metadata:
name: nfs-demo
spec:
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: v1
kind: Service
metadata:
name: nfs-demo-service
spec:
selector:
name: nfs-demo
type: NodePort
ports:
- port: 82
targetPort: 80
在 NFS Server 中執行 echo 'nfs Volume' > /share/data.txt
,然後訪問 k8sdemo 服務的 GetVolumeData 接口獲取文件內容,如下:
persistentVolumeClaim
PersistentVolumeClaim(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: v1
kind: PersistentVolume
metadata:
name: pv-share-a
spec:
nfs:
path: /share_a
server: 192.168.1.13
accessModes:
- ReadWriteMany
capacity:
storage: 500Mi
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-share-b
spec:
nfs:
path: /share_b
server: 192.168.1.13
accessModes:
- ReadWriteMany
capacity:
storage: 1Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-share-c
spec:
nfs:
path: /share_c
server: 192.168.1.13
accessModes:
- ReadWriteMany
capacity:
storage: 2Gi
創建 PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-k8sdemo
spec:
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/v1
kind: Deployment
metadata:
name: pvc-demo
spec:
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: v1
kind: Service
metadata:
name: pvc-demo-service
spec:
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 接口獲取文件內容,如下:
configMap
configMap 主要使鏡像和配置文件解耦,以便實現鏡像的可移植性和可複用性,configMap 是配置信息的集合,可直接注入到 Pod 的容器中使用,扮演着配置中心的角色。configMap 可以以數據卷的形式掛載,也可以基於環境變量的形式注入到 Pod 容器中使用。另外 secret 是一種相對安全的 configMap,它默認會將配置信息進行 base64 編碼,使配置不是明文直接存儲在 configMap 中,起到一定的保護作用。
下面主要介紹 configMap 以數據卷掛載方式的使用,如下圖,在 Kubernetes 集羣中創建一個 configMap 資源類型,然後供 Pod 內的容器使用。
如下,創建一個數據卷形式的 ConfigMap,appsettings.json 是 .NET Core 程序內使用的配置文件。
apiVersion: v1
kind: ConfigMap
metadata:
name: configmap-k8sdemo
data:
appsettings.json: |-
{
"ServiceName": "k8sdemo"
}
如下配置中 volumes 指定了 configMap 資源的名稱爲以上創建的 configMap 對象:configmap-k8sdemo
。
apiVersion: apps/v1
kind: Deployment
metadata:
name: configmap-demo
spec:
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: v1
kind: Service
metadata:
name: configmap-demo-service
spec:
selector:
name: configmap-demo
type: NodePort
ports:
- port: 84
targetPort: 80
通過訪問 k8sdemo 服務的 GetConfig 接口獲取指定 key 的值: