容器的生命週期可能很短,會被頻繁地創建和銷燬。那麼容器在銷燬時,保存在容器中的數據也會被清除。這種結果對用戶來說,在某些情況下是不樂意看到的。爲了持久化保存容器的數據,kubernetes引入了Volume的概念。
Volume是Pod中能夠被多個容器訪問的共享目錄,它被定義在Pod上,然後被一個Pod裏的多個容器掛載到具體的文件目錄下,kubernetes通過Volume實現同一個Pod中不同容器之間的數據共享
以及數據的持久化存儲。Volume的生命容器不與Pod中單個容器的生命週期相關,當容器終止或者重啓時,Volume中的數據也不會丟失。
kubernetes的Volume支持多種類型,比較常見的有下面幾個:
- 簡單存儲:EmptyDir、HostPath、NFS
- 高級存儲:PV、PVC
- 配置存儲:ConfigMap、Secret
基本存儲
EmptyDir
EmptyDir是最基礎的Volume類型,一個EmptyDir就是Host上的一個空目錄。
EmptyDir是在Pod被分配到Node時創建的,它的初始內容爲空,並且無須指定宿主機上對應的目錄文件,因爲kubernetes會自動分配一個目錄,當Pod銷燬時, EmptyDir中的數據也會被永久刪除。 EmptyDir用途如下:
- 臨時空間,例如用於某些應用程序運行時所需的臨時目錄,且無須永久保留
- 一個容器需要從另一個容器中獲取數據的目錄(多容器共享目錄)
接下來,通過一個容器之間文件共享的案例來使用一下EmptyDir。
在一個Pod中準備兩個容器nginx和busybox,然後聲明一個Volume分別掛在到兩個容器的目錄中,然後nginx容器負責向Volume中寫日誌,busybox中通過命令將日誌內容讀到控制檯。
創建一個volume-emptydir.yaml
apiVersion: v1
kind: Pod
metadata:
name: volume-emptydir
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
volumeMounts: # 將logs-volume掛在到nginx容器中,對應的目錄爲 /var/log/nginx
- name: logs-volume
mountPath: /var/log/nginx
- name: busybox
image: busybox:1.30
command: ["/bin/sh","-c","tail -f /logs/access.log"] # 初始命令,動態讀取指定文件中內容
volumeMounts: # 將logs-volume 掛在到busybox容器中,對應的目錄爲 /logs
- name: logs-volume
mountPath: /logs
volumes: # 聲明volume, name爲logs-volume,類型爲emptyDir
- name: logs-volume
emptyDir: {}
# 創建Pod
kubectl create -f volume-emptydir.yaml
# 查看pod
kubectl get pods volume-emptydir -n dev -o wide
# 通過podIp訪問nginx
curl 10.244.2.76
# 通過kubectl logs命令查看指定容器的標準輸出
kubectl logs -f volume-emptydir -n dev -c busybox
HostPath
上節課提到,EmptyDir中數據不會被持久化,它會隨着Pod的結束而銷燬,如果想簡單的將數據持久化到主機中,可以選擇HostPath。
HostPath就是將Node主機中一個實際目錄掛在到Pod中,以供容器使用,這樣的設計就可以保證Pod銷燬了,但是數據依據可以存在於Node主機上。
創建一個volume-hostpath.yaml:
apiVersion: v1
kind: Pod
metadata:
name: volume-hostpath
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
volumeMounts:
- name: logs-volume
mountPath: /var/log/nginx
- name: busybox
image: busybox:1.30
command: ["/bin/sh","-c","tail -f /logs/access.log"]
volumeMounts:
- name: logs-volume
mountPath: /logs
volumes:
- name: logs-volume
hostPath:
path: /root/logs
type: DirectoryOrCreate # 目錄存在就使用,不存在就先創建後使用
關於type的值的一點說明:
DirectoryOrCreate 目錄存在就使用,不存在就先創建後使用
Directory 目錄必須存在
FileOrCreate 文件存在就使用,不存在就先創建後使用
File 文件必須存在
Socket unix套接字必須存在
CharDevice 字符設備必須存在
BlockDevice 塊設備必須存在
# 創建Pod
kubectl create -f volume-hostpath.yaml
# 查看Pod
kubectl get pods volume-hostpath -n dev -o wide
#訪問nginx
curl 10.244.1.62
# 接下來就可以去host的/root/logs目錄下查看存儲的文件了
### 注意: 下面的操作需要到Pod所在的節點運行(案例中是node1)
ls /root/logs/
# 同樣的道理,如果在此目錄下創建一個文件,到容器中也是可以看到的
NFS
HostPath可以解決數據持久化的問題,但是一旦Node節點故障了,Pod如果轉移到了別的節點,又會出現問題了,此時需要準備單獨的網絡存儲系統,比較常用的用NFS、CIFS。
NFS是一個網絡文件存儲系統,可以搭建一臺NFS服務器,然後將Pod中的存儲直接連接到NFS系統上,這樣的話,無論Pod在節點上怎麼轉移,只要Node跟NFS的對接沒問題,數據就可以成功訪問。
1)首先要準備nfs的服務器,這裏爲了簡單,直接是master節點做nfs服務器
# 在nfs上安裝nfs服務
yum install nfs-utils -y
# 準備一個共享目錄
mkdir /root/data/nfs -pv
# 將共享目錄以讀寫權限暴露給192.168.176.0/24網段中的所有主機
# /root/data/nfs 192.168.176.0/24(rw,no_root_squash)
vi /etc/exports
cat /etc/exports
# 啓動nfs服務
systemctl restart nfs
2)接下來,要在的每個node節點上都安裝下nfs,這樣的目的是爲了node節點可以驅動nfs設備
# 在node上安裝nfs服務,注意不需要啓動
yum install nfs-utils -y
3)接下來,就可以編寫pod的配置文件了,創建volume-nfs.yaml
apiVersion: v1
kind: Pod
metadata:
name: volume-nfs
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
volumeMounts:
- name: logs-volume
mountPath: /var/log/nginx
- name: busybox
image: busybox:1.30
command: ["/bin/sh","-c","tail -f /logs/access.log"]
volumeMounts:
- name: logs-volume
mountPath: /logs
volumes:
- name: logs-volume
nfs:
server: 192.168.176.101 #nfs服務器地址
path: /root/data/nfs #共享文件路徑
4)最後,運行下pod,觀察結果
# 創建pod
kubectl create -f volume-nfs.yaml
# 查看pod
kubectl get pods volume-nfs -n dev
# 查看nfs服務器上的共享目錄,發現已經有文件了
ls /root/data/
高級存儲
前面已經學習了使用NFS提供存儲,此時就要求用戶會搭建NFS系統,並且會在yaml配置nfs。由於kubernetes支持的存儲系統有很多,要求客戶全都掌握,顯然不現實。爲了能夠屏蔽底層存儲實現的細節,方便用戶使用, kubernetes引入PV和PVC兩種資源對象。
PV(Persistent Volume)是持久化卷的意思,是對底層的共享存儲的一種抽象。一般情況下PV由kubernetes管理員進行創建和配置,它與底層具體的共享存儲技術有關,並通過插件完成與共享存儲的對接。
PVC(Persistent Volume Claim)是持久卷聲明的意思,是用戶對於存儲需求的一種聲明。換句話說,PVC其實就是用戶向kubernetes系統發出的一種資源需求申請。
使用了PV和PVC之後,工作可以得到進一步的細分:
- 存儲:存儲工程師維護
- PV: kubernetes管理員維護
- PVC:kubernetes用戶維護
PV
PV是存儲資源的抽象,下面是資源清單文件:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv2
spec:
nfs: # 存儲類型,與底層真正存儲對應
capacity: # 存儲能力,目前只支持存儲空間的設置
storage: 2Gi
accessModes: # 訪問模式
storageClassName: # 存儲類別
persistentVolumeReclaimPolicy: # 回收策略
PV 的關鍵配置參數說明:
-
存儲類型
底層實際存儲的類型,kubernetes支持多種存儲類型,每種存儲類型的配置都有所差異
-
存儲能力(capacity)
目前只支持存儲空間的設置( storage=1Gi ),不過未來可能會加入IOPS、吞吐量等指標的配置
-
訪問模式(accessModes)
用於描述用戶應用對存儲資源的訪問權限,訪問權限包括下面幾種方式:
- ReadWriteOnce(RWO):讀寫權限,但是隻能被單個節點掛載
- ReadOnlyMany(ROX): 只讀權限,可以被多個節點掛載
- ReadWriteMany(RWX):讀寫權限,可以被多個節點掛載
需要注意的是,底層不同的存儲類型可能支持的訪問模式不同
-
回收策略(persistentVolumeReclaimPolicy)
當PV不再被使用了之後,對其的處理方式。目前支持三種策略:
- Retain (保留) 保留數據,需要管理員手工清理數據
- Recycle(回收) 清除 PV 中的數據,效果相當於執行 rm -rf /thevolume/*
- Delete (刪除) 與 PV 相連的後端存儲完成 volume 的刪除操作,當然這常見於雲服務商的存儲服務
需要注意的是,底層不同的存儲類型可能支持的回收策略不同
-
存儲類別
PV可以通過storageClassName參數指定一個存儲類別
- 具有特定類別的PV只能與請求了該類別的PVC進行綁定
- 未設定類別的PV則只能與不請求任何類別的PVC進行綁定
-
狀態(status)
一個 PV 的生命週期中,可能會處於4中不同的階段:
- Available(可用): 表示可用狀態,還未被任何 PVC 綁定
- Bound(已綁定): 表示 PV 已經被 PVC 綁定
- Released(已釋放): 表示 PVC 被刪除,但是資源還未被集羣重新聲明
- Failed(失敗): 表示該 PV 的自動回收失敗
實驗
使用NFS作爲存儲,來演示PV的使用,創建3個PV,對應NFS中的3個暴露的路徑。
- 準備NFS環境
# 創建目錄
mkdir /root/data/{pv1,pv2,pv3} -pv
# 暴露服務
vi /etc/exports
/root/data/pv1 192.168.176.0/24(rw,no_root_squash)
/root/data/pv2 192.168.176.0/24(rw,no_root_squash)
/root/data/pv3 192.168.176.0/24(rw,no_root_squash)
cat /etc/exports
# 重啓服務
systemctl restart nfs
- 創建pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv1
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
path: /root/data/pv1
server: 192.168.176.100
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv2
spec:
capacity:
storage: 2Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
path: /root/data/pv2
server: 192.168.176.100
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv3
spec:
capacity:
storage: 3Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
path: /root/data/pv3
server: 192.168.176.100
# 創建 pv
kubectl create -f pv.yaml
# 查看pv
kubectl get pv -o wide
PVC
PVC是資源的申請,用來聲明對存儲空間、訪問模式、存儲類別需求信息。下面是資源清單文件:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc
namespace: dev
spec:
accessModes: # 訪問模式
selector: # 採用標籤對PV選擇
storageClassName: # 存儲類別
resources: # 請求空間
requests:
storage: 5Gi
PVC 的關鍵配置參數說明:
- 訪問模式(accessModes)
用於描述用戶應用對存儲資源的訪問權限
-
選擇條件(selector)
通過Label Selector的設置,可使PVC對於系統中己存在的PV進行篩選
-
存儲類別(storageClassName)
PVC在定義時可以設定需要的後端存儲的類別,只有設置了該class的pv才能被系統選出
-
資源請求(Resources )
描述對存儲資源的請求
實驗
- 創建pvc.yaml,申請pv
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc1
namespace: dev
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc2
namespace: dev
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc3
namespace: dev
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
# 創建pvc
kubectl create -f pvc.yaml
# 查看pvc
kubectl get pvc -n dev -o wide
# 查看pv
kubectl get pv -o wide
- 創建pods.yaml, 使用pv
apiVersion: v1
kind: Pod
metadata:
name: pod1
namespace: dev
spec:
containers:
- name: busybox
image: busybox:1.30
command: ["/bin/sh","-c","while true;do echo pod1 >> /root/out.txt; sleep 10; done;"]
volumeMounts:
- name: volume
mountPath: /root/
volumes:
- name: volume
persistentVolumeClaim:
claimName: pvc1
readOnly: false
---
apiVersion: v1
kind: Pod
metadata:
name: pod2
namespace: dev
spec:
containers:
- name: busybox
image: busybox:1.30
command: ["/bin/sh","-c","while true;do echo pod2 >> /root/out.txt; sleep 10; done;"]
volumeMounts:
- name: volume
mountPath: /root/
volumes:
- name: volume
persistentVolumeClaim:
claimName: pvc2
readOnly: false
# 創建pod
kubectl create -f pods.yaml
# 查看pod
kubectl get pods -n dev -o wide
# 查看pvc
kubectl get pvc -n dev -o wide
# 查看pv
kubectl get pv -n dev -o wide
# 查看nfs中的文件存儲
more /root/data/pv1/out.txt
more /root/data/pv2/out.txt
生命週期
PVC和PV是一一對應的,PV和PVC之間的相互作用遵循以下生命週期:
-
資源供應:管理員手動創建底層存儲和PV
-
資源綁定:用戶創建PVC,kubernetes負責根據PVC的聲明去尋找PV,並綁定
在用戶定義好PVC之後,系統將根據PVC對存儲資源的請求在已存在的PV中選擇一個滿足條件的
- 一旦找到,就將該PV與用戶定義的PVC進行綁定,用戶的應用就可以使用這個PVC了
- 如果找不到,PVC則會無限期處於Pending狀態,直到等到系統管理員創建了一個符合其要求的PV
PV一旦綁定到某個PVC上,就會被這個PVC獨佔,不能再與其他PVC進行綁定了
-
資源使用:用戶可在pod中像volume一樣使用pvc
Pod使用Volume的定義,將PVC掛載到容器內的某個路徑進行使用。
-
資源釋放:用戶刪除pvc來釋放pv
當存儲資源使用完畢後,用戶可以刪除PVC,與該PVC綁定的PV將會被標記爲“已釋放”,但還不能立刻與其他PVC進行綁定。通過之前PVC寫入的數據可能還被留在存儲設備上,只有在清除之後該PV才能再次使用。
-
資源回收:kubernetes根據pv設置的回收策略進行資源的回收
對於PV,管理員可以設定回收策略,用於設置與之綁定的PVC釋放資源之後如何處理遺留數據的問題。只有PV的存儲空間完成回收,才能供新的PVC綁定和使用
配置存儲
ConfigMap
ConfigMap是一種比較特殊的存儲卷,它的主要作用是用來存儲配置信息的。
創建configmap.yaml,內容如下:
apiVersion: v1
kind: ConfigMap
metadata:
name: configmap
namespace: dev
data:
info: |
username:admin
password:123456
接下來,使用此配置文件創建configmap
# 創建configmap
kubectl create -f configmap.yaml
# 查看configmap詳情
kubectl describe cm configmap -n dev
接下來創建一個pod-configmap.yaml,將上面創建的configmap掛載進去
apiVersion: v1
kind: Pod
metadata:
name: pod-configmap
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
volumeMounts: # 將configmap掛載到目錄
- name: config
mountPath: /configmap/config
volumes: # 引用configmap
- name: config
configMap:
name: configmap
# 創建pod
kubectl create -f pod-configmap.yaml
# 查看pod
kubectl get pod pod-configmap -n dev
#進入容器
kubectl exec -it pod-configmap -n dev /bin/sh
# cat /configmap/config/info
# 可以看到映射已經成功,每個configmap都映射成了一個目錄
# key--->文件 value---->文件中的內容
# 此時如果更新configmap的內容, 容器中的值也會動態更新
kubectl edit cm configmap -n dev
# 將password改爲123456789
# 再次查看
# cat /configmap/config/info
Secret
在kubernetes中,還存在一種和ConfigMap非常類似的對象,稱爲Secret對象。它主要用於存儲敏感信息,例如密碼、祕鑰、證書等等。
- 首先使用base64對數據進行編碼
echo -n 'admin' | base64 #準備username
echo -n '123456' | base64 #準備password
- 接下來編寫secret.yaml,並創建Secret
apiVersion: v1
kind: Secret
metadata:
name: secret
namespace: dev
type: Opaque
data:
username: YWRtaW4=
password: MTIzNDU2
# 創建secret
kubectl create -f secret.yaml
# 查看secret詳情
kubectl describe secret secret -n dev
- 創建pod-secret.yaml,將上面創建的secret掛載進去:
apiVersion: v1
kind: Pod
metadata:
name: pod-secret
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
volumeMounts: # 將secret掛載到目錄
- name: config
mountPath: /secret/config
volumes:
- name: config
secret:
secretName: secret
# 創建pod
kubectl create -f pod-secret.yaml
# 查看pod
kubectl get pod pod-secret -n dev
# 進入容器,查看secret信息,發現已經自動解碼了
kubectl exec -it pod-secret /bin/sh -n dev
/ # ls /secret/config/
/ # more /secret/config/username
/ # more /secret/config/password
至此,已經實現了利用secret實現了信息的編碼。