PV,PVC概述
PV的全稱是: PersistentVolume (持久化卷),是對底層的共享存儲的一種抽象,PV由管理員進行創建和配置,它和具體的底層的共享存儲技術的實現方式有關,比如Ceph、GlusterFS、NFS等,都是通過插件機制完成與共享存儲的對接.
PVC的全稱是: PersistenVolumeClaim (持久化卷聲明),PVC是用戶存儲的一種聲明,PVC和Pod比較類型,Pod是消耗節點,PVC消耗的是PV資源,Pod可以請求CPU的內存,而PVC可以請求特定的存儲空間和訪問模式。對於真正存儲的用戶不需要關心底層的存儲實現細節,只需要直接使用PVC即可.
但是通過PVC請求一定的存儲空間也很有可能不足以滿足對於存儲設備的各種需求,而且不同的應用程序對於存儲性能的要求也能也不盡相同,比如讀寫速度、並發性能等,為了解決這一問題,Kubernetes又為我們引入了一個新的資源對象: StorageClass,通過StorageClass的定義,管理員可以將存儲資源定義為某種類型的資源,比如快速存儲、慢速存儲等,用戶根據StorageClass的描述就可以非常直觀的知道各種存儲資源特性了,這樣就可以根據應用的特性去申請合適的存儲資源了.
PV和PVC生命周期
PV可以看作可用的存儲資源,PVC則是對存儲資源的需求,PV和PVC的互相關系遵循如下圖
資源供應
Kubernetes支持兩種資源的供應模式:靜態模式(Staic)和動態模式(Dynamic)。資源供應的結果就是創建好的PV.
靜態模式: 管理員手工創建許多PV,在定義PV時需要將后端存儲的特性進行設置
動態模式: 管理員無需手動創建PV,而是通過StorageClass的設置對后端存儲進行描述,標記為某種"類型(Class)",此時要求PVC對存儲的類型進行聲明,系統將自動完成PV的創建及PVC的綁定,PVC可以聲明為Class為"",說明該PVC禁止使用動態模式
資源綁定(Binding)
在用戶定義好PVC后,系統將根據PVC對存儲資源的請求(存儲空間和訪問模式)在已存在的PV中選擇一個滿足PVC要求的PV,一旦找到,就將PV與用戶定義的PVC進行綁定,然后用戶的應用就可以使用這個PVC了。如果系統中沒有滿足PVC要求的PV,PVC則會無限期處於Pending狀態,直到等到系統管理員創建了一個符合要求的PV。PV一旦綁定在某個PVC上,就被這個PVC獨占,不能再與其他PVC進行綁定了。在這種情況下,當PVC申請的存儲空間比PV的少時,整個PV的空間都能夠為PVC所用,可能會造成資源的浪費。如果資源供應使用的是動態模式,則系統在PVC找到合適的StorageClass后,將會自動創建PV並完成PVC的綁定
資源使用(Using)
Pod 使用volume的定義,將PVC掛載到容器內的某個路徑進行使用。volume的類型為persistentVoulumeClaim,在容器應用掛載了一個PVC后,就能被持續獨占使用。不過,多個Pod可以掛載同一個PVC,應用程序需要考慮多個實例共同訪問一塊存儲空間的問題
資源釋放(Releasing)
當用戶對存儲資源使用哪個完畢后,用戶可以刪除PVC,與該PVC綁定的PV將會被標記為已釋放,但還不能立刻與其他PVC進行綁定。通過之前PVC寫入的數據可能還留在存儲設備上,只有在清除之后該PV才能繼續使用.
資源回收(Reclaiming)
對於PV,管理員可以設定回收策略(Reclaim Policy)用於設置與之綁定的PVC釋放資源之后,對於遺留數據如何處理。只有PV的存儲空間完成回收,才能供新的PVC綁定和使用。
1 . 靜態資源下,通過PV和PVC完成綁定,並供Pod使用的存儲管理機制
2 . 動態資源下,通過StorageClass和PVC完成資源動態綁定(系統自動生成PV,並供Pod使用的存儲管理機制
存儲卷概述
由於容器本身是非持久化的,因此需要解決在容器中運行應用程序遇到的一些問題。首先,當容器崩潰時,kubelet將重新啟動容器,但是寫入容器的文件將會丟失,容器將會以鏡像的初始狀態重新開始;第二,在通過一個Pod中一起運行的容器,通常需要共享容器之間一些文件。Kubernetes通過存儲卷解決上述的兩個問題。
在Docker有存儲卷的概念卷,但Docker中存儲卷只是磁盤的或另一個容器中的目錄,並沒有對其生命周期進行管理。Kubernetes的存儲卷有自己的生命周期,它的生命周期與使用的它Pod生命周期一致。因此,相比於在Pod中運行的容器來說,存儲卷的存在時間會比的其中的任何容器都長,並且在容器重新啟動時會保留數據。當然,當Pod停止存在時,存儲卷也將不再存在。在Kubernetes支持多種類型的卷,而Pod可以同時使用各種類型和任意數量的存儲卷。在Pod中通過指定下面的字段來使用存儲卷:
spec.volumes: 通過此字段提供指定的存儲卷
spec.containers.volumeMounts: 通過此字段將存儲卷掛載到容器中
存儲卷類型和示例
當前Kubernetes支持如下所列的存儲卷類型,並以hostPath、nfs和persistentVolumeClaim類型的存儲卷為例,介紹如何定義存儲卷,以及如何在Pod中被使用.
* awsElasticBlockStore
* azureDisk
* azureFile
* cephfs
* configMap
* csi
* downwardAPI
* emptyDir
* fc (fibre channel)
* flocker
* gcePersistentDisk
* gitRepo
* glusterfs
* hostPath
* iscsi
* local
* nfs
* persistentVolumeClaim
* projected
* portworxVolume
* quobyte
* rbd
* scaleIO
* secret
* storageos
* vsphereVolume
xample1 EmptyDir(兩個Pod目錄互相掛載)
EmptyDir是一個空目錄,他的生命周期和所屬的Pod是完全一致的,他用處是把同一Pod內的不同容器之間共享工作過程產生的文件,
mkdir /storage
cd /storage
mkdir volumes
cat pod_volume.demo1.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-demo
namespace: default
labels:
app: myapp
tier: frontend
annotations:
youmen.com/created-by: "youmen admin"
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html/
- name: busybox
image: busybox:latest
imagePullPolicy: IfNotPresent
volumeMounts:
- name: html
mountPath: /data/
command: ['/bin/sh', '-c']
args:
- 'while true; do echo $(date) >> /data/index.html; sleep 3; done'
volumes:
- name: html
emptyDir: {}
kubectl apply -f pod_volume.demo1.yaml
curl 10.244.3.34 -s
Tue Dec 24 15:37:09 UTC 2019
Tue Dec 24 15:37:12 UTC 2019
Tue Dec 24 15:37:15 UTC 2019
Tue Dec 24 15:37:18 UTC 2019
Tue Dec 24 15:37:21 UTC 2019
Tue Dec 24 15:37:24 UTC 2019
Example2 HostPath(主機目錄掛載)
依賴於node,這種會把宿主機的指定卷加載到容器之中,實現數據持久,但是如果Pod發生跨主機的重建,內容很難保證,或者Node節點宕機了
這種卷一般和DaemonSet搭配使用,用來操作主機文件,例如進行日志采集的FLK的FluentD就采用這種方式,加載主機的容器日志目錄,達到收集本機所有日志的目的
# hostPath
# 將Pod里面的目錄內容綁定到宿主機目錄,Pod刪除並不到導致宿主機刪除.
# 如果指定宿主機目錄不存在要不要先創建取決於type
mkdir /data/pod/volume1 -p
cat pod-volhost.demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-vol-hostpath
namespace: default
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html/
volumes:
- name: html
hostPath:
path: /data/pod/volume1
type: DirectoryOrCreate
kubectl apply -f pod-volhost-demo.yaml
echo youmen >> /data/pod/volume1/index.html
kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-vol-hostpath 1/1 Running 0 3m49s 10.244.3.35 node1 <none> <none>
curl 10.244.3.35
youmen
# 我們刪除Pod,可以到Node主機查看目錄,依然存在.
[root@node1 ~]# cat /data/pod/volume1/index.html
youmen
NFS網絡共享存儲
部署nfs
# 建議找集群外的一台機器做NFS
hostnamectl set-hostname stor01
echo 172.19.0.18 stor01 >> /etc/hosts
yum -y install nfs-utils
mkdir -pv /data/volumes -p
chmod 755 /data/volumes
vim /etc/exports
/data/volumes *(rw,no_root_squash,sync)
# 存儲目錄,*允許所有人連接,rw讀寫權限,sync文件同時寫入硬盤及內存,no_root_squash 使用者root用戶自動修改為普通用戶
systemctl start nfs && systemctl enable nfs
驗證nfs服務可用性
# 查看nfs目錄掛載權限
cat /var/lib/nfs/etab
/data/volumes *(rw,sync,wdelay,hide,nocrossmnt,secure,no_root_squash,no_all_squash,no_subtree_check,secure_locks,acl,no_pnfs,anonuid=65534,anongid=65534,sec=sys,rw,secure,no_root_squash,no_all_squash)
# 檢查下掛載目錄是否正常
showmount -e localhost
Export list for localhost:
/data/volumes *
掛載存儲卷
# 切換到k8s集群內的節點掛載存儲卷
yum -y install nfs-utils
echo 172.19.0.18 stor01 >> /etc/hosts
# 掛載測試以下,然后umount掉,待會創建Pod自動會掛載
mount -t nfs stor01:/data/volumes /mnt
# 驗證一下
df -h |grep nfs1
nfs1:/data/volumes 17G 1.4G 16G 9% /mnt
cat pod-volnfs.demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-volnfs-demo
namespace: default
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html/
volumes:
- name: html
nfs:
path: /data/volumes
server: stor01
kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-volnfs-demo 1/1 Running 0 20s 10.244.1.49 node2 <none> <none>
curl 10.244.1.49
nfs
# 可以刪除Pod再創建數據依然存在,哪怕節點宕掉依然數據不會丟失,但是當掛載的數據,Pod多了,效果就不盡人意了
# 接下來我們通過pv,pvc來使用這個nfs存儲
使用pv,pvc作為存儲
Pv和Pvc是K8s的一種標准資源,Pvc被Pv調用后就會被綁定起來,取決於用戶怎么綁,
因為pvc屬於集群資源級別的不能定義在名成空間
創建pv
# 這里使用nfs類型后端存儲, 1g存儲空間,訪問模式為ReadWriteOnce,回收策略為Recyle
cat nfs_demo1.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv1 #pv名稱
spec:
capacity: #存儲能力,一個pv對象都要指定一個存儲能力,
目前僅支持存儲空間的設置
storage: 1Gi #存儲空間
accessModes:
- ReadWriteOnce #訪問模式
persistentVolumeReclaimPolicy: Recycle #回收策略
nfs: #服務模式 (nfs、ceph、hostpath等)
path: /data/volumes #共享數據目錄掛載點
server: nfs1 #nfs服務器地址
kubectl apply -f nfs_demo1.yaml
persistentvolume/pv1 created
[root@master storage]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv1 1Gi RWO Recycle Available 5s
# pv1: 名稱
# 1Gi: 存儲空間大小
# RWO: 訪問模式(ReadWriteOnce縮寫)
# Recycle: 回收策略
# Available: PV狀態
# persistentVolumeReclaimPolicy回收策略
# Retain (保留) 保留數據,需要管理員手動清理
# Recycle (回收) 清除PV中的數據,效果相當於執行刪除命令
# Delete (刪除) 與PV相連的后端存儲完成volume的刪除操作,常見於雲服務商的存儲服務
# 不過需要注意的是,目前只有NFS和HostPath兩類支持回收策略,一般設置Retain比較保險
# 狀態有以下幾種
# Available: PV狀態,表示可用狀態,還未被任何PVC綁定
# Bound: 已綁定,已經綁定到某個PVC
# Released: 已釋放,對應的pvc已經刪除,但資源還沒有被集群收回
# Failed: PV自動回收失敗
pv相關配置說明
Capacity 存儲能力
通過PV的capacity屬性來設置存儲空間,目前僅支持storage=數據大小,未來可能會加入IOPS、吞吐量等指標配置
AccessModes訪問模式
AccessModes 是用來對PV進行訪問模式的設置,用於描述用戶應用對存儲資源的訪問權限
ReadWriteOnce (RWO):讀寫權限,但是只能被單個節點掛載
ReadOnlyMany (ROX):只讀權限,可能被多個節點掛載
ReadWriteMany (RWX):讀寫權限,可以被多個節點掛載
注意: 一些pv可能支持多種訪問模式,但掛載時候只可以使用一種訪問模式,多種訪問模式不奏效
PVC
PV實際上沒有存儲,相當於我們node一樣,還需要創建Pod進行消費,接下來我們進行PVC的創建與配置
# 創建一個數據卷聲明
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-nfs
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
kubectl apply -f pvc-nfs-demo1.yaml
kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
pvc-nfs Bound pv1 1Gi RWO
kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv1 1Gi RWO Recycle Bound default/pvc-nfs 10m
# 當我們創建pvc之后,pv的狀態變成了Bound綁定狀態,並且和pvc的狀態相同。
# 並且可以看到pvc已經綁定到名稱為pv1的volume上,
# 同時在pv上可以看到綁定到名稱為pvc-nfs的pvc中
查看pv,pvc
[root@master storage]# kubectl describe pv pv1
Name: pv1
Labels: <none>
Annotations: pv.kubernetes.io/bound-by-controller: yes
Finalizers: [kubernetes.io/pv-protection]
StorageClass:
Status: Bound
Claim: default/pvc-nfs
Reclaim Policy: Recycle
Access Modes: RWO
VolumeMode: Filesystem
Capacity: 1Gi
Node Affinity: <none>
Message:
Source:
Type: NFS (an NFS mount that lasts the lifetime of a pod)
Server: nfs1
Path: /data/volumes
ReadOnly: false
Events: <none>
[root@master storage]# kubectl describe pvc pvc-nfs
Name: pvc-nfs
Namespace: default
StorageClass:
Status: Bound
Volume: pv1
Labels: <none>
Annotations: pv.kubernetes.io/bind-completed: yes
pv.kubernetes.io/bound-by-controller: yes
Finalizers: [kubernetes.io/pvc-protection]
Capacity: 1Gi
Access Modes: RWO
VolumeMode: Filesystem
Mounted By: <none>
Events: <none>
# 在Kubernetes中會自動幫我們查看pv狀態為Available並且根據聲明pvc容量storage的大小進行篩選匹配,
# 同時還會根據AccessMode進行匹配。如果pvc匹配不到pv會一直處於pending狀態。
根據Labels匹配PV與PVC
同時,pv與pvc中間還可以通過label標簽進行匹配,配置如下
# 記得下面配置文件修改一下名字,不可以重復
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv2
labels: #這里將pv設置一個labels
app: nfs
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
nfs:
path: /data/volumes
server: nfs1
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc2-nfs
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
selector: # pvc匹配標簽為app=nfs的pv
matchLabels:
app: nfs
[root@master storage]# kubectl apply -f nfs_demo2.yaml
[root@master storage]# kubectl get pv,pvc
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/pv1 1Gi RWO Recycle Bound default/pvc-nfs 28m
persistentvolume/pv2 1Gi RWO Recycle Bound default/pvc2-nfs 94s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/pvc-nfs Bound pv1 1Gi RWO 17m
persistentvolumeclaim/pvc2-nfs Bound pv2 1Gi RWO 94s
# 有一點需要注意,當我們pvc申請的容量小於我們pv的容量是可以進行綁定的
# 當我們申請pvc的容量大於pv的容量是無法進行綁定的。 這里需要注意
Deployment引用pvc
[root@master storage]# cat deployment-nfs1.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: pv-nfs-nginx
spec:
replicas: 3
selector:
matchLabels:
app: pv-nfs-nginx
template:
metadata:
labels:
app: pv-nfs-nginx
spec:
containers:
- name: pv-nfs-nginx
image: nginx
ports:
- containerPort: 80
volumeMounts: #掛載,首先添加需要掛載的目錄
- name: pv-nginx #掛載點的名稱
mountPath: /usr/share/nginx/html #掛載點的路徑
volumes: #綁定
- name: pv-nginx
persistentVolumeClaim: #將鏡像中的nginx目錄掛載到下面名稱的pvc中
claimName: pvc-nfs #pvc名稱
---
apiVersion: v1
kind: Service
metadata:
name: nfs-pvc
labels:
app: pv-nfs-nginx
spec:
type: NodePort
ports:
- port: 80
targetPort: 80
selector:
app: pv-nfs-nginx
[root@master storage]# kubectl apply -f deployment-nfs1.yaml
檢查Pod和svc狀態
[root@master storage]# kubectl get pods,svc |grep pv
pod/pv-nfs-nginx-6b4759b5b8-95vhk 1/1 Running 0 74s
pod/pv-nfs-nginx-6b4759b5b8-mjwhn 1/1 Running 0 58s
pod/pv-nfs-nginx-6b4759b5b8-p4jhn 1/1 Running 0 11s
service/nfs-pvc NodePort 10.0.0.57 <none> 80:31850/TCP 4m23s
# 這里我們可以看到pod已經正常啟動,並且svc也已經暴露端口了。
# 如果需要更改顯示內容,更改之前nfs存儲里面的那個目錄文件就可以了
由於我們的index.html直接掛在到了/data1/k8s目錄下面,如果有很多個pod都使用pvc進行掛載,會造成我們數據目錄的文件比較亂
這里我們添加一個
subpath
subPath的目的是為了在單一Pod中多次使用同一個volume而設計的。
[root@master storage]# cat deployment-nfs1.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: pv-nfs-nginx
spec:
replicas: 3
selector:
matchLabels:
app: pv-nfs-nginx
template:
metadata:
labels:
app: pv-nfs-nginx
spec:
containers:
- name: pv-nfs-nginx
image: nginx
ports:
- containerPort: 80
volumeMounts: #掛載,首先添加需要掛載的目錄
- name: pv-nginx #掛載點的名稱
mountPath: /usr/share/nginx/html #掛載點的路徑
subPath: nginx-pvc
volumes: #綁定
- name: pv-nginx
persistentVolumeClaim: #將鏡像中的nginx目錄掛載到下面名稱的pvc中
claimName: pvc-nfs #pvc名稱
---
apiVersion: v1
kind: Service
metadata:
name: nfs-pvc
labels:
app: pv-nfs-nginx
spec:
type: NodePort
ports:
- port: 80
targetPort: 80
selector:
app: pv-nfs-nginx
[root@master storage]# kubectl apply -f deployment-nfs1.yaml
# 當我們更新完pod之后,等pod正常啟動。
# 就可以看到在我們nfs存儲目錄下面單獨創建了一個名稱為nginx-pvc的目錄,
# 這個目錄實際上就是我們subpath后面指定的名稱
[root@nfs1 ~]# ls /data/volumes/
index.html nginx-pvc
[root@nfs1 ~]# ls /data/volumes/nginx-pvc/
[root@nfs1 ~]# mv /data/volumes/index.html /data/volumes/nginx-pvc/
# 這個目錄下面也是沒有任何文件的,我們需要將原來index.html拷貝過去即可
# 現在我們刪除deployment,下面的數據並不會刪除。這樣使用pv和pvc持久化就完成
# 如果我們直接刪除或者有pod在使用pv或者pvc是無法直接刪除的,
# 當我們使用Recycle模式時,刪除所有pv和pvc后,
# 數據也會進行刪除。所以刪除pv和pvc請謹慎操作
[root@nfs1 ~]# cat /data/volumes/nginx-pvc/index.html
Pvc Persistence
NFS存儲的缺點
不支持動態創建持久卷,只能手工創建
先手工創建PV,再通過PV手工創建PVC,PVC就是真正可用的持久卷PVC是和PV進行綁定的:
PVC會根據自己需求空間的大小自動選擇合適的PV,比如需要一個5G的PVC,PV分別為2G,7G和10G,那么PVC會自動選擇7G的,但是剩余的空間不是浪費了么?原因如下:
一個被綁定的PV只能用於一個PVC,他們是一對一綁定的,如果PVC大小只需要5G,但是所選的PV有7G,那么剩余的2G是沒辦法使用的,如果不想這樣浪費空間只能使用動態創建的方式.