Kubernetes學習筆記(五):卷

簡介

卷是Pod的一部分,與Pod共享生命周期。它不是獨立的Kubernetes對象,因此不能單獨創建。

卷提供的存儲功能不但可以解決容器重啟後數據丟失的問題,還可以使數據在容器間共享。

一些卷的類型:

  • emptyDir:用於存儲臨時數據的空目錄
  • hostPath:用於將目錄從工作節點掛載到pod
  • gitRepo:通過檢出Git倉庫的內容來初始化的卷
  • nfs:掛載到pod中的nfs共享卷
  • configMap、secret、downwardAPI:用於將Kubernetes部分資源和集群資訊公開給pod的特殊類型的卷
  • persistentVolumeClaim:一種使用預置或者動態配置的持久存儲類型

單個容器可以同時使用不同類型的多個卷。

emptyDir

emptyDir卷對於在同一個pod中運行的容器之間共享文件特別有用。但也可以被單個容器用於將數據臨時寫入磁碟。

下面的例子中,Pod包含兩個容器,這個兩個容器分別將卷html掛載到容器內的不同路徑,實現了文件共享。html-generator每隔一秒寫入當前時間到index.html,web-server提供web服務使index.html可以被訪問。

# volume-share-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: volume-share
spec:
  containers:
    - image: alpine		# 容器鏡像一
      name: html-generator
      volumeMounts:		# 將名為html的卷掛載到容器的/var/html
      - name: html
        mountPath: /var/html
      command: ["sh","-c","mkdir /var/html; while :; do echo $(date) > /var/html/index.html;sleep 1;done"]
    - image: nginx:alpine	#容器鏡像二
      name: web-server
      volumeMounts:		# 與上面相同的卷卷掛載到容器的/usr/share/nginx/html
      - name: html
        mountPath: /usr/share/nginx/html
        readOnly: true
      ports:
      - containerPort: 80
  volumes:			# 創建一個名為html的卷
  - name: html
    emptyDir: {}

創建pod,設置埠轉發

-> [[email protected]] [~] k create -f volume-share-pod.yaml
pod/volume-share created

-> [[email protected]] [~] k port-forward volume-share 80:80
Forwarding from 127.0.0.1:80 -> 80
Forwarding from [::1]:80 -> 80

發出請求

-> [[email protected]] [~] curl //localhost
Sun May 24 01:14:48 UTC 2020
-> [[email protected]] [~] curl //localhost
Sun May 24 01:14:49 UTC 2020

如果進行下面的改動,emptyDir的內容會存在記憶體中

  volumes:
  - name: html
    emptyDir: 
      medium: Memory

gitRepo

gitRepo卷基本上也是一個emptyDir卷,它在容器啟動前從git倉庫檢出填充數據。

當git倉庫內容發生改變時,對已存在的Pod內的gitRepo卷是不可見的。但啟動新的Pod時會檢出最新的。

下面的例子中,該pod創建了一個名為html的gitRepo卷的,創建一個提供web服務的容器web-server,並將卷html掛載到web-server的/usr/share/nginx/html。

# volume-gitrepo-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: volume-gitrepo
spec:
  containers:
    - image: nginx:alpine
      name: web-server
      volumeMounts:
      - name: html
        mountPath: /usr/share/nginx/html
        readOnly: true
      ports:
      - containerPort: 80
  volumes:		# 創建一個名為html的gitRepo卷
  - name: html
    gitRepo:
      repository: //github.com/orccn/kube-dockerfile.git	# 倉庫地址
      revision: master	# 分支
      directory: .	# 檢出到卷的根目錄

創建pod,設置埠轉發

-> [[email protected]] [~] k create -f volume-gitrepo-pod.yaml
pod/volume-gitrepo created

-> [[email protected]] [~] k port-forward volume-gitrepo 80:80
Forwarding from 127.0.0.1:80 -> 80
Forwarding from [::1]:80 -> 80

發出請求

-> [[email protected]] [~] curl //localhost/etcd/Dockerfile
FROM k8s.gcr.io/etcd:3.4.3-0

hostPath

hostPath卷提供的是映射到工作節點本地持久存儲。所以應僅當需要在工作節點上讀寫文件時才使用hostPath

下例中,Pod中創建一個名為html的hostPath卷掛載到工作節點的 /tmp/html,容器volume-hostpath將卷html掛載到/var/html,並且向其中寫入文件。

# volume-hostpath-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: volume-hostpath
spec:
  containers:
    - image: alpine
      name: volume-hostpath
      command: ["sh","-c","mkdir /var/html; while :; do echo $(date) > /var/html/index.html;sleep 1;done"]
      volumeMounts:
      - name: html
        mountPath: /var/html
  volumes:
  - name: html
    hostPath:
      path: /tmp/html

