k8s-儲存
一、ConfigMap
ConfigMap
功能在 Kubernetes1.2
版本中引入,許多應用程式會從配置檔案、命令列引數或環境變數中讀取配置資訊。ConfigMap API
給我們提供了向容器中注入配置資訊的機制,ConfigMap
可以被用來儲存單個屬性,也可以用來儲存整個配置檔案或者 JSON
二進位制大物件
ConfigMap 的建立
1. 資源清單建立
-
建立 ConfigMap 的資源清單:
apiVersion: v1 # 版本,通過 kubectl explain cm 可以檢視 kind: ConfigMap metadata: name: special-config # ConfigMap 的名字 namespace: default # 名稱空間 data: # key: value 結構,配置資料 special.how: very special.type: charm
-
建立
kubectl apply -f comfigmap.yaml
2. 使用目錄建立
-
建立 /root/k8s/yaml/configmap/game.properties 檔案:
enemies=aliens lives=3 enemies.cheat=true enemies.cheat.level=noGoodRotten secret.code.passphrase=UUDDLRLRBABAS secret.code.allowed=true secret.code.lives=30
-
建立 /root/k8s/yaml/configmap/ui.properties 檔案:
color.good=purple color.bad=yellow allow.textmode=true how.nice.to.look=fairlyNice
-
建立 configmap ,
--from-file
指定在目錄下的所有檔案都會被用在ConfigMap
裡面建立一個鍵值對,鍵的名字就是檔名,值就是檔案的內容kubectl create configmap game-config --from-file=../configmap/
-
檢視建立的
configmap
(可簡寫為cm
):$ kubectl get cm NAME DATA AGE game-config 2 6m40s # 檢視詳細資訊 kubectl get cm game-config -o yaml kubectl describe cm game-config
3. 使用檔案建立
通過 --from-file
引數只要指定為一個檔案就可以從單個檔案中建立 ConfigMap
--from-file
這個引數可以使用多次,你可以使用兩次分別指定上個例項中的那兩個配置檔案,效果就跟指定整個目錄是一樣的
kubectl create configmap game-config-2 --fromfile=game.properties
kubectl get configmaps game-config-2 -o yaml
4. 使用字面值建立
使用文字值建立,利用 --from-literal
引數傳遞配置資訊,該引數可以使用多次,格式如下
kubectl create configmap special-config --from-literal=special.how=very --fromliteral=special.type=charm
kubectl get configmaps special-config -o yaml
Pod 中使用 ConfigMap
1. 使用 ConfigMap 來替代環境變數
-
建立兩個
ConfigMap
(configmap.yaml):apiVersion: v1 kind: ConfigMap metadata: name: special-config namespace: default data: special.how: very special.type: charm --- apiVersion: v1 kind: ConfigMap metadata: name: env-config namespace: default data: log_level: INFO
-
建立 Pod,並引入
apiVersion: v1 kind: Pod metadata: name: dapi-test-pod spec: containers: - name: test-container image: wangyanglinux/myapp:v1 command: [ "/bin/sh", "-c", "env" ] # 列印 env env: # 從 ConfigMap 中選擇讀取的鍵,並起個別名 - name: SPECIAL_LEVEL_KEY # 鍵別名,在這值應該是 very valueFrom: configMapKeyRef: name: special-config # ComfigMap 的名稱 key: special.how # 上句指定 ConfigMap 中的鍵名 - name: SPECIAL_TYPE_KEY # 鍵別名,在這值應該是 charm valueFrom: configMapKeyRef: name: special-config # ComfigMap 的名稱 key: special.type # 上句指定 ConfigMap 中的鍵名 envFrom: # 直接從 ConfigMap 中讀取全部配置 - configMapRef: name: env-config # ComfigMap 的名稱 restartPolicy: Never
-
檢視日誌,可以看到 ConfigMap 中的配置已經注入到了容器中
2. 使用 ConfigMap 設定命令列引數
-
建立 ConfigMap
apiVersion: v1 kind: ConfigMap metadata: name: special-config namespace: default data: special.how: very special.type: charm
-
建立 Pod
apiVersion: v1 kind: Pod metadata: name: dapi-test-pod spec: containers: - name: test-container image: wangyanglinux/myapp:v1 command: [ "/bin/sh", "-c", "echo $(SPECIAL_LEVEL_KEY) $(SPECIAL_TYPE_KEY)" ] #可以調整啟動Pod時的命令 env: # 從 ConfigMap 中選擇讀取的鍵,並起個別名 - name: SPECIAL_LEVEL_KEY # 鍵別名,在這值應該是 very valueFrom: configMapKeyRef: name: special-config # ComfigMap 的名稱 key: special.how # 上句指定 ConfigMap 中的鍵名 - name: SPECIAL_TYPE_KEY # 鍵別名,在這值應該是 charm valueFrom: configMapKeyRef: name: special-config # ComfigMap 的名稱 key: special.type restartPolicy: Never
-
檢視日誌
$ kubectl logs dapi-test-pod very charm
3. 通過資料卷外掛使用ConfigMap
通過 Volume
方式掛載,ConfigMap
中的鍵名就是 檔名,鍵值就是 檔案內容
-
建立
ConfigMap
apiVersion: v1 kind: ConfigMap metadata: name: special-config namespace: default data: special.how: very special.type: charm
-
建立
Pod
apiVersion: v1 kind: Pod metadata: name: dapi-test-pod spec: containers: - name: test-container image: wangyanglinux/myapp:v1 command: ["/bin/sh", "-c", "cat /etc/config/special.how"] # 列印掛載目錄下的檔案內容 volumeMounts: # volume 掛載 - name: config-volume # 掛載下面指定的 volume mountPath: /etc/config # 掛載到的目錄(容器內路徑,該目錄下,檔名就裡鍵名,檔案內容就是鍵值) volumes: - name: config-volume # volume 名稱 configMap: # 來自 ConfigMap name: special-config # ConfigMap 名字 restartPolicy: Never
-
檢視日誌
$ kubectl logs dapi-test-pod very
4. ConfigMap 的熱更新
-
建立一個
ConfigMap
和Deployment
:apiVersion: v1 kind: ConfigMap metadata: name: log-config namespace: default data: log_level: INFO --- apiVersion: extensions/v1beta1 kind: Deployment metadata: name: my-nginx spec: replicas: 1 template: metadata: labels: run: my-nginx spec: containers: - name: my-nginx image: wangyanglinux/myapp:v1 ports: - containerPort: 80 volumeMounts: # 這塊兒不懂看上一節《通過資料卷外掛使用ConfigMap》 - name: config-volume mountPath: /etc/config # 容器內這個目錄下會有 log_level 這個檔案,內容為 INFO volumes: - name: config-volume configMap: name: log-config
-
檢視
/etc/config/log_level
檔案的內容$ kubectl exec my-nginx-c484b98b4-sbls9 -it -- cat /etc/config/log_level INFO
-
修改 ConfigMap
kubectl edit configmap log-config
-
稍微等一會兒,再次檢視
/etc/config/log_level
檔案的內容,可以看到,Pod 中的配置也改了$ kubectl exec my-nginx-c484b98b4-sbls9 -it -- cat /etc/config/log_level DEBUG
- 注意:更新 ConfigMap 後:
- 使用該
ConfigMap
掛載的Env
不會同步更新 - 使用該
ConfigMap
掛載的Volume
中的資料需要一段時間(實測大概10秒)才能同步更新
- 使用該
-
讓
Pod
滾動更新ConfigMap
更新後,並不會讓相應的檔案過載。例如,Nginx
在啟動時,會載入一次配置檔案(配置檔案中有ConfigMap
的相關引數),載入完成後,無論這個配置檔案再怎麼變化,Nginx
都不會再載入它。因此需要ConfigMap
更新後滾動更新Pod
。- 可以通過修改
pod annotations
的方式強制觸發滾動更新 - 這裡我們在
.spec.template.metadata.annotations
中新增version/config
,每次通過修改version/config
的時間來觸發滾動更新
kubectl patch deployment my-nginx --patch \ '{"spec": {"template": {"metadata": {"annotations":{"version/config": "20201110" }}}}}'
- 可以通過修改
二、Secret
Secret
解決了密碼、token
、金鑰等敏感資料的配置問題,而不需要把這些敏感資料暴露到映象或者 Pod Spec
中。Secret
可以以 Volume
或者環境變數的方式使用
Secret
有三種型別:
Service Account
:用來訪問Kubernetes API
,由Kubernetes
自動建立,並且會自動掛載到Pod
的/run/secrets/kubernetes.io/serviceaccount
目錄中Opaque
:base64
編碼格式的Secret
,用來儲存密碼、金鑰等。加密程度不高kubernetes.io/dockerconfigjson
:用來儲存私有docker registry
的認證資訊
1. Service Account(不常用)
Service Account
用來訪問 Kubernetes API
,由 Kubernetes
自動建立,並且會自動掛載到 Pod
的 /run/secrets/kubernetes.io/serviceaccount
目錄中
# 1. 隨便找一個需要訪問 Kubernetes API 的 Pod
$ kubectl get pod -n kube-system
NAME READY STATUS RESTARTS AGE
kube-proxy-2pqkk 1/1 Running 6 40d
# 2. 檢視該 Pod 中 /run/secrets/kubernetes.io/serviceaccount 目錄下的檔案
$ kubectl exec kube-proxy-2pqkk -n kube-system -it -- ls /run/secrets/kubernetes.io/serviceaccount
ca.crt:訪問 API Service 時的證書
namespace:名稱空間
token:認證的金鑰資訊
2. Opaque Secret
Opaque
型別的資料是一個 map
型別,要求 value
是 base64
編碼格式:
(1)建立 Opaque Secret
-
給使用者名稱和密碼用 base64 加密
$ echo -n admin | base64 YWRtaW4= $ echo -n 123 | base64 MTIz
base64 解碼:
$ echo -n YWRtaW4= | base64 -d admin
-
使用加密後的使用者名稱和密碼建立
Secret
apiVersion: v1 # kubectl explain secret 檢視 kind: Secret metadata: name: mysecret # Secret 名稱 type: Opaque # Secret 的型別 data: password: MTIz # 密碼 username: YWRtaW4= # 使用者名稱
-
檢視
Secret
$ kubectl get secret NAME TYPE DATA AGE default-token-fm46c kubernetes.io/service-account-token 3 40d mysecret Opaque 2 12s
- default-token-xxxxx:k8s 預設會在每個名稱空間下都建立一個,用於 Pod 的掛載
(2)將 Secret 掛載到 Volume 中
-
建立 Pod
apiVersion: v1 kind: Pod metadata: labels: name: secret-test name: secret-test spec: volumes: # 建立一個卷 - name: secrets # 卷名 secret: # 卷使用的方案 secretName: mysecret # 來自於上一節建立的 mysecret containers: - image: wangyanglinux/myapp:v1 name: db volumeMounts: # 卷掛載 - name: secrets # 掛載的是上面宣告的 secrets mountPath: "/etc/secrets" # 掛載的目錄(容器內目錄) readOnly: true # 只讀
-
檢視
# Opaque Secret 中的使用者名稱和密碼都已經掛載進來了 $ kubectl exec secret-test -it -- ls /etc/secrets password username # 檢視內容,發現內容已經自動被解密 $ kubectl exec secret-test -it -- cat /etc/secrets/password 123 $ kubectl exec secret-test -it -- cat /etc/secrets/username admin
(3)將 Secret 匯出到環境變數中
-
建立 Deployment:
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: pod-deployment spec: replicas: 2 template: metadata: labels: app: pod-deployment spec: containers: - name: pod-1 image: wangyanglinux/myapp:v1 ports: - containerPort: 80 env: - name: TEST_USER # 環境變數名 valueFrom: secretKeyRef: # 從 Secret 中獲取 name: mysecret # Secret 的名字 key: username # Secret 中的鍵名 - name: TEST_PASSWORD # 環境變數名 valueFrom: secretKeyRef: # 從 Secret 中獲取 name: mysecret # Secret 的名字 key: password # Secret 中的鍵名(相比 configmap,Secret 在這兒不需要使用明文,稍微安全一點)
-
檢視環境變數
# 進入容器 $ kubectl exec pod-deployment-747f78bc67-2w9wk -it -- /bin/sh # 檢視環境變數 $ echo $TEST_USER admin $ echo $TEST_PASSWORD 123
3. kubernetes.io/dockerconfigjson
使用 Kuberctl
建立 docker registry
認證的 secret
# kubectl create secret docker-registry \ # 建立 Secret 的型別
# myregistrykey \ # Secret 的名稱
# --docker-server=hub.zyx.com \ # docker server 的地址
# --docker-username=admin \ # docker 使用者名稱
# --docker-password=Harbor12345 \ # docker 密碼
# --docker-email=aa@qq.com # docker 郵箱
kubectl create secret docker-registry \
myregistrykey \
--docker-server=hub.zyx.com \
--docker-username=admin \
--docker-password=Harbor12345 \
--docker-email=aa@qq.com
在建立 Pod
的時候,通過 imagePullSecrets
來引用剛建立的 myregistrykey
,來拉取私有倉庫的映象
apiVersion: v1
kind: Pod
metadata:
name: foo
spec:
containers:
- name: foo
image: hub.zyx.com/zyx/myapp:v1
imagePullSecrets: # 當去私有倉庫拉取時的認證資訊
- name: myregistrykey # 認證資訊,上一步建立的 docker registry
三、Volume
容器磁碟上的檔案的生命週期是短暫的,這就使得在容器中執行重要應用時會出現一些問題。
-
首先,當容器崩潰時,
kubelet
會重啟它,但是容器中的檔案將丟失——容器以乾淨的狀態(映象最初的狀態)重新啟動。 -
其次,在
Pod
中同時執行多個容器時,這些容器之間通常需要共享檔案。
Kubernetes
中的 Volume
抽象就很好的解決了這些問題
Kubernetes
中的卷有明確的壽命 —— 與封裝它的 Pod
相同。所以,卷的生命比 Pod 中的所有容器都長,當這個容器重啟時資料仍然得以儲存。當然,當 Pod 不再存在時,卷也將不復存在。也許更重要的是,Kubernetes
支援多種型別的卷,Pod
可以同時使用任意數量的卷
Kubernetes 支援以下型別的卷:
awsElasticBlockStore
azureDisk
azureFile
cephfs
csi
downwardAPI
emptyDir
fc
flocker
gcePersistentDisk
gitRepo
glusterfs
hostPath
iscsi
local
nfs
persistentVolumeClaim
projected
portworxVolume
quobyte
rbd
scaleIO
secret
storageos
vsphereVolume
1. emptyDir
當 Pod
被分配給節點時,首先建立 emptyDir
卷,並且只要該 Pod
在該節點上執行,該卷就會存在。正如卷的名字所述,它最初是空的。該卷可以掛載到 Pod
每個容器中的相同或不同路徑上,並且每個容器都可以讀取和寫入 emptyDir
卷中的檔案。當出於任何原因從節點中刪除 Pod
時, emptyDir
中的資料將被永久刪除
注意:容器崩潰不會從節點中移除 pod,因此 emptyDir
卷中的資料在容器時是安全的
emptyDir
的用法有:
- 暫存空間,例如用於基於磁碟的合併排序
- 用作長時間計算崩潰恢復時的檢查點
Web
伺服器容器提供資料時,儲存內容管理器容器提取的檔案
-
建立一個
Pod
,裡面有兩個容器,都掛載同一個emptyDir
apiVersion: v1 kind: Pod metadata: name: test-pd spec: containers: - image: wangyanglinux/myapp:v1 name: test-container # 容器名 volumeMounts: - mountPath: /cache # 掛載到容器的哪個目錄下 name: cache-volume # 通過哪個 volume 掛載 - name: test2-container image: busybox args: # 執行命令,睡眠,防止容器退出 - /bin/sh - -c - sleep 6000s volumeMounts: - mountPath: /test # 掛載到容器的哪個目錄下 name: cache-volume # 通過哪個 volume 掛載 volumes: - name: cache-volume # volume 名稱 emptyDir: {} # volume 型別
-
可以看到,兩個容器都能讀取到
emptyDir
中的資料# 在容器2的 /test 目錄下,建立一個 index.html 檔案 $ kubectl exec test-pd -c test2-container -it -- touch /test/index.html # 檢視容器1的 /cache 目錄 $ kubectl exec test-pd -c test-container -it -- ls /cache index.html
2. hostPath
hostPath
卷將主機節點的檔案系統中的檔案或目錄掛載到叢集中
hostPath
的用途如下:
- 執行需要訪問
Docker
內部的容器;使用/var/lib/docker
的hostPath
- 在容器中執行
cAdvisor
;使用/dev/cgroups
的hostPath
注意事項:
- 由於每個節點上的檔案都不同,具有相同配置(例如從
podTemplate
建立的)的pod
在不同節點上的行為可能會有所不同。 - 當
Kubernetes
按照計劃新增資源感知排程時,將無法考慮hostPath
使用的資源 - 在底層主機上建立的檔案或目錄只能由
root
寫入。您需要在特權容器中以root
身份執行程式,或修改主機上的檔案許可權以便寫入hostPath
卷
(1)type
的值
值 | 行為 |
---|---|
空字串(預設)用於向後相容,這意味著在掛載 hostPath 卷之前不會執行任何檢查。 | |
DirectoryOrCreate | 如果在給定的路徑上沒有任何東西存在,那麼將根據需要在那裡建立一個空目錄,許可權設定為 0755,與 Kubelet 具有相同的組和所有權。 |
Directory | 給定的路徑下必須存在目錄 |
FileOrCreate | 如果在給定的路徑上沒有任何東西存在,那麼會根據需要建立一個空檔案,許可權設定為 0644,與 Kubelet 具有相同的組和所有權。 |
File | 給定的路徑下必須存在檔案 |
Socket | 給定的路徑下必須存在 UNIX 套接字 |
CharDevice | 給定的路徑下必須存在字元裝置 |
BlockDevice | 給定的路徑下必須存在塊裝置 |
(2)示例
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: wangyanglinux/myapp:v1
name: test-container
volumeMounts:
- mountPath: /test-pd # 掛載到的容器內路徑
name: test-volume # 選擇下面宣告的 volume 進行掛載
volumes:
- name: test-volume # volume名稱
hostPath: # volume 型別
path: /data # 本機的 /data 目錄(Pod 執行在叢集的哪個節點,就是哪個節點上的 /data 目錄)
type: Directory # 型別(如果不存在 /data 會報錯)
注意:要確保每個 k8s 節點上都存在 /data
這個目錄
四、PV/PVC
1. 概念
-
PersistentVolume
(PV):是由管理員設定的儲存,它是群集的一部分。就像節點是叢集中的資源一樣,PV 也是叢集中的資源。
PV
是Volume
之類的卷外掛,但具有獨立於使用PV
的Pod
的生命週期。此API
物件包含儲存實現的細節,即NFS
、iSCSI
或特定於雲供應商的儲存系統 -
PersistentVolumeClaim
(PVC):是使用者儲存的請求。它與
Pod
相似。Pod
消耗節點資源,PVC
消耗PV
資源。Pod
可以請求特定級別的資源(CPU 和記憶體)。PVC
可以宣告可以請求特定的大小和訪問模式(例如,可以以讀/寫一次或 只讀多次模式掛載)
生命週期
PV的分類
-
靜態 pv:
叢集管理員建立一些 PV。它們帶有可供群集使用者使用的實際儲存的細節,一般儲存訪問至後端儲存的細節(怎麼連線,地址多少…)。它們存在於 Kubernetes API 中,可用於消費
-
動態 pv:
當管理員建立的靜態 PV 都不匹配使用者的
PersistentVolumeClaim
時,叢集可能會嘗試動態地為 PVC 建立卷。此配置基於StorageClasses
:PVC 必須請求 [儲存類],並且管理員必須建立並配置該類才能進行動態建立。宣告該類為""
可以有效地禁用其動態配置要啟用基於儲存級別的動態儲存配置,叢集管理員需要啟用 API server 上的
DefaultStorageClass
[准入控制器]。例如,通過確保DefaultStorageClass
位於 API server 元件的--admission-control
標誌,使用逗號分隔的有序值列表中,可以完成此操作
繫結:
master
中的控制環路監視新的 PVC
,尋找匹配的 PV
(如果可能),並將它們繫結在一起。如果為新的 PVC
動態調配 PV
,則該環路將始終將該 PV
繫結到 PVC
。否則,使用者總會得到他們所請求的儲存,但是容量可能超出要求的數量。
一旦 PV 和 PVC 繫結後, PersistentVolumeClaim
繫結是排他性的,不管它們是如何繫結的。 PVC
跟 PV
繫結是一對一的對映
持久化卷宣告的保護
PVC
保護的目的是確保由 Pod 正在使用的 PVC
不會從系統中移除,因為如果被移除的話可能會導致資料丟失。
注意:當 Pod 狀態為 Pending
並且 Pod 已經分配給節點或者 Pod 為 Running
狀態時,PVC 處於活動狀態。
當啟用PVC 保護 alpha
功能時,如果使用者刪除了一個 pod 正在使用的 PVC,則該 PVC 不會被立即刪除。PVC 的刪除將被推遲,直到 PVC 不再被任何 pod 使用。
2. PV 一些概念
(1)PV 的型別(外掛)
PersistentVolume
型別以外掛形式實現。Kubernetes
目前支援以下外掛型別:
- GCEPersistentDisk、AWSElasticBlockStore、AzureFile、AzureDisk、FC (Fibre Channel)
- FlexVolume、Flocker、
NFS
、iSCS、RBD(Ceph Block Device)、CephFS - Cinder(OpenStack block storage)、
Glusterfs
、VsphereVolume、Quobyte、Volumes HostPath
、VMware、Photon、Portworx、Volumes、ScaleIO、Volumes、StorageOS
(2)訪問模式
PersistentVolume
可以以資源提供者支援的任何方式掛載到主機上。如下表所示,供應商具有不同的功能,每個 PV 的訪問模式都將被設定為該卷支援的特定模式。例如,NFS 可以支援多個讀/寫客戶端,但特定的 NFS PV 可能以只讀方式匯出到伺服器上。每個 PV 都有一套自己的用來描述特定功能的訪問模式
ReadWriteOnce
——該卷可以被單個節點以讀/寫模式掛載ReadOnlyMany
——該卷可以被多個節點以只讀模式掛載ReadWriteMany
——該卷可以被多個節點以讀/寫模式掛載
在命令列中,訪問模式縮寫為:RWO
:ReadWriteOnceROX
:ReadOnlyManyRWX
:ReadWriteMany
注意:一個卷一次只能使用一種訪問模式掛載,即使它支援很多訪問模式。例如,GCEPersistentDisk
可以由單個節點作為 ReadWriteOnce
模式掛載,或由多個節點以 ReadWriteMany
模式掛載,但不能同時掛載
Volume 外掛 | ReadWriteOnce | ReadOnlyMany | ReadWriteMany |
---|---|---|---|
AWSElasticBlockStoreAWSElasticBlockStore | ✓ | - | - |
AzureFile | ✓ | ✓ | ✓ |
AzureDisk | ✓ | - | - |
CephFS | ✓ | ✓ | ✓ |
Cinder | ✓ | - | - |
FC | ✓ | ✓ | - |
FlexVolume | ✓ | ✓ | - |
Flocker | ✓ | - | - |
GCEPersistentDisk | ✓ | ✓ | - |
Glusterfs | ✓ | ✓ | ✓ |
HostPath | ✓ | - | - |
iSCSI | ✓ | ✓ | - |
PhotonPersistentDisk | ✓ | - | - |
Quobyte | ✓ | ✓ | ✓ |
NFS | ✓ | ✓ | ✓ |
RBD | ✓ | ✓ | - |
VsphereVolume | ✓ | - | - |
PortworxVolume | ✓ | - | ✓ |
ScaleIO | ✓ | ✓ | - |
StorageOS | ✓ | - | - |
(3)回收策略
Retain
(保留):手動回收Recycle
(回收):基本擦除( 相當於執行了rm -rf /thevolume/*
)Delete
(刪除):關聯的儲存資產(例如 AWS EBS、GCE PD、Azure Disk 和 OpenStack Cinder 卷)將被刪除
當前,只有 NFS
和 HostPath
支援回收策略。AWS EBS、GCE PD、Azure Disk 和 Cinder 卷支援刪除策略
(4)狀態
卷可以處於以下的某種狀態:
Available
(可用):一塊空閒資源還沒有被任何宣告繫結Bound
(已繫結):卷已經被宣告繫結Released
(已釋放):宣告被刪除,但是資源還未被叢集重新宣告Failed
(失敗):該卷的自動回收失敗
命令列會顯示繫結到 PV 的 PVC 的名稱
(5)模板
apiVersion: v1
kind: PersistentVolume # 型別:PV
metadata:
name: pv0003 # 名稱
spec:
capacity:
storage: 5Gi # 卷的大小:5G
volumeMode: Filesystem # 檔案型別
accessModes: # 訪問策略
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle # 回收策略
storageClassName: slow # 儲存類的一個名稱
mountOptions: # 其它說明,也可以不指定,讓他自己做判斷
- hard
- nfsvers=4.1
nfs:
path: /tmp # 掛載到哪個目錄下
server: 172.17.0.2 # 掛載到哪個伺服器
3. NFS 持久化示例
(1)安裝 NFS
-
新建一臺虛擬機器,安裝 NFS,我的虛擬機器 IP 為 192.168.66.20
yum install -y nfs-common nfs-utils rpcbind mkdir /nfs chmod 777 /nfs chown nfsnobody /nfs vim /etc/exports # 檔案中寫入以下內容 /nfs *(rw,no_root_squash,no_all_squash,sync) systemctl start rpcbind systemctl start nfs
-
在 k8s 每個節點中安裝 NFS 客戶端:
# 安裝依賴 $ yum -y install nfs-utils rpcbind
-
NFS 的一些操作
# 檢視共享目錄 $ showmount -e 192.168.66.20 Export list for 192.168.66.20: /nfs * # 共享目錄與本地目錄掛載 $ mkdir ~/test $ mount -t nfs 192.168.66.20:/nfs ~/test # 解除掛載 $ umount ~/test
(2)建立 PV 和 StatefulSet
-
建立一個 pv.yaml
apiVersion: v1 kind: PersistentVolume metadata: name: nfspv1 spec: capacity: # 容量 storage: 10Gi accessModes: # 訪問模式 - ReadWriteOnce persistentVolumeReclaimPolicy: Retain # 回收策略 storageClassName: nfs nfs: # nfs伺服器配置 path: /nfs # 目錄 server: 192.168.66.20 # IP
-
建立 PV
$ kubectl apply -f nfspv.yaml persistentvolume/nfspv1 created $ kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE nfspv1 10Gi RWO Retain Available nfs 7s
-
建立 Service 和 StatefulSet
apiVersion: v1 kind: Service metadata: name: nginx labels: app: nginx spec: ports: - port: 80 name: web clusterIP: None selector: app: nginx --- apiVersion: apps/v1 kind: StatefulSet metadata: name: web spec: selector: matchLabels: app: nginx serviceName: "nginx" # 指定 Service 名稱(上面建立的,一定要是個無頭服務) replicas: 3 # 副本數 template: metadata: labels: app: nginx spec: containers: # 容器資訊 - name: nginx image: wangyanglinux/myapp:v2 ports: - containerPort: 80 # 釋放的埠 name: web # 埠名字 volumeMounts: # 掛載 - name: www mountPath: /usr/share/nginx/html # 容器內目錄 volumeClaimTemplates: # 卷請求宣告模板(pvc模板) - metadata: name: www spec: accessModes: [ "ReadWriteOnce" ] # 指定要請求的卷的訪問模式 storageClassName: "nfs" # 指定要請求的卷的類名,只有與 PV 中的storageClassName 相同時,才會匹配 resources: requests: storage: 1Gi # 指定要請求的卷大小必須滿足 1G
-
因為前面只建立了一個 pv,所以
StatefulSet
只有一個 Pod 可以匹配,檢視 Pod:# 檢視pod,只有一個因為pv,所以只有一個pod匹配成功後正常執行 # 因為是 StatefulSet,第二個沒能正常啟動,所以第三個Pod不會建立 $ kubectl get pod NAME READY STATUS RESTARTS AGE web-0 1/1 Running 0 57s web-1 0/1 Pending 0 54s # 檢視 pv,可以看到只有一個繫結成功 # 第二個繫結不成功是因為訪問模式不匹配 # 第三個繫結不成功是因為 storageClass 不匹配 $ kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE nfspv1 10Gi RWO Retain Bound default/www-web-0 nfs 3m35s nfspv2 5Gi ROX Retain Available nfs 34m nfspv3 5Gi RWO Retain Available nfs1 34m # 檢視 pvc,每個Pod有一個pvc $ kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE www-web-0 Bound nfspv1 10Gi RWO nfs 6m6s www-web-1 Pending nfs 6m3s
-
在 NFS 伺服器的
/nfs
目錄中建立index.html
,寫入“/nfs訪問成功”,然後通過nginx
來訪問:# 獲取IP $ kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES web-0 1/1 Running 0 27m 10.244.1.58 k8s-node01 <none> <none> $ curl 10.244.1.58 /nfs訪問成功
-
嘗試刪除 Pod,pv 中的資料不會丟失
$ kubectl delete pod web-0 pod "web-0" deleted # 可以看到 IP 已經變了,說明上一個Pod刪除後又建了個新的(Pod 的 name 一致) $ kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES web-0 1/1 Running 0 15s 10.244.2.60 k8s-node02 <none> <none> # 可以看到仍然可以成功訪問,資料不會丟失 $ curl 10.244.2.60 /nfs訪問成功
-
刪除 StatefulSet 後,pvc 不會自動刪除,pv也不會自動釋放,需要手動刪除
# 刪除 StatefulSet 後,pvc 仍然存在 $ kubectl delete statefulset web $ kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE www-web-0 Bound nfspv1 10Gi RWO nfs 13h # 刪除 pvc 後,pv 沒有自動釋放 $ kubectl delete pvc www-web-0 $ kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE nfspv1 10Gi RWO Retain Released default/www-web-0 nfs 13h # 手動釋放 pv $ kubectl edit pv nfspv1 # 將下面的 spec.claimRef 刪除 spec: claimRef: apiVersion: v1 kind: PersistentVolumeClaim name: www-web-0 namespace: default resourceVersion: "619064" uid: 99cea07e-339e-431c-bcb6-c398c884b29c # 再次檢視 pv 已經得到釋放 $ kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE nfspv1 10Gi RWO Retain Available nfs 13h
3. PV 的一些說明
-
匹配 Pod name ( 網路標識 ) 的模式為:
$(StatefulSet名稱)-$(序號)
,比如上面的示例:web-0,web-1,web-2 -
StatefulSet
為每個 Pod 副本建立了一個 DNS 域名(意味著其它 Pod 可以通過這個域名來訪問),這個域名的格式為:$(podname).(headless server name)
,也就意味著服務間是通過 Pod 域名來通訊而非 Pod IP,因為當 Pod 所在 Node 發生故障時, Pod 會被飄移到其它 Node 上,Pod IP 會發生變化,但是 Pod 域名不會有變化# 隨便進入一個以前的 Pod,沒有就新建一個,然後ping域名,可以成功 ping 通 $ ping web-0.nginx PING web-0.nginx (10.244.2.60): 56 data bytes 64 bytes from 10.244.2.60: seq=0 ttl=62 time=0.388 ms 64 bytes from 10.244.2.60: seq=1 ttl=62 time=0.263 ms
-
StatefulSet 使用 Headless 服務來控制 Pod 的域名(意味著 Pod 外部可以通過這個域名來訪問),這個域名的 FQDN(完全現定域名) 為:
$(service name).(namespace).svc.cluster.local
,其中,“cluster.local
” 指的是叢集的域名# 1. 檢視 DNS 伺服器 coredns 的 ip $ kubectl get pod -n kube-system -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES coredns-5c98db65d4-5ztqn 1/1 Running 10 46d 10.244.0.19 k8s-master01 <none> <none> coredns-5c98db65d4-pc62t 1/1 Running 10 46d 10.244.0.18 k8s-master01 <none> <none> # 2. 通過 coredns 來解析域名,可以看到解析後的域名對應 StatefulSet 下的 Pod 的 IP # 用 dig 解析域名(沒有 dig 要安裝:yum -y install bind-utils) # 命令格式:dig -t A 域名 @DNS伺服器IP $ dig -t A nginx.default.svc.cluster.local @10.244.0.19 ...省略 nginx.default.svc.cluster.local. 30 IN A 10.244.2.60 ...省略
-
根據
volumeClaimTemplates
,為每個 Pod 建立一個pvc
,pvc
的命名規則匹配模式:
(volumeClaimTemplates.name)-(pod_name)
,比如上面的 volumeMounts.name=www, Pod
name=web-[0-2],因此建立出來的 PVC 是 www-web-0、www-web-1、www-web-2 -
刪除 Pod 不會刪除其 pvc,手動刪除 pvc 將自動釋放 pv
相關文章
- k8s-資料持久化儲存卷,nfs,pv/pvcK8S持久化NFS
- 塊儲存 檔案儲存 物件儲存物件
- 行式儲存 列式儲存
- 儲存—物件儲存_Minio物件
- 自動儲存、靜態儲存和動態儲存
- k8s-核心元件K8S元件
- MyISAM 儲存引擎,Innodb 儲存引擎儲存引擎
- 資料儲存--檔案儲存
- 儲存
- 物件儲存 vs 檔案儲存 vs 塊儲存,選哪個?物件
- 儲存過程與儲存函式儲存過程儲存函式
- PostgreSQL儲存智慧-空間聚集儲存SQL
- Redis儲存結構以及儲存格式Redis
- 聚焦資料時代新儲存需求,浪潮儲存的新儲存之道
- 雲原生儲存詳解:容器儲存與 K8s 儲存卷K8S
- mysql儲存函過程和儲存函式都屬於儲存程式MySql儲存函式
- 物件儲存,未來儲存新潮流物件
- Android儲存(2)– 介面卡儲存Android
- Flutter持久化儲存之檔案儲存Flutter持久化
- 容器附加儲存(CAS)是雲原生儲存
- 儲存引擎儲存引擎
- 照片儲存
- 儲存管理
- 物件儲存物件
- 儲存Word
- 【儲存】EMC
- 本地儲存
- 【Java】儲存Java
- HDS儲存
- 儲存概述
- 儲存lun
- 段式儲存
- 儲存器
- MySQL儲存MySql
- Flutter持久化儲存之key-value儲存Flutter持久化
- Flutter持久化儲存之資料庫儲存Flutter持久化資料庫
- DAOS 分散式非同步物件儲存|儲存模型分散式非同步物件模型
- Android-內部儲存和外部儲存Android