09 . Kubernetes之pv、pvc及使用nfs網路儲存應用

men發表於2020-07-02

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內的不同容器之間共享工作過程產生的檔案,

apiVersion: v1  
kind: Pod  
metadata:  
  name: test-pd  
spec:  
  containers:  
  - image: gcr.io/google_containers/test-webserver  
    name: test-container  
    volumeMounts:   
    - mountPath: /cache  
      name: cache-volume  
  volumes:  
  - name: cache-volume  
    emptyDir: {}
mkdir /storage
cd /storage
mkdir volumes

cat pod.vol.demo.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: {}

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
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

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
Example3(NFS共享儲存)
# 找叢集外的一臺機器做NFS
hostnamectl  set-hostname stor01
echo 172.19.0.18    stor01 >> /etc/hosts
yum -y install nfs-utils
mkdir -pv /data/volumes
echo nfs >> /data/volumes/index.html 
vim  /etc/exports
/data/volumes     172.19.0.0/16(rw,no_root_squash)
systemctl  start nfs

# 切換到k8s叢集內的節點掛載儲存卷
yum -y install nfs-utils
echo 172.19.0.18    stor01 >> /etc/hosts
# 掛載測試以下,然後umount掉,待會建立Pod自動會掛載
mount -t nfs stor01:/data/volumes  /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

image.png

Pv和Pvc是K8s的一種標準資源,Pvc被Pv呼叫後就會被繫結起來,取決於使用者怎麼綁,

kubectl explain explain pvc.spec
FIELDS:
   accessModes	<[]string>
     AccessModes contains the desired access modes the volume should have. More
     info:
     https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1

   dataSource	<Object>
     This field requires the VolumeSnapshotDataSource alpha feature gate to be
     enabled and currently VolumeSnapshot is the only supported data source. If
     the provisioner can support VolumeSnapshot data source, it will create a
     new volume and data will be restored to the volume at the same time. If the
     provisioner does not support VolumeSnapshot data source, volume will not be
     created and the failure will be reported as an event. In the future, we
     plan to support more data source types and the behavior of the provisioner
     may change.

   resources	<Object>
     Resources represents the minimum resources the volume should have. More
     info:
     https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources

   selector	<Object>
     A label query over volumes to consider for binding.

   storageClassName	<string>
     Name of the StorageClass required by the claim. More info:
     https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1

   volumeMode	<string>
     volumeMode defines what type of volume is required by the claim. Value of
     Filesystem is implied when not included in claim spec. This is a beta
     feature.

   volumeName	<string>
     VolumeName is the binding reference to the PersistentVolume backing this
     claim.
Example4(建立多個Pvc)

因為pvc屬於叢集資源級別的不能定義在名成空間

mkdir v{1,2,3,4,5}
exportfs -arv
exporting 172.19.0.0/16:/data/volumes/v5
exporting 172.19.0.0/16:/data/volumes/v4
exporting 172.19.0.0/16:/data/volumes/v3
exporting 172.19.0.0/16:/data/volumes/v2
exporting 172.19.0.0/16:/data/volumes/v1

showmount -e
Export list for stor01:
/data/volumes/v5 172.19.0.0/16
/data/volumes/v4 172.19.0.0/16
/data/volumes/v3 172.19.0.0/16
/data/volumes/v2 172.19.0.0/16
/data/volumes/v1 172.19.0.0/16

切換到k8s控制節點

 cat pod-pv.yaml 
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv001
  labels:
    name: pv001
spec:
  nfs:
    path: /data/volumes/v1
    server: stor01
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 2Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv002
  labels:
    name: pv002
spec:
  nfs:
    path: /data/volumes/v2
    server: stor01
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 4Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv003
  labels:
    name: pv003
spec:
  nfs:
    path: /data/volumes/v3
    server: stor01
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 4Gi
kubectl apply -f pod-pvc.yaml

檢視Pv

kubectl get pv
NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
pv001   2Gi        RWO,RWX        Retain           Available                                   87s
pv002   4Gi        RWO,RWX        Retain           Available                                   87s
pv003   4Gi        RWO,RWX        Retain           Available                                   87s

建立pvc然後繫結pv

cat pod-vol-pvc.yaml 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mypvc
  namespace: default
spec:
  accessModes: ["ReadWriteMany"]
  resources:
    requests:
      storage: 2Gi
---
apiVersion: v1
kind: Pod 
metadata:
  name: pod-vol-pvc 
  namespace: default
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html/
  volumes:
  - name: html
    persistentVolumeClaim:
      claimName: mypvc 

kubectl get pv
NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM     STORAGECLASS   REASON   AGE
pv001   4Gi        RWO,RWX        Retain           Available                                    43s
pv002   4Gi        RWX            Retain           Bound       default/mypvc                  43s
pv003   4Gi        RWO,RWX        Retain           Available                                   43s

kubectl get pvc
NAME    STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mypvc   Bound    pv002    4Gi        RWX                           106s

pvc建立在pv之上,而且指定的空間大小不能超過pv大小,否則一直Pending掛起
NFS儲存的缺點

不支援動態建立持久卷,只能手工建立
先手工建立PV,再通過PV手工建立PVC,PVC就是真正可用的持久卷

PVC是和PV進行繫結的:
PVC會根據自己需求空間的大小自動選擇合適的PV,比如需要一個5G的PVC,PV分別為2G,7G和10G,那麼PVC會自動選擇7G的,但是剩餘的空間不是浪費了麼?原因如下:
一個被繫結的PV只能用於一個PVC,他們是一對一繫結的,如果PVC大小隻需要5G,但是所選的PV有7G,那麼剩餘的2G是沒辦法使用的,如果不想這樣浪費空間只能使用動態建立的方式.

相關文章