創建volume-hostpath,查看其部署在哪個節點。

-> [[email protected]] [~] k create -f volume-hostpath-pod.yaml
pod/volume-hostpath created

-> [[email protected]] [~] k get po -o wide
NAME              READY   STATUS    RESTARTS   AGE     IP            NODE       NOMINATED NODE   READINESS GATES
volume-hostpath   1/1     Running   0          9m32s   10.244.1.11   kube1.vm   <none>           <none>

進入節點kube1.vm

-> [[email protected]] [~] cat /tmp/html/index.html
Sun May 24 02:26:14 UTC 2020

PV與PVC

PV(PersistentVolume持久卷)也是一種資源,並且不屬於任何命名空間。它的功能與卷類似,但是它的生命周期是獨立於Pod的。PV由集群管理員創建,並被Pod通過PVC(PersistentVolumeClaim,持久卷聲明)使用。

在創建PV時,管理員可以指定其大小和支援的訪問模式:

  • ReadWriteOnce(RWO):僅允許單個節點掛載讀寫
  • ReadOnlyMany(ROX):允許多個節點掛載只讀
  • ReadWriteMany(RWX):允許多個節點掛載讀寫

一個卷不論支援多少種訪問模式,同時只能以一種訪問模式載入。

創建PV

管理員在創建PV時需要指定,PV的大小、訪問模式、實際存儲類型、路徑等。

# pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mypv
spec:
  capacity:
    storage: 10Mi	# 定義大小
  accessModes:	# 支援單個客戶端掛在為讀寫模式或者多個客戶端只讀模式
  - ReadWriteOnce
  - ReadOnlyMany
  persistentVolumeReclaimPolicy: Retain	# 當聲明被釋放後,PV將被保留
  hostPath:
    path: /tmp/pv

條件有限,所以存儲類型只能先選hostPath用著,接下來創建pv並查看

-> [[email protected]] [~] k create -f pv.yaml
persistentvolume/volume-pv created

-> [[email protected]] [~] k get pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
mypv   10Mi       RWO,ROX        Retain           Available                                   4s

創建PVC

假設要部署一個需要持久化存儲的Pod,將要用到持久卷,但是不能在Pod中直接使用,需要先聲明一個。

# pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mypvc
spec:
  resources:
    requests:
      storage: 10Mi
  accessModes:
  - ReadWriteOnce
  storageClassName: ""

PVC創建好後,Kubernetes會尋找適當的PV將其綁定到PVC。持久卷必須要足夠大,並且包含聲明中指定的訪問模式。

-> [[email protected]] [~] k create -f pvc.yaml
persistentvolumeclaim/mypvc created

-> [[email protected]] [~] k get pv,pvc
NAME                    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM           STORAGECLASS   REASON   AGE
persistentvolume/mypv   10Mi       RWO,ROX        Retain           Bound    default/mypvc                           7m11s

NAME                          STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/mypvc   Bound    mypv     10Mi       RWO,ROX                       9s

可以看到持久卷被綁定到default/mypvc聲明上,default是mypvc的命名空間。PV不存在命名空間的概念,但是PVC只能在特定命名空間創建。

Pod中使用PVC

# use-pvc-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: use-pvc
spec:
  containers:	# 這裡的內容與volume-share.yaml的一樣,所以功能不再贅敘,
    - image: alpine
      name: html-generator
      volumeMounts:
      - name: html
        mountPath: /var/html
      command: ["sh","-c","mkdir /var/html; while :; do echo $(date) > /var/html/index.html;sleep 1;done"]
  volumes:
  - name: html
    persistentVolumeClaim:	# 這裡使用了PVC類型,制定了PVC的名字
      claimName: mypvc

創建Pod

-> [[email protected]] [~] k create -f use-pvc-pod.yaml
pod/use-pvc created

-> [[email protected]] [~] k get po -o wide
NAME      READY   STATUS    RESTARTS   AGE    IP            NODE       NOMINATED NODE   READINESS GATES
use-pvc   1/1     Running   0          4m1s   10.244.1.12   kube1.vm   <none>           <none>

與kube1.vm查看是否寫入了內容

-> [[email protected]] [~] cat /tmp/pv/index.html
Sun May 24 06:43:02 UTC 2020

回收PV

刪除PVC後,查看PV,可以看到此時的狀態是Released,而不是之前的Available。

-> [[email protected]] [~] k delete pvc mypvc
persistentvolumeclaim "mypvc" deleted

-> [[email protected]] [~] k get pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS     CLAIM           STORAGECLASS   REASON   AGE
mypv   10Mi       RWO,ROX        Retain           Released   default/mypvc                           35s

這時候在創建PVC,會發現mypvc的狀態一直是Pending,因為沒有可用的PV。所以,persistentVolumeReclaimPolicy設置為Retain的PV需要手動刪除重建才能恢復可用。

-> [[email protected]] [~] k create -f  pvc.yaml
persistentvolumeclaim/mypvc created

-> [[email protected]] [~] k get pvc
NAME    STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mypvc   Pending                                                     16s

persistentVolumeReclaimPolicy用來設置回收策略,其他兩種是Recycle和Delete。可以在現有的PV上更改卷回收策略,比如開始設置為Delete,可以改為Retain。

PV與PVC的聲明周期,以及在Pod中的使用

StorageClass

使用PV與PVC可以使開發人員不用關心實際的存儲技術,但是仍然需要集群管理人員來支援實際的存儲。可以通過創建StorageClass資源解決此問題。

StorageClass的作用是:為引用它的PVC在創建的時候通過置備程式創建一個PV。

工作流程簡介:

  • 集群管理員根據不同性能及特性創建若干個StorageClass
  • 開發人員創建一個引用StorageClass的PVC
  • Kubernetes查找引用的StorageClass置備程式,並按照PVC的訪問模式和存儲大小置備新的PV

因為環境問題,下面的例子使用minikube運行。

創建StorageClass

# storageclass-fast.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast
provisioner: k8s.io/minikube-hostpath
parameters:
  type: pd-ssd

運行查看

-> [[email protected]] [~/work/k8s] kubectl create -f storageclass-fast.yaml
storageclass.storage.k8s.io/fast created

-> [[email protected]] [~/work/k8s] kubectl get sc
NAME                 PROVISIONER                RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
fast                 k8s.io/minikube-hostpath   Delete          Immediate           false                  114m
standard (default)   k8s.io/minikube-hostpath   Delete          Immediate           false                  117m

創建PVC引用StorageClass

# pvc-storageclass.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-sc
spec:
  resources:
    requests:
      storage: 10Mi
  accessModes:
  - ReadWriteOnce
  storageClassName: fast	# 引用了上面名為fast的StorageClass

可以看到,成功創建了一個名為pvc-be95fa9c-0666-4e3b-a2e2-a68c0bbea6f6的持久卷,綁定的持久卷聲明是default/pvc-sc。訪問模式與存儲大小與pvc-sc的都一致。

-> [[email protected]] [~/work/k8s] kubectl create -f pvc-storageclass.yaml
persistentvolumeclaim/pvc-sc created

-> [[email protected]] [~/work/k8s] kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM            STORAGECLASS   REASON   AGE
pvc-be95fa9c-0666-4e3b-a2e2-a68c0bbea6f6   10Mi       RWO            Delete           Bound    default/pvc-sc   fast                    3m36s

-> [[email protected]] [~/work/k8s] kubectl get pvc
NAME     STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc-sc   Bound    pvc-be95fa9c-0666-4e3b-a2e2-a68c0bbea6f6   10Mi       RWO            fast           3m40s

不指定存儲類的動態配置

如果將PVC配置中的storageClassName設置為空,那麼創建PVC時將優先綁定到預先配置的PV,而不是由StorageClass配置新的PV。

流程圖

小結

  • 卷是Pod的一部分,與Pod共享生命周期。它不是獨立的Kubernetes對象,因此不能單獨創建。
  • 卷提供的存儲功能不但可以解決容器重啟後數據丟失的問題,還可以使數據在容器間共享。
  • gitRepo卷基本上相當於emptyDir,它在容器啟動前從git倉庫檢出填充數據。但git倉庫與gitRepo的內容並不保持同步。
  • hostPath卷提供的是映射到工作節點本地持久存儲
  • PV是一種資源,並且不屬於任何命名空間。它的功能與卷類似,但是它的生命周期是獨立於Pod的。PV由集群管理員創建,並被Pod通過PVC使用。
  • PVC創建好後,Kubernetes會尋找適當的PV將其綁定到PVC。持久卷必須要足夠大,並且包含聲明中指定的訪問模式。
  • 可以在現有的PV上更改卷回收策略,比如開始設置為Delete,可以改為Retain。
  • StorageClass為引用它的PVC在創建的時候通過置備程式創建一個PV
  • 如果將PVC配置中的storageClassName設置為空,那麼創建PVC時將優先綁定到預先配置的PV,而不是由StorageClass配置新的PV。