k8s實踐(七):存儲卷和數據持久化(Volumes and Persistent Storage)

環境說明:

主機名 操作系統版本 ip docker version kubelet version 配置 備註
master Centos 7.6.1810 172.27.9.131 Docker 18.09.6 V1.14.2 2C2G master主機
node01 Centos 7.6.1810 172.27.9.135 Docker 18.09.6 V1.14.2 2C2G node節點
node02 Centos 7.6.1810 172.27.9.136 Docker 18.09.6 V1.14.2 2C2G node節點
centos7 Centos 7.3.1611 172.27.9.181 × × 1C1G nfs服務器

 

k8s集羣部署詳見:Centos7.6部署k8s(v1.14.2)集羣
k8s學習資料詳見:基本概念、kubectl命令和資料分享

 

一、Volume

1. 概念

  Kubernetes的卷是pod的一個組成部分,因此像容器一樣在pod的規範中就定義了。它們不是獨立的Kubernetes對象,也不能單獨創建或刪除。pod中的所有容器都可以使用卷,但必須先將它掛載在每個需要訪問它的容器中。在每個容器中,都可以在其文件系統的任意位置掛載卷。

2. 爲什麼需要Volume

  容器磁盤上的文件的生命週期是短暫的,這就使得在容器中運行重要應用時會出現一些問題。首先,當容器崩潰時,kubelet會重啓它,但是容器中的文件將丟失——容器以乾淨的狀態(鏡像最初的狀態)重新啓動。其次,在 Pod 中同時運行多個容器時,這些容器之間通常需要共享文件。Kubernetes 中的 Volume 抽象就很好的解決了這些問題。

3. Volume類型

目前,Kubernetes支持以下Volume 類型:

k8s實踐(七):存儲卷和數據持久化(Volumes and Persistent Storage)

本文將對emptyDir,hostPath,共享存儲NFS,PV及PVC分別進行測試實踐。

二、emptyDir

1. emptyDir概念

  emptyDir是最基礎的Volume類型,用於存儲臨時數據的簡單空目錄。如果Pod設置了emptyDir類型Volume,Pod被分配到Node上時候,會創建emptyDir,只要Pod運行在Node上,emptyDir都會存在(容器掛掉不會導致emptyDir丟失數據),但是如果Pod從Node上被刪除(Pod被刪除,或者Pod發生遷移),emptyDir也會被刪除,並且永久丟失。

 

  下面將用emptyDir卷實現在同一pod中兩個容器之間的文件共享

k8s實踐(七):存儲卷和數據持久化(Volumes and Persistent Storage)

2. 創建pod emptyDir-fortune

[root@master ~]# more emptyDir-pod.yaml 
apiVersion: v1
kind: Pod
metadata:
  labels:
    app: prod                           #pod標籤 
  name: emptydir-fortune
spec:
  containers:
  - image: loong576/fortune
    name: html-generator
    volumeMounts:                       #名爲html的卷掛載至容器的/var/htdocs目錄
    - name: html
      mountPath: /var/htdocs
  - image: nginx:alpine
    name: web-server
    volumeMounts:                       #掛載相同的卷至容器/usr/share/nginx/html目錄且設置爲只讀
    - name: html
      mountPath: /usr/share/nginx/html 
      readOnly: true
    ports:
    - containerPort: 80
      protocol: TCP
  volumes:
  - name: html                          #卷名爲html的emptyDir卷同時掛載至以上兩個容器
    emptyDir: {} 
[root@master ~]# kubectl apply -f emptyDir-pod.yaml 
pod/emptydir-fortune created
[root@master ~]# kubectl get po -o wide             
NAME               READY   STATUS    RESTARTS   AGE   IP             NODE     NOMINATED NODE   READINESS GATES
emptydir-fortune   2/2     Running   0          9s    10.244.2.140   node02   <none>           <none>

k8s實踐(七):存儲卷和數據持久化(Volumes and Persistent Storage)

創建pod emptydir-fortune,該pod有兩個容器,同時掛載emptyDir卷,容器html-generator向卷中寫入隨機內容,通過訪問容器web-server驗證是否實現文件的共享。

2.1 loong576/fortune鏡像

root@master ~]# more Dockerfile

[root@master ~]# more fortune/Dockerfile 
FROM ubuntu:latest

RUN apt-get update ; apt-get -y install fortune
ADD fortuneloop.sh /bin/fortuneloop.sh

E*TRYPOINT /bin/fortuneloop.sh

該鏡像的base鏡像爲ubuntu,鏡像啓動時會執行fortuneloop.sh腳本
 

fortuneloop.sh腳本:

[root@master ~]# more fortuneloop.sh 
#!/bin/bash
trap "exit" SIGINT
mkdir /var/htdocs

while :
do
  echo $(date) Writing fortune to /var/htdocs/index.html
  /usr/games/fortune > /var/htdocs/index.html
  sleep 10
done

該腳本主要是每10秒鐘輸出隨機短語至index.html文件中。

3. 訪問nginx

3.1 創建service

[root@master ~]# more service-fortune.yaml 
apiVersion: v1
kind: Service
metadata:
  name: my-service           #service名
spec:
  type: NodePort
  selector:
    app: prod                #pod標籤,由此定位到pod emptydir-fortune 
  ports:
  - protocol: TCP
    nodePort: 30002          #節點監聽端口,暴露靜態端口30002對外提供服務
    port: 8881               #ClusterIP監聽的端口 
    targetPort: 80           #容器端口 
  sessionAffinity: ClientIP  #是否支持Session,同一個客戶端的訪問請求都轉發到同一個後端Pod 
[root@master ~]# kubectl apply -f service-fortune.yaml 
service/my-service created
[root@master ~]# kubectl get svc 
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP          3d17h
my-service   NodePort    10.102.191.57   <none>        8881:30002/TCP   9s

3.2 nginx訪問

[root@master ~]# curl 10.102.191.57:8881
Writing is easy; all you do is sit staring at the blank sheet of paper until
drops of blood form on your forehead.
                -- Gene Fowler
[root@master ~]# curl 172.27.9.135:30002
Don't Worry, Be Happy.
                -- Meher Baba

k8s實踐(七):存儲卷和數據持久化(Volumes and Persistent Storage)

結論:

  • 容器nginx成功的讀取到了容器fortune寫入存儲的內容,emptyDir卷可以實現容器間的文件共享。

  • emptyDir卷的生存週期與pod的生存週期相關聯,所以當刪除pod時,卷的內容就會丟失

三、hostPath

1. 概念

  hostPath允許掛載Node上的文件系統到Pod裏面去。如果Pod需要使用Node上的文件,可以使用hostPath。在同一個節點上運行並在其hostPath卷中使用相同路徑的pod可以看到相同的文件。

k8s實踐(七):存儲卷和數據持久化(Volumes and Persistent Storage)

2. 創建pod hostpath-nginx

2.1 創建掛載目錄

在node節點上創建掛載目錄,master和各node上分別執行如下操作

[root@master ~]# mkdir /data && cd /data && echo `hostname` > index.html

2.2 創建pod

[root@master ~]# more hostPath-pod.yaml 
apiVersion: v1
kind: Pod
metadata:
  labels:
    app: prod 
  name: hostpath-nginx 
spec:
  containers:
  - image: nginx 
    name: nginx 
    volumeMounts:
    - mountPath: /usr/share/nginx/html   #容器掛載點 
      name: nginx-volume                 #掛載卷nginx-volume
  volumes:
  - name: nginx-volume                   #卷名
    hostPath:
      path: /data                        #準備掛載的node上的文件系統
[root@master ~]# kubectl apply -f hostPath-pod.yaml 
pod/hostpath-nginx created
[root@master ~]# kubectl get po -o wide
NAME               READY   STATUS    RESTARTS   AGE   IP             NODE     NOMINATED NODE   READINESS GATES
emptydir-fortune   2/2     Running   0          40m   10.244.2.140   node02   <none>           <none>
hostpath-nginx     1/1     Running   0          16s   10.244.1.140   node01   <none>           <none>

3. 訪問pod hostpath-nginx

[root@master ~]# curl 10.244.1.140
node01

k8s實踐(七):存儲卷和數據持久化(Volumes and Persistent Storage)

結論:

  • pod運行在node01上,訪問的內容爲'node01',爲掛載的文件系統/data下index.html內容,容器成功讀取到掛載的節點文件系統裏的內容。

  • 僅當需要在節點上讀取或寫入系統文件時才使用hostPath , 切勿使用它們來持久化跨pod的數據。

  • hostPath可以實現持久存儲,但是在node節點故障時,也會導致數據的丟失。

四、NFS共享存儲

1. 概念

  NFS是Network File System的縮寫,即網絡文件系統。Kubernetes中通過簡單地配置就可以掛載NFS到Pod中,而NFS中的數據是可以永久保存的,同時NFS支持同時寫操作。

  emptyDir可以提供不同容器間的文件共享,但不能存儲;hostPath可以爲不同容器提供文件的共享並可以存儲,但受制於節點限制,不能跨節點共享;這時需要網絡存儲 (NAS),即既可以方便存儲容器又可以從任何集羣節點訪問,本文以NFS爲例做測試。

2. nfs搭建及配置

nfs搭建詳見:Centos7下NFS服務器搭建及客戶端連接配置

 

完成nfs服務器搭建和客戶端nfs軟件安裝安裝後,可在master和各node節點檢查nfs服務是否正常

[root@master ~]# showmount -e 172.27.9.181
Export list for 172.27.9.181:
/backup 172.27.9.0/24

master和node01、node02節點都執行showmount命令,用於驗證nfs服務是否正常,/backup爲nfs服務器對外提供的共享目錄。

k8s實踐(七):存儲卷和數據持久化(Volumes and Persistent Storage)

本文測試的NFS內容:

k8s實踐(七):存儲卷和數據持久化(Volumes and Persistent Storage)

3. 新建pod mongodb-nfs

[root@master ~]# more mongodb-pod-nfs.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: mongodb-nfs
spec:
  containers:
  - image: mongo
    name: mongodb
    volumeMounts:
    - name: nfs-data               #掛載的卷名,與上面的mongodb-data保持一致
      mountPath: /data/db          #MongoDB數據存放的路徑
    ports:
    - containerPort: 27017
      protocol: TCP
  volumes:
  - name: nfs-data                 #卷名
    nfs:
      server: 172.27.9.181         #nfs服務器ip
      path: /backup                #nfs服務器對外提供的共享目錄
[root@master ~]# kubectl apply -f mongodb-pod-nfs.yaml 
pod/mongodb-nfs created
[root@master ~]# kubectl get po -o wide                
NAME          READY   STATUS    RESTARTS   AGE   IP             NODE     NOMINATED NODE   READINESS GATES
mongodb-nfs   1/1     Running   0          23s   10.244.2.142   node02   <none>           <none>

注意此時pod的ip爲10.244.2.142

k8s實踐(七):存儲卷和數據持久化(Volumes and Persistent Storage)

4. nfs共享存儲測試

4.1 向MongoDB寫入數據

[root@master ~]# kubectl exec -it mongodb-nfs mongo
> use loong
switched to db loong
> db.foo.insert({name:'loong576'})
WriteResult({ "nInserted" : 1 })

切換至db loong,插入JSON文檔(name:'loong576')

4.2 查看寫入的數據

> db.foo.find()
{ "_id" : ObjectId("5d6e17b018651a21e0063641"), "name" : "loong576" }

k8s實踐(七):存儲卷和數據持久化(Volumes and Persistent Storage)

4.3 刪除pod並重建

[root@master ~]# kubectl delete pod mongodb-nfs 
pod "mongodb-nfs" deleted
[root@master ~]# kubectl apply -f mongodb-pod-nfs.yaml
pod/mongodb-nfs created
[root@master ~]# kubectl get po -o wide               
NAME          READY   STATUS    RESTARTS   AGE   IP             NODE     NOMINATED NODE   READINESS GATES
mongodb-nfs   1/1     Running   0          22s   10.244.2.143   node02   <none>           <none>

刪除pod mongodb-nfs並重建,此時podip變爲10.244.2.143,再次訪問MongoDB驗證之前寫入的文檔是否還存在。

4.4 新pod讀取共享存儲數據

[root@master ~]# kubectl exec  -it mongodb-nfs  mongo
> use loong
switched to db loong
> db.foo.find()
{ "_id" : ObjectId("5d6e17b018651a21e0063641"), "name" : "loong576" }

k8s實踐(七):存儲卷和數據持久化(Volumes and Persistent Storage)

即使pod被刪除重建仍然能訪問共享數據。

結論:

  • NFS共享存儲可持久化數據
  • NFS共享存儲可跨節點提供數據共享

五、PV and PVC

1. 概念

   PersistentVolume (持久卷, 簡稱 PV)和Persistent VolumeClaim(持久卷聲明,簡稱 PVC)使得K8s集羣具備了存儲的邏輯抽象能力,使得在配置Pod的邏輯裏可以忽略對實際後臺存儲技術的配置,而把這項配置的工作交給PV的配置者,即集羣的管理者。存儲的PV和PVC的這種關係,跟計算的Node和Pod的關係是非常類似的;PV和Node是資源的提供者,根據集羣的基礎設施變化而變化,由K8s集羣管理員配置;而PVC和Pod是資源的使用者,根據業務服務的需求變化而變化,由K8s集羣的使用者即服務的管理員來配置。

 

  當集羣用戶需要在其pod中使用持久化存儲時,他們首先創建PVC清單,指定所需要的最低容量要求和訪問模式,然後用戶將待久卷聲明清單提交給Kubernetes API服務器,Kubernetes將找到可匹配的PV並將其綁定到PVC。PVC可以當作pod中的一個捲來使用,其他用戶不能使用相同的PV,除非先通過刪除PVC綁定來釋放。

k8s實踐(七):存儲卷和數據持久化(Volumes and Persistent Storage)

2. 創建PV

2.1 nfs配置

nfs服務器共享目錄配置:

[root@centos7 ~]# exportfs         
/backup/v1      172.27.9.0/24
/backup/v2      172.27.9.0/24
/backup/v3      172.27.9.0/24

master和各node節點檢查nfs配置:

[root@master ~]# showmount -e 172.27.9.181
Export list for 172.27.9.181:
/backup/v3 172.27.9.0/24
/backup/v2 172.27.9.0/24
/backup/v1 172.27.9.0/24

2.2 PV創建

[root@master ~]# more pv-nfs.yaml 
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv001
spec:
  capacity:
    storage: 2Gi                             #指定PV容量爲2G
  volumeMode: Filesystem                     #卷模式,默認爲Filesystem,也可設置爲'Block'表示支持原始塊設備
  accessModes:
    - ReadWriteOnce                          #訪問模式,該卷可以被單個節點以讀/寫模式掛載
  persistentVolumeReclaimPolicy: Retain      #回收策略,Retain(保留),表示手動回收
  storageClassName: nfs                      #類名,PV可以具有一個類,一個特定類別的PV只能綁定到請求該類別的PVC
  nfs:                                       #指定NFS共享目錄和IP信息
    path: /backup/v1
    server: 172.27.9.181
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv002
spec:
  capacity:
    storage: 2Gi                             #指定PV容量爲2G
  volumeMode: Filesystem                     #卷模式,默認爲Filesystem,也可設置爲'Block'表示支持原始塊設備
  accessModes:
    - ReadOnlyMany                           #訪問模式,該卷可以被多個節點以只讀模式掛載
  persistentVolumeReclaimPolicy: Retain      #回收策略,Retain(保留),表示手動回收
  storageClassName: nfs                      #類名,PV可以具有一個類,一個特定類別的PV只能綁定到請求該類別的PVC
  nfs:                                       #指定NFS共享目錄和IP信息
    path: /backup/v2
    server: 172.27.9.181
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv003
spec:
  capacity:
    storage: 1Gi                             #指定PV容量爲1G
  volumeMode: Filesystem                     #卷模式,默認爲Filesystem,也可設置爲'Block'表示支持原始塊設備
  accessModes:
    - ReadWriteOnce                          #訪問模式,該卷可以被單個節點以讀/寫模式掛載
  persistentVolumeReclaimPolicy: Retain      #回收策略,Retain(保留),表示手動回收
  storageClassName: nfs                      #類名,PV可以具有一個類,一個特定類別的PV只能綁定到請求該類別的PVC
  nfs:                                       #指定NFS共享目錄和IP信息
    path: /backup/v3
    server: 172.27.9.181
[root@master ~]# kubectl apply -f pv-nfs.yaml 
persistentvolume/pv001 created
persistentvolume/pv002 created
persistentvolume/pv003 created
[root@master ~]# kubectl get pv
NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLA*S   REASON   AGE
pv001   2Gi        RWO            Retain           Available           nfs                     26s
pv002   2Gi        ROX            Retain           Available           nfs                     26s
pv003   1Gi        RWO            Retain           Available           nfs                     26s

創建pv001、pv002、pv003,分別對應nfs的共享目錄/backup/v1、/backup/v2、/backup/v2。

k8s實踐(七):存儲卷和數據持久化(Volumes and Persistent Storage)

卷可以處於以下的某種狀態:

  • Available(可用),一塊空閒資源還沒有被任何聲明綁定
  • Bound(已綁定),卷已經被聲明綁定
  • Released(已釋放),聲明被刪除,但是資源還未被集羣重新聲明
  • Failed(失敗),該卷的自動回收失敗

 

PV的訪問模式有三種:

  • 第一種,ReadWriteOnce:是最基本的方式,可讀可寫,但只支持被單個Pod掛載。
  • 第二種,ReadOnlyMany:可以以只讀的方式被多個Pod掛載。
  • 第三種,ReadWriteMany:這種存儲可以以讀寫的方式被多個Pod共享。不是每一種存儲都支持這三種方式,像共享方式,目前支持的還比較少,比較常用的是NFS。

k8s實踐(七):存儲卷和數據持久化(Volumes and Persistent Storage)

PV不屬於任何命名空間, 它跟節點一樣是集羣層面的資源,區別於pod和PVC。

3. 創建PVC

3.1 PVC創建

[root@master ~]# more pvc-nfs.yaml 
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: mypvc                       #聲明的名稱,當做pod的卷使用時會用到                          
spec:
  accessModes:
    - ReadWriteOnce                 #訪問卷模式,篩選PV條件之一
  volumeMode: Filesystem            #卷模式,與PV保持一致,指示將卷作爲文件系統或塊設備使用
  resources:                        #聲明可以請求特定數量的資源,篩選PV條件之一
    requests:
      storage: 2Gi
  storageClassName: nfs             #請求特定的類,與PV保持一致,否則無法完成綁定
[root@master ~]# kubectl apply -f pvc-nfs.yaml 
persistentvolumeclaim/mypvc created
[root@master ~]# kubectl get pvc
NAME    STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mypvc   Bound    pv001    2Gi        RWO            nfs            22s

創建PVC mypvc,訪問卷模式爲ReadWriteOnce,大小爲2G;WO、ROX、RWX、RWO表示可以同時使用卷的工作節點的數量而並非pod的數量。

k8s實踐(七):存儲卷和數據持久化(Volumes and Persistent Storage)

3.2 查看選中的PV

PVC篩選條件:

PV accessModes storage
pv001
pv002 ×
pv003 ×

PV查看:

[root@master ~]# kubectl get pv
NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM           STORAGECLA*S   REASON   AGE
pv001   2Gi        RWO            Retain           Bound       default/mypvc   nfs                     12m
pv002   2Gi        ROX            Retain           Available                   nfs                     12m
pv003   1Gi        RWO            Retain           Available                   nfs                     12m

k8s實踐(七):存儲卷和數據持久化(Volumes and Persistent Storage)

pv001被選中,符合PVC定義,pv002訪問模式不匹配,pv003大小不匹配。

4. pod中使用PVC

[root@master ~]# more mongodb-pod-pvc.yaml                  
apiVersion: v1
kind: Pod
metadata:
  name: mongodb-pvc 
spec:
  containers:
  - image: mongo
    name: mongodb
    volumeMounts:
    - name: pvc-data
      mountPath: /data/db
    ports:
    - containerPort: 27017
      protocol: TCP
  volumes:
  - name: pvc-data
    persistentVolumeClaim:
      claimName: mypvc          #與pvc中聲明的name保持一致
[root@master ~]# kubectl apply -f mongodb-pod-pvc.yaml 
pod/mongodb-pvc created
[root@master ~]# kubectl get po -o wide                           
NAME          READY   STATUS    RESTARTS   AGE   IP             NODE     NOMINATED NODE   READINESS GATES
mongodb-pvc   1/1     Running   0          16s   10.244.2.144   node02   <none>           <none>

k8s實踐(七):存儲卷和數據持久化(Volumes and Persistent Storage)

創建pod mongodb-pvc,使用PVC mypvc,測試同四-4中的nfs共享存儲測試,不再贅述。

k8s實踐(七):存儲卷和數據持久化(Volumes and Persistent Storage)

 
 

本文所有腳本和配置文件已上傳:k8s實踐(七):存儲卷和數據持久化(Volumes and Persistent Storage)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章