k8s數據存儲

容器的生命週期可能很短,會被頻繁地創建和銷燬。那麼容器在銷燬時,保存在容器中的數據也會被清除。這種結果對用戶來說,在某些情況下是不樂意看到的。爲了持久化保存容器的數據,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中通過命令將日誌內容讀到控制檯。

img

創建一個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

image-20220530160018041

image-20220530160029120

image-20220530164559963

image-20220530164613698

HostPath

上節課提到,EmptyDir中數據不會被持久化,它會隨着Pod的結束而銷燬,如果想簡單的將數據持久化到主機中,可以選擇HostPath。

HostPath就是將Node主機中一個實際目錄掛在到Pod中,以供容器使用,這樣的設計就可以保證Pod銷燬了,但是數據依據可以存在於Node主機上。

img

創建一個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/

# 同樣的道理,如果在此目錄下創建一個文件,到容器中也是可以看到的

image-20220530170756121

image-20220530170936284

NFS

HostPath可以解決數據持久化的問題,但是一旦Node節點故障了,Pod如果轉移到了別的節點,又會出現問題了,此時需要準備單獨的網絡存儲系統,比較常用的用NFS、CIFS。

NFS是一個網絡文件存儲系統,可以搭建一臺NFS服務器,然後將Pod中的存儲直接連接到NFS系統上,這樣的話,無論Pod在節點上怎麼轉移,只要Node跟NFS的對接沒問題,數據就可以成功訪問。

img

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

image-20220530172018076

image-20220530172215154

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/

image-20220530172712913

高級存儲

前面已經學習了使用NFS提供存儲,此時就要求用戶會搭建NFS系統,並且會在yaml配置nfs。由於kubernetes支持的存儲系統有很多,要求客戶全都掌握,顯然不現實。爲了能夠屏蔽底層存儲實現的細節,方便用戶使用, kubernetes引入PV和PVC兩種資源對象。

PV(Persistent Volume)是持久化卷的意思,是對底層的共享存儲的一種抽象。一般情況下PV由kubernetes管理員進行創建和配置,它與底層具體的共享存儲技術有關,並通過插件完成與共享存儲的對接。

PVC(Persistent Volume Claim)是持久卷聲明的意思,是用戶對於存儲需求的一種聲明。換句話說,PVC其實就是用戶向kubernetes系統發出的一種資源需求申請。

img

使用了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個暴露的路徑。

  1. 準備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

image-20220531101109652

  1. 創建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

image-20220531101416718

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 )

    描述對存儲資源的請求

實驗

  1. 創建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  

image-20220531104018609

  1. 創建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

image-20220531104432574

image-20220531104607200

生命週期

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綁定和使用

img

配置存儲

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

image-20220531112436453

接下來創建一個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

image-20220531112818077

image-20220531113013273

image-20220531112950561

image-20220531113147074

Secret

在kubernetes中,還存在一種和ConfigMap非常類似的對象,稱爲Secret對象。它主要用於存儲敏感信息,例如密碼、祕鑰、證書等等。

  1. 首先使用base64對數據進行編碼
echo -n 'admin' | base64 #準備username
echo -n '123456' | base64 #準備password

image-20220531141359996

  1. 接下來編寫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

image-20220531141705201

  1. 創建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實現了信息的編碼。

image-20220531141921708

image-20220531142006905

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