8 第八章 數據存儲


8 第八章 數據存儲

在前面已經提到,容器的生命周期可能很短,會被頻繁地創建和銷毀。那么容器在銷毀時,保存在容器中的數據也會被清除。這種結果對用戶來說,在某些情況下是不樂意看到的。為了持久化保存容器的數據,kubernetes引入了Volume的概念。

Volume是Pod中能夠被多個容器訪問的共享目錄,它被定義在Pod上,然后被一個Pod里的多個容器掛載到具體的文件目錄下,kubernetes通過Volume實現同一個Pod中不同容器之間的數據共享以及數據的持久化存儲。Volume的生命容器不與Pod中單個容器的生命周期相關,當容器終止或者重啟時,Volume中的數據也不會丟失。

kubernetes的Volume支持多種類型,比較常見的有下面幾個:

  • 簡單存儲:EmptyDir、HostPath、NFS

  • 高級存儲:PV、PVC

  • 配置存儲:ConfigMap、Secret

8.1 基本存儲

8.1.1 EmptyDir

EmptyDir是最基礎的Volume類型,一個EmptyDir就是Host上的一個空目錄。

EmptyDir是在Pod被分配到Node時創建的,它的初始內容為空,並且無須指定宿主機上對應的目錄文件,因為kubernetes會自動分配一個目錄,當Pod銷毀時, EmptyDir中的數據也會被永久刪除。 EmptyDir用途如下:

  • 臨時空間,例如用於某些應用程序運行時所需的臨時目錄,且無須永久保留

  • 一個容器需要從另一個容器中獲取數據的目錄(多容器共享目錄)

接下來,通過一個容器之間文件共享的案例來使用一下EmptyDir。

在一個Pod中准備兩個容器nginx和busybox,然后聲明一個Volume分別掛在到兩個容器的目錄中,然后nginx容器負責向Volume中寫日志,busybox中通過命令將日志內容讀到控制台。

創建一個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
[root@k8s-master01 ~]# kubectl create -f volume-emptydir.yaml
pod/volume-emptydir created

# 查看pod
[root@k8s-master01 ~]# kubectl get pods volume-emptydir -n dev -o wide
NAME                  READY   STATUS    RESTARTS   AGE      IP       NODE   ......
volume-emptydir       2/2     Running   0          97s   10.42.2.9   node1  ......

# 通過podIp訪問nginx
[root@k8s-master01 ~]# curl 10.42.2.9
......

# 通過kubectl logs命令查看指定容器的標准輸出
[root@k8s-master01 ~]# kubectl logs -f volume-emptydir -n dev -c busybox
10.42.1.0 - - [27/Jun/2021:15:08:54 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"

8.1.2 HostPath

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

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

創建一個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
[root@k8s-master01 ~]# kubectl create -f volume-hostpath.yaml
pod/volume-hostpath created

# 查看Pod
[root@k8s-master01 ~]# kubectl get pods volume-hostpath -n dev -o wide
NAME                  READY   STATUS    RESTARTS   AGE   IP             NODE   ......
pod-volume-hostpath   2/2     Running   0          16s   10.42.2.10     node1  ......

#訪問nginx
[root@k8s-master01 ~]# curl 10.42.2.10

# 接下來就可以去host的/root/logs目錄下查看存儲的文件了
### 注意: 下面的操作需要到Pod所在的節點運行(案例中是node1)
[root@node1 ~]# ls /root/logs/
access.log  error.log

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

8.1.3 NFS

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

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

1)首先要准備nfs的服務器,這里為了簡單,直接是master節點做nfs服務器

# 在nfs上安裝nfs服務
[root@nfs ~]# yum install nfs-utils -y

# 准備一個共享目錄
[root@nfs ~]# mkdir /root/data/nfs -pv

# 將共享目錄以讀寫權限暴露給192.168.5.0/24網段中的所有主機
[root@nfs ~]# vim /etc/exports
[root@nfs ~]# more /etc/exports
/root/data/nfs     192.168.5.0/24(rw,no_root_squash)

# 啟動nfs服務
[root@nfs ~]# systemctl restart nfs

2)接下來,要在的每個node節點上都安裝下nfs,這樣的目的是為了node節點可以驅動nfs設備

# 在node上安裝nfs服務,注意不需要啟動
[root@k8s-master01 ~]# 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.5.6  #nfs服務器地址
    path: /root/data/nfs #共享文件路徑

4)最后,運行下pod,觀察結果

# 創建pod
[root@k8s-master01 ~]# kubectl create -f volume-nfs.yaml
pod/volume-nfs created

# 查看pod
[root@k8s-master01 ~]# kubectl get pods volume-nfs -n dev
NAME READY STATUS RESTARTS AGE
volume-nfs 2/2 Running 0 2m9s

# 查看nfs服務器上的共享目錄,發現已經有文件了
[root@k8s-master01 ~]# ls /root/data/
access.log error.log

8.2 高級存儲

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

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

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

使用了PV和PVC之后,工作可以得到進一步的細分:

  • 存儲:存儲工程師維護

  • PV: kubernetes管理員維護

  • PVC:kubernetes用戶維護

8.2.1 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環境

# 創建目錄
[root@nfs ~]# mkdir /root/data/{pv1,pv2,pv3} -pv

# 暴露服務
[root@nfs ~]# more /etc/exports
/root/data/pv1 192.168.5.0/24(rw,no_root_squash)
/root/data/pv2 192.168.5.0/24(rw,no_root_squash)
/root/data/pv3 192.168.5.0/24(rw,no_root_squash)

# 重啟服務
[root@nfs ~]# systemctl restart nfs

2) 創建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.5.6

---

apiVersion: v1
kind: PersistentVolume
metadata:
name: pv2
spec:
capacity:
storage: 2Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
path: /root/data/pv2
server: 192.168.5.6

---

apiVersion: v1
kind: PersistentVolume
metadata:
name: pv3
spec:
capacity:
storage: 3Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
path: /root/data/pv3
server: 192.168.5.6
# 創建 pv
[root@k8s-master01 ~]# kubectl create -f pv.yaml
persistentvolume/pv1 created
persistentvolume/pv2 created
persistentvolume/pv3 created

# 查看pv
[root@k8s-master01 ~]# kubectl get pv -o wide
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS AGE VOLUMEMODE
pv1 1Gi RWX Retain Available 10s Filesystem
pv2 2Gi RWX Retain Available 10s Filesystem
pv3 3Gi RWX Retain Available 9s Filesystem

8.2.2 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
[root@k8s-master01 ~]# kubectl create -f pvc.yaml
persistentvolumeclaim/pvc1 created
persistentvolumeclaim/pvc2 created
persistentvolumeclaim/pvc3 created

# 查看pvc
[root@k8s-master01 ~]# kubectl get pvc -n dev -o wide
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE VOLUMEMODE
pvc1 Bound pv1 1Gi RWX 15s Filesystem
pvc2 Bound pv2 2Gi RWX 15s Filesystem
pvc3 Bound pv3 3Gi RWX 15s Filesystem

# 查看pv
[root@k8s-master01 ~]# kubectl get pv -o wide
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM AGE VOLUMEMODE
pv1 1Gi RWx Retain Bound dev/pvc1 3h37m Filesystem
pv2 2Gi RWX Retain Bound dev/pvc2 3h37m Filesystem
pv3 3Gi RWX Retain Bound dev/pvc3 3h37m Filesystem

2) 創建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
[root@k8s-master01 ~]# kubectl create -f pods.yaml
pod/pod1 created
pod/pod2 created

# 查看pod
[root@k8s-master01 ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE
pod1 1/1 Running 0 14s 10.244.1.69 node1
pod2 1/1 Running 0 14s 10.244.1.70 node1

# 查看pvc
[root@k8s-master01 ~]# kubectl get pvc -n dev -o wide
NAME STATUS VOLUME CAPACITY ACCESS MODES AGE VOLUMEMODE
pvc1 Bound pv1 1Gi RWX 94m Filesystem
pvc2 Bound pv2 2Gi RWX 94m Filesystem
pvc3 Bound pv3 3Gi RWX 94m Filesystem

# 查看pv
[root@k8s-master01 ~]# kubectl get pv -n dev -o wide
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM AGE VOLUMEMODE
pv1 1Gi RWX Retain Bound dev/pvc1 5h11m Filesystem
pv2 2Gi RWX Retain Bound dev/pvc2 5h11m Filesystem
pv3 3Gi RWX Retain Bound dev/pvc3 5h11m Filesystem

# 查看nfs中的文件存儲
[root@nfs ~]# more /root/data/pv1/out.txt
node1
node1
[root@nfs ~]# more /root/data/pv2/out.txt
node2
node2

8.2.3 生命周期

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

8.3 配置存儲

8.3.1 ConfigMap

ConfigMap是一種比較特殊的存儲卷,它的主要作用是用來存儲配置信息的。

創建configmap.yaml,內容如下:

apiVersion: v1
kind: ConfigMap
metadata:
name: configmap
namespace: dev
data:
info: |
username:admin
password:123456

接下來,使用此配置文件創建configmap

# 創建configmap
[root@k8s-master01 ~]# kubectl create -f configmap.yaml
configmap/configmap created

# 查看configmap詳情
[root@k8s-master01 ~]# kubectl describe cm configmap -n dev
Name: configmap
Namespace: dev
Labels: <none>
Annotations: <none>

Data
====
info:
----
username:admin
password:123456

Events: <none>

接下來創建一個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
[root@k8s-master01 ~]# kubectl create -f pod-configmap.yaml
pod/pod-configmap created

# 查看pod
[root@k8s-master01 ~]# kubectl get pod pod-configmap -n dev
NAME READY STATUS RESTARTS AGE
pod-configmap 1/1 Running 0 6s

#進入容器
[root@k8s-master01 ~]# kubectl exec -it pod-configmap -n dev /bin/sh
# cd /configmap/config/
# ls
info
# more info
username:admin
password:123456

# 可以看到映射已經成功,每個configmap都映射成了一個目錄
# key--->文件 value---->文件中的內容
# 此時如果更新configmap的內容, 容器中的值也會動態更新

8.3.2 Secret

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

1) 首先使用base64對數據進行編碼

[root@k8s-master01 ~]# echo -n 'admin' | base64 #准備username
YWRtaW4=
[root@k8s-master01 ~]# echo -n '123456' | base64 #准備password
MTIzNDU2

2) 接下來編寫secret.yaml,並創建Secret

apiVersion: v1
kind: Secret
metadata:
name: secret
namespace: dev
type: Opaque
data:
username: YWRtaW4=
password: MTIzNDU2
# 創建secret
[root@k8s-master01 ~]# kubectl create -f secret.yaml
secret/secret created

# 查看secret詳情
[root@k8s-master01 ~]# kubectl describe secret secret -n dev
Name: secret
Namespace: dev
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
password: 6 bytes
username: 5 bytes

3) 創建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
[root@k8s-master01 ~]# kubectl create -f pod-secret.yaml
pod/pod-secret created

# 查看pod
[root@k8s-master01 ~]# kubectl get pod pod-secret -n dev
NAME READY STATUS RESTARTS AGE
pod-secret 1/1 Running 0 2m28s

# 進入容器,查看secret信息,發現已經自動解碼了
[root@k8s-master01 ~]# kubectl exec -it pod-secret /bin/sh -n dev
/ # ls /secret/config/
password username
/ # more /secret/config/username
admin
/ # more /secret/config/password
123456

至此,已經實現了利用secret實現了信息的編碼。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM