kubernetes?volume數(shù)據(jù)存儲的使用解讀
寫在前面:如有問題,以你為準(zhǔn)
概述
容器的生命周期可能很短,會被頻繁的創(chuàng)建和銷毀
保存在容器中的數(shù)據(jù)也會被清除,所以要實現(xiàn)持久化存儲(Volume)
通過將本地目錄掛載到pod,一個目錄可以掛載到多個pod中的容器
Volume是Pod中能夠被多個容器訪問的共享目錄
通過Volume實現(xiàn)同一個Pod中不同容器之間的數(shù)據(jù)共享以及數(shù)據(jù)的持久化存儲
Volume的生命周期不和Pod中的單個容器的生命周期有關(guān),當(dāng)容器終止或者重啟的時候,Volume中的數(shù)據(jù)也不會丟失
kubernetes的Volume支持多種類型,比較常見的有下面的幾個:
- 簡單存儲:EmptyDir、HostPath、NFS。
- 高級存儲:PV、PVC。
- 配置存儲:ConfigMap、Secret。
EmptyDir:pod中臨時存儲空間可以被多個容器掛載,實現(xiàn)共享目錄,pod刪除其也刪除
HostPath:將node上的目錄掛載到pod里面的容器里面,實現(xiàn)了持久化存儲,但是沒有實現(xiàn)存儲高可用
Kubernetes 目前支持多達(dá) 28 種數(shù)據(jù)卷類型(其中大部分特定于具體的云環(huán)境如 GCE/AWS/Azure 等)
非持久性存儲:
- emptyDir
- HostPath
網(wǎng)絡(luò)連接性存儲:
- SAN:iSCSI、ScaleIO Volumes、FC (Fibre Channel)
- NFS:nfs,cfs
分布式存儲
- Glusterfs
- RBD (Ceph Block Device)
- CephFS
- Portworx Volumes
- Quobyte Volumes
云端存儲
- GCEPersistentDisk
- AWSElasticBlockStore
- AzureFile
- AzureDisk
- Cinder (OpenStack block storage)
- VsphereVolume
- StorageOS
自定義存儲
- FlexVolume
基本存儲
EmptyDir
EmptyDir是最基礎(chǔ)的Volume類型,一個EmptyDir就是Host上的一個空目錄。
不是持久化數(shù)據(jù)存儲,生命周期跟pod一樣,一般是用于多容器共享目錄
EmptyDir是在Pod被分配到Node時創(chuàng)建的,它的初始內(nèi)容為空,并且無須指定宿主機(jī)上對應(yīng)的目錄文件,因為kubernetes會自動分配一個目錄,當(dāng)Pod銷毀時,EmptyDir中的數(shù)據(jù)也會被永久刪除。
EmptyDir的用途如下:
- 臨時空間,例如用于某些應(yīng)用程序運行時所需的臨時目錄,且無須永久保留。
- 一個容器需要從另一個容器中獲取數(shù)據(jù)的目錄(多容器共享目錄)。
示例
接下來,通過一個容器之間的共享案例來使用描述一個EmptyDir。
在一個Pod中準(zhǔn)備兩個容器nginx和busybox,然后聲明一個volume分別掛載到兩個容器的目錄中,然后nginx容器負(fù)責(zé)向volume中寫日志,busybox中通過命令將日志內(nèi)容讀到控制臺。


apiVersion: v1
kind: Pod
metadata:
name: volume-emptydir
namespace: study
spec:
containers:
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c","tail -f /var/log/nginx/access.log"]
ports:
- containerPort: 80
volumeMounts: # 將logs-volume掛載到nginx容器中對應(yīng)的目錄,該目錄為/var/log/nginx
- name: logs-volume # 卷名
mountPath: /var/log/nginx # 要掛載到容器的目錄,該目錄且要容器必須存在
- name: busybox
image: busybox:1.30
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c","tail -f /logs/access.log"] # 初始命令,持續(xù)讀取指定文件
# 讀取的時候是前臺,而kubectl logs命令就是讀取前臺日志/dev/stdout和/dev/stderr
volumeMounts: # 將logs-volume掛載到busybox容器中的對應(yīng)目錄,該目錄為/logs
- name: logs-volume
mountPath: /logs
volumes: # 聲明volume,name為logs-volume,類型為emptyDir
- name: logs-volume # 用于掛載時指定的volume名字
emptyDir: {} # 指定volume類型[root@master k8s]# kubectl apply -f emptydir.yaml pod/volume-emptydir created [root@master k8s]# kubectl get pod volume-emptydir -n study -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES volume-emptydir 2/2 Running 0 142m 10.244.104.44 node2 <none> <none>
[root@master k8s]# kubectl describe pod -n study # 訪問nginx,查看busybox掛載的目錄是否有信息,有則說明nginx的日志在logs-volume,且busybox也掛載了目錄 [root@master k8s]# curl 10.244.104.44 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> [root@master k8s]# kubectl logs -f volume-emptydir -n study -c busybox 192.168.100.53 - - [19/Feb/2023:08:15:33 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.61.1" "-"
HostPath
HostPath就是將Node主機(jī)中的一個實際目錄掛載到Pod中,以供容器使用,這樣的設(shè)計就可以保證Pod銷毀了,但是數(shù)據(jù)依舊可以保存在Node節(jié)點主機(jī)上
但是一旦Node節(jié)點故障了,Pod如果轉(zhuǎn)移到別的Node節(jié)點上,又會出現(xiàn)問題
還有就是將容器的日志導(dǎo)出等場景

volumes:
- name: test-volume
hostPath:
# directory location on host
path: /data
# this field is optional
type: Directory # 目錄必須存在指定路徑
# FileOrCreate 不會創(chuàng)建文件的父目錄,如果掛載文件的父目錄不存在,則pod啟動失敗
# DirectoryOrCreate 如果不存在就創(chuàng)建,且權(quán)限為0755,與 Kubelet 具有相同的組和所有權(quán)
# File 文件必須存在于給定路徑
# 如果不寫type就跳過檢測,則如果目錄不存在,yaml創(chuàng)建時候也不會報錯,類似于可選項
示例
apiVersion: v1
kind: Pod
metadata:
name: volume-hostpath
namespace: study
spec:
containers:
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
volumeMounts: # 將logs-volume掛載到nginx容器中對應(yīng)的目錄,該目錄為/var/log/nginx
- name: logs-volume
mountPath: /var/log/nginx
- name: busybox
image: busybox:1.30
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c","tail -f /logs/access.log"] # 初始命令,動態(tài)讀取指定文件
volumeMounts: # 將logs-volume掛載到busybox容器中的對應(yīng)目錄,該目錄為/logs
- name: logs-volume
mountPath: /logs
volumes: # 聲明volume,name為logs-volume,類型為hostPath
- name: logs-volume
hostPath:
path: /root/logs
type: DirectoryOrCreate # 目錄存在就使用,不存在就先創(chuàng)建再使用
nodeName: master # 測試方便查看hostPath被掛載的目錄[root@master k8s]# kubectl apply -f emptydir.yaml pod/volume-hostpath created [root@master k8s]# kubectl get pod -n study -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES volume-hostpath 2/2 Running 0 58s 10.244.219.71 master <none> <none> [root@master k8s]# curl 10.244.219.71 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> [root@master k8s]# cat /root/logs/access.log 192.168.100.53 - - [20/Feb/2023:06:23:56 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.61.1" "-"
subPath
subpath 主要是為了防止覆蓋
使用 mountPath 掛載 /etc/nginx ,但是我告訴 kubernetes 我掛載那個文件,此時就可以使用 subPath 了,此時 Kubernetes 就只會覆蓋 /etc/nginx/nginx.conf 文件
換言之,mountPath 告訴 Kubernetes 我需要覆蓋容器中的那個目錄,但是有了 subPath ,Kubernetes 就知道了,哦,原來你只需要覆蓋 mountPath 下邊的子文件?。╯ubPath )
apiVersion: v1
kind: Pod
metadata:
name: my-lamp-site
spec:
containers:
- name: mysql
image: mysql
env:
- name: MYSQL_ROOT_PASSWORD
value: "rootpasswd"
volumeMounts:
- mountPath: /var/lib/mysql
name: site-data
subPath: m
- name: php
image: php:7.0-apache
volumeMounts:
- mountPath: /var/www/html
name: site-data
subPath: m
volumes:
- name: site-data
emptyDir: {}NFS
hostpath可以實現(xiàn)持久化,但是沒有實現(xiàn)高可用,但是一旦Node節(jié)點故障了,Pod如果轉(zhuǎn)移到別的Node節(jié)點上,又會出現(xiàn)問題,而NFS就是為解決持久化存儲高可用
常用的有:NFS CIFS 等
NFS是一個網(wǎng)絡(luò)文件存儲系統(tǒng),可以搭建一臺NFS服務(wù)器,然后將Pod中的存儲直接連接到NFS系統(tǒng)上,這樣,無論P(yáng)od在節(jié)點上怎么轉(zhuǎn)移,只要Node和NFS的對接沒有問題,數(shù)據(jù)就可以成功訪問。

Master 搭建 NFS
首先需要準(zhǔn)備NFS服務(wù)器,這里為了簡單,直接在Master節(jié)點做NFS服務(wù)器。
在Master節(jié)點上安裝NFS服務(wù)器:
yum install -y nfs-utils rpcbind mkdir -pv /root/data/k8s-nfs vim /etc/exports # 將共享目錄以讀寫權(quán)限暴露給192.168.100.0/24網(wǎng)段中的所有主機(jī) /root/data/k8s-nfs 192.168.100.0/24(rw,no_root_squash) # 讀寫權(quán)限,不使用root權(quán)限 systemctl start rpcbind systemctl enable rpcbind systemctl start nfs-server systemctl enable nfs-server
在Node節(jié)點上都安裝NFS服務(wù)器,目的是為了Node節(jié)點可以驅(qū)動NFS設(shè)備
# 在Node節(jié)點上安裝NFS服務(wù),不需要啟動 yum -y install nfs-utils showmount -e 192.168.100.53 mount -t nfs 192.168.100.53:/root/data/k8s-nfs /mnt #關(guān)機(jī)后 mount -t nfs 192.168.100.53:/root/data/k8s-nfs/mnt
示例
apiVersion: v1
kind: Pod
metadata:
name: volume-nfs
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
volumeMounts: # 將logs-volume掛載到nginx容器中對應(yīng)的目錄,該目錄為/var/log/nginx
- name: logs-volume
mountPath: /var/log/nginx
- name: busybox
image: busybox:1.30
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c","tail -f /logs/access.log"] # 初始命令,動態(tài)讀取指定文件
volumeMounts: # 將logs-volume掛載到busybox容器中的對應(yīng)目錄,該目錄為/logs
- name: logs-volume
mountPath: /logs
volumes: # 聲明volume
- name: logs-volume
nfs:
server: 192.168.100.53 # NFS服務(wù)器地址
path: /root/data/k8s-nfs # 共享文件路徑[root@master k8s]# kubectl apply -f emptydir.yaml pod/volume-hostpath created [root@master k8s]# kubectl get pod -n study -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES volume-hostpath 2/2 Running 0 64s 10.244.104.53 node2 <none> <none> [root@master k8s]# curl 10.244.104.53 [root@master k8s]# cat /root/data/k8s-nfs/access.log 192.168.100.53 - - [20/Feb/2023:07:15:55 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.61.1" "-" 192.168.100.53 - - [20/Feb/2023:07:49:20 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.61.1" "-"
高級存儲(pv pvc)
概述架構(gòu)
此時就要求用戶會搭建NFS系統(tǒng)等,并且會在yaml配置nfs
由于kubernetes支持的存儲系統(tǒng)有很多,要求客戶全部掌握,顯然不現(xiàn)實。為了能夠屏蔽底層存儲實現(xiàn)的細(xì)節(jié),方便用戶使用,kubernetes引入了PV和PVC兩種資源對(類似于資源池化,pv和pvc是抽象的存儲資源)

使用了PV和PVC之后,工作可以得到進(jìn)一步的提升:
- 存儲:存儲工程師維護(hù)。
- PV:kubernetes管理員維護(hù)。
- PVC:kubernetes用戶維護(hù)。
下面實操的架構(gòu):pod-pvc-pv-NFS
生命周期

PVC和PV是一一對應(yīng)的,PV和PVC之間的相互作用遵循如下的生命周期。
1.資源供應(yīng):管理員手動創(chuàng)建底層存儲和PV。
2.資源綁定:
用戶創(chuàng)建PVC,kubernetes負(fù)責(zé)根據(jù)PVC聲明去尋找PV,并綁定在用戶定義好PVC之后,系統(tǒng)將根據(jù)PVC對存儲資源的請求在以存在的PV中選擇一個滿足條件的。
- 一旦找到,就將該P(yáng)V和用戶定義的PVC進(jìn)行綁定,用戶的應(yīng)用就可以使用這個PVC了。
- 如果找不到,PVC就會無限期的處于Pending狀態(tài),直到系統(tǒng)管理員創(chuàng)建一個符合其要求的PV。
PV一旦綁定到某個PVC上,就會被這個PVC獨占,不能再和其他的PVC進(jìn)行綁定了。
3.資源使用:
- 用戶可以在Pod中像volume一樣使用PVC,Pod使用Volume的定義,將PVC掛載到容器內(nèi)的某個路徑進(jìn)行使用。
4.資源釋放:
- 上一個 pod還有東西存在目錄中
- 用戶刪除PVC來釋放PV。
- 當(dāng)存儲資源使用完畢后,用戶可以刪除PVC,和該P(yáng)VC綁定的PV將會標(biāo)記為“已釋放”,但是還不能立刻和其他的PVC進(jìn)行綁定。通過之前PVC寫入的數(shù)據(jù)可能還留在存儲設(shè)備上,只有在清除之后該P(yáng)V才能再次使用。
5.資源回收:
- kubernetes根據(jù)PV設(shè)置的回收策略進(jìn)行資源的回收。
- 對于PV,管理員可以設(shè)定回收策略,用于設(shè)置與之綁定的PVC釋放資源之后如何處理遺留數(shù)據(jù)的問題。只有PV的存儲空間完成回收,才能供新的PVC綁定和使用。
理論與使用 PV
類似于linux lvm 中的 vg
一般由kubernetes運維管理人員創(chuàng)建,然后提供給需要使用的人創(chuàng)建pvc
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv2
# 沒有namespace 是集群級別的資源,跨namespace使用
spec:
nfs: # 存儲類型,和底層正則的存儲對應(yīng)
path:
server:
capacity: # 存儲能力,目前只支持存儲空間的設(shè)置
storage: 2Gi
accessModes: # 訪問模式
- ReadWriteOnce
# ReadOnlyMany
# ReadWriteMany
storageClassName: # 存儲類別
persistentVolumeReclaimPolicy: # 回收策略pv的關(guān)鍵配置參數(shù)說明:
1.存儲類型:底層實際存儲的類型,kubernetes支持多種存儲類型,每種存儲類型的配置有所不同。
2.存儲能力(capacity):目前只支持存儲空間的設(shè)置(storage=1Gi),不過未來可能會加入IOPS、吞吐量等指標(biāo)的配置。
3.訪問模式(accessModes):
用來描述用戶應(yīng)用對存儲資源的訪問權(quán)限,訪問權(quán)限包括下面幾種方式:
- ReadWriteOnce(RWO):讀寫權(quán)限,但是只能被單個節(jié)點掛載(單pvc掛載)。
- ReadOnlyMany(ROX):只讀權(quán)限,可以被多個節(jié)點掛載。
- ReadWriteMany(RWX):讀寫權(quán)限,可以被多個節(jié)點掛載。
需要注意的是,底層不同的存儲類型可能支持的訪問模式不同。
4.回收策略( persistentVolumeReclaimPolicy):
當(dāng)PV不再被使用之后,對其的處理方式,目前支持三種策略:
- Retain(保留):保留數(shù)據(jù),需要管理員手動清理數(shù)據(jù)。
- Recycle(回收):清除PV中的數(shù)據(jù),效果相當(dāng)于 rm -rf /volume/*。
- Delete(刪除):和PV相連的后端存儲完成volume的刪除操作,常見于云服務(wù)器廠商的存儲服務(wù)。
需要注意的是,底層不同的存儲類型可能支持的回收策略不同。
5.存儲類別(storageClassName):PV可以通過storageClassName參數(shù)指定一個存儲類別。
- 具有特定類型的PV只能和請求了該類別的PVC進(jìn)行綁定。
- 未設(shè)定類別的PV只能和不請求任何類別的PVC進(jìn)行綁定。
6.狀態(tài)(status):一個PV的生命周期,可能會處于4種不同的階段。
- Available(可用):表示可用狀態(tài),還未被任何PVC綁定。
- Bound(已綁定):表示PV已經(jīng)被PVC綁定。
- Released(已釋放):表示PVC被刪除,但是資源還沒有被集群重新釋放。
- Failed(失?。罕硎驹揚(yáng)V的自動回收失敗。
示例
mkdir -pv /root/data/{pv1,pv2,pv3}
chmod 777 -R /root/data
# 修改 NFS 的配置文件
vim /etc/exports
/root/data/pv1 192.168.18.0/24(rw,no_root_squash)
/root/data/pv2 192.168.18.0/24(rw,no_root_squash)
/root/data/pv3 192.168.18.0/24(rw,no_root_squash)
systemctl restart nfs-serverapiVersion: v1
kind: PersistentVolume
metadata:
name: pv1
spec:
nfs: # 存儲類型,和底層正則的存儲對應(yīng)
path: /root/data/pv1
server: 192.168.100.53
capacity: # 存儲能力,目前只支持存儲空間的設(shè)置
storage: 1Gi
accessModes: # 訪問模式
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain # 回收策略
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv2
spec:
nfs: # 存儲類型,和底層正則的存儲對應(yīng)
path: /root/data/pv2
server: 192.168.100.53
capacity: # 存儲能力,目前只支持存儲空間的設(shè)置
storage: 2Gi
accessModes: # 訪問模式
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain # 回收策略
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv3
spec:
nfs: # 存儲類型,和底層正則的存儲對應(yīng)
path: /root/data/pv3
server: 192.168.100.53
capacity: # 存儲能力,目前只支持存儲空間的設(shè)置
storage: 3Gi
accessModes: # 訪問模式
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain # 回收策略[root@master k8s]# kubectl apply -f pv.yaml persistentvolume/pv1 created persistentvolume/pv2 created persistentvolume/pv3 created [root@master k8s]# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pv1 1Gi RWX Retain Available 27s pv2 2Gi RWX Retain Available 27s pv3 3Gi RWX Retain Available 27s CLAIM 如果被使用這里會顯示pvc的名字
理論與使用 PVC
PVC是對 PV 資源的申請,用來聲明對存儲空間、訪問模式、存儲類別需求信息
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc
namespace: dev
spec:
accessModes: # 訪客模式
- ReadWriteMany
selector: # 采用標(biāo)簽對PV選擇
storageClassName: # 存儲類別
resources: # 請求空間
requests:
storage: 5GiPVC的關(guān)鍵配置參數(shù)說明:
1.訪客模式(accessModes):用于描述用戶應(yīng)用對存儲資源的訪問權(quán)限。
必須和要連接的pv聲明的訪問模式要一樣
- ReadWriteOnce(RWO):讀寫權(quán)限,但是只能被單個節(jié)點掛載(單pvc掛載)。
- ReadOnlyMany(ROX):只讀權(quán)限,可以被多個節(jié)點掛載。
- ReadWriteMany(RWX):讀寫權(quán)限,可以被多個節(jié)點掛載。
2.用于描述用戶應(yīng)用對存儲資源的訪問權(quán)限:
- 選擇條件(selector):通過Label Selector的設(shè)置,可使PVC對于系統(tǒng)中已存在的PV進(jìn)行篩選。
- 存儲類別(storageClassName):PVC在定義時可以設(shè)定需要的后端存儲的類別,只有設(shè)置了該class的pv才能被系統(tǒng)選出。
- 資源請求(resources):描述對存儲資源的請求。
優(yōu)先級為
- pvc(1Gi)= pv(1Gi)
- 同等容量的pv沒有了則申請比自己大即可 pvc(1Gi)= pv(2Gi)
- 如果沒有比自己大的,也沒有相等的即失敗
3.狀態(tài)(status)
- Bound(已綁定):表示PVC已經(jīng)綁定PV。
創(chuàng)建PVC后一直綁定不了PV的原因
- ①PVC的空間申請大小比PV的空間要大。
- ②PVC的storageClassName和PV的storageClassName不一致。
- ③PVC的accessModes和PV的accessModes不一致。
上面 pv 申請pv1:1g pv2:2g pv3:3g
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc1
namespace: study
spec:
accessModes: # 訪客模式
- ReadWriteMany # 和pv一樣
resources: # 請求空間
requests:
storage: 1Gi #申請pv1
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc2
namespace: study
spec:
accessModes: # 訪客模式
- ReadWriteMany
resources: # 請求空間
requests:
storage: 1Gi # 上面pv1已經(jīng)被申請了沒有同等容量的pv,所以申請pv2(2G)
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc3
namespace: study
spec:
accessModes: # 訪客模式
- ReadWriteMany
resources: # 請求空間
requests:
storage: 5Gi # 沒有與這個相等也沒有比他大的,所以申請失敗[root@master k8s]# kubectl apply -f pvc.yaml persistentvolumeclaim/pvc1 created persistentvolumeclaim/pvc2 created persistentvolumeclaim/pvc3 created [root@master k8s]# kubectl get pvc -n study NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE pvc1 Bound pv1 1Gi RWX 54s pvc2 Bound pv2 2Gi RWX 54s pvc3 Pending 54s # Bound 被使用 # pvc3 沒找到存儲容量相等或大于所以一直在ping,可以另外創(chuàng)建新的pv [root@master k8s]# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pv1 1Gi RWX Retain Bound study/pvc1 23m pv2 2Gi RWX Retain Bound study/pvc2 23m pv3 3Gi RWX Retain Available 23m
創(chuàng)建pod連接 PVC
apiVersion: v1
kind: Pod
metadata:
name: pod1
namespace: study
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: # 這個字段是pvc的kind: PersistentVolumeClaim
claimName: pvc1
readOnly: false # 只讀
---
apiVersion: v1
kind: Pod
metadata:
name: pod2
namespace: study
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: pvc2
readOnly: false[root@master k8s]# kubectl apply -f pod-pvc.yaml pod/pod1 created pod/pod2 created [root@master k8s]# ls /root/data/pv1/out.txt /root/data/pv1/out.txt
刪除
[root@master k8s]# kubectl delete -f pod-pvc.yaml pod "pod1" deleted pod "pod2" deleted [root@master k8s]# kubectl get pvc -n study NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE pvc1 Bound pv1 1Gi RWX 123m pvc2 Bound pv2 2Gi RWX 123m pvc3 Pending 123m [root@master k8s]# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pv1 1Gi RWX Retain Bound study/pvc1 126m pv2 2Gi RWX Retain Bound study/pvc2 126m pv3 3Gi RWX Retain Available 126m [root@master k8s]# kubectl delete -f pvc.yaml persistentvolumeclaim "pvc1" deleted persistentvolumeclaim "pvc2" deleted persistentvolumeclaim "pvc3" deleted [root@master k8s]# kubectl get pv -n study NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pv1 1Gi RWX Retain Released study/pvc1 127m pv2 2Gi RWX Retain Released study/pvc2 127m pv3 3Gi RWX Retain Available 127m # persistentVolumeReclaimPolicy:Retain 所以要手動釋放,并重新聲明
persistentVolumeReclaimPolicy:Retain
將pv從Released 變?yōu)?Available
使用edit命令刪除pv中的pvc的綁定信息即可變?yōu)閍vailable
注意:先將資源拿出來備份,反正數(shù)據(jù)還沒備份就被pvc使用了
[root@master k8s]# kubectl get pv -n study NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pv1 1Gi RWX Retain Released study/pvc1 129m pv2 2Gi RWX Retain Released study/pvc2 129m pv3 3Gi RWX Retain Available 129m [root@master k8s]# kubectl edit pv pv2 # pv1
# 刪除下面字段
claimRef:
apiVersion: v1
kind: PersistentVolumeClaim
name: pvc2
namespace: study
resourceVersion: "849619"
uid: fac30e1d-90a5-4e8b-9092-e6b83fd55d62
persistentvolume/pv2 edited[root@master k8s]# kubectl get pv -n study NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pv1 1Gi RWX Retain Available 134m pv2 2Gi RWX Retain Available 134m pv3 3Gi RWX Retain Available 134m
問題
pv創(chuàng)建不會檢測nfs的路徑是否連上,哪怕nfs都沒有這個配置也連不上也能創(chuàng)建,不會報錯,甚至后續(xù)的pvc和pod連接pvc都不會報錯
動態(tài)供應(yīng)
- 靜態(tài)供應(yīng):集群管理員創(chuàng)建若干 PV 卷。這些卷對象帶有真實存儲的細(xì)節(jié)信息,并且對集群用戶可用(可見)。PV 卷對象存在于 Kubernetes API 中,可供用戶消費(使用)。
- 動態(tài)供應(yīng):集群自動根據(jù) PVC 創(chuàng)建出對應(yīng) PV 進(jìn)行使用


① 集群管理員預(yù)先創(chuàng)建存儲類(StorageClass)。
② 用戶創(chuàng)建使用存儲類的持久化存儲聲明(PVC:PersistentVolumeClaim)。
③ 存儲持久化聲明通知系統(tǒng),它需要一個持久化存儲(PV: PersistentVolume)。
④ 系統(tǒng)讀取存儲類的信息。
⑤ 系統(tǒng)基于存儲類的信息,在后臺自動創(chuàng)建 PVC 需要的 PV 。
⑥ 用戶創(chuàng)建一個使用 PVC 的 Pod 。
⑦ Pod 中的應(yīng)用通過 PVC 進(jìn)行數(shù)據(jù)的持久化。
⑧ PVC 使用 PV 進(jìn)行數(shù)據(jù)的最終持久化處理。
https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner
設(shè)置 NFS 動態(tài)供應(yīng)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-client
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner # 指定一個供應(yīng)商的名字
# or choose another name, 必須匹配 deployment 的 env PROVISIONER_NAME'
parameters:
archiveOnDelete: "false" # 刪除 PV 的時候,PV 中的內(nèi)容是否備份
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-client-provisioner
labels:
app: nfs-client-provisioner
namespace: default
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: nfs-client-provisioner
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner
containers:
- name: nfs-client-provisioner
image: ccr.ccs.tencentyun.com/gcr-containers/nfs-subdir-external-provisioner:v4.0.2
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: k8s-sigs.io/nfs-subdir-external-provisioner
- name: NFS_SERVER
value: 192.168.65.100 # NFS 服務(wù)器的地址
- name: NFS_PATH
value: /nfs/data # NFS 服務(wù)器的共享目錄
volumes:
- name: nfs-client-root
nfs:
server: 192.168.65.100
path: /nfs/data
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
namespace: default
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-client-provisioner-runner
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
namespace: default
roleRef:
kind: ClusterRole
name: nfs-client-provisioner-runner
apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
namespace: default
rules:
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
namespace: default
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
namespace: default
roleRef:
kind: Role
name: leader-locking-nfs-client-provisioner
apiGroup: rbac.authorization.k8s.io測試動態(tài)供應(yīng)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nginx-pvc
namespace: default
labels:
app: nginx-pvc
spec:
storageClassName: nfs-client # 注意此處
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi
---
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: default
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.20.2
resources:
limits:
cpu: 200m
memory: 500Mi
requests:
cpu: 100m
memory: 200Mi
ports:
- containerPort: 80
name: http
volumeMounts:
- name: localtime
mountPath: /etc/localtime
- name: html
mountPath: /usr/share/nginx/html/
volumes:
- name: localtime
hostPath:
path: /usr/share/zoneinfo/Asia/Shanghai
- name: html
persistentVolumeClaim:
claimName: nginx-pvc
readOnly: false
restartPolicy: Always設(shè)置 SC 為默認(rèn)驅(qū)動
kubectl patch storageclass <your-class-name> -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
kubectl patch storageclass nfs-client -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nginx-pvc
namespace: default
labels:
app: nginx-pvc
spec:
# storageClassName: nfs-client 不寫,就使用默認(rèn)的
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi
---
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: default
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.20.2
resources:
limits:
cpu: 200m
memory: 500Mi
requests:
cpu: 100m
memory: 200Mi
ports:
- containerPort: 80
name: http
volumeMounts:
- name: localtime
mountPath: /etc/localtime
- name: html
mountPath: /usr/share/nginx/html/
volumes:
- name: localtime
hostPath:
path: /usr/share/zoneinfo/Asia/Shanghai
- name: html
persistentVolumeClaim:
claimName: nginx-pvc
readOnly: false
restartPolicy: Always展望
目前,只需要運維人員部署好各種 storageclass,開發(fā)人員在使用的時候,創(chuàng)建 PVC 即可;但是,存儲系統(tǒng)太多太多,運維人員也未必會一一掌握,此時就需要 Rook 來統(tǒng)一管理了。

特殊存儲卷
ConfigMap
ConfigMap是一個比較特殊的存儲卷,它的主要作用是用來存儲配置信息的
創(chuàng)建configmap
apiVersion: v1
kind: ConfigMap
metadata:
name: configmap-test
namespace: study
data: # 下面都是要存儲的數(shù)據(jù)
info: # 會創(chuàng)建一個名為info文件,文件內(nèi)容如下
username:admin
password:123456
# 如果更新ConfigMap中的內(nèi)容,容器中的值也會動態(tài)更新
# 更改后重新 apply 或 使用 edit命令
# 注意:需要一定時間[root@master k8s]# kubectl apply -f configmap.yaml configmap/configmap-test created [root@master k8s]# kubectl get cm -n study NAME DATA AGE configmap-test 1 22s kube-root-ca.crt 1 4d21h
創(chuàng)建pod掛載configmap
apiVersion: v1
kind: Pod
metadata:
name: pod-configmap
namespace: study
spec:
containers:
- name: nginx
image: nginx:1.17.1
volumeMounts:
- mountPath: /configmap/config
name: config
volumes:
- name: config
configMap:
name: configmap-test # 綁定 configmap[root@master k8s]# kubectl apply -f configmap.yaml pod/pod-configmap created [root@master k8s]# kubectl get pod -n study NAME READY STATUS RESTARTS AGE pod-configmap 1/1 Running 0 11s [root@master k8s]# kubectl exec -it -n study pod-configmap -c nginx /bin/sh # ls /configmap/config info # cat /configmap/config/info username:admin password:123456
Secret
在kubernetes中,還存在一種和ConfigMap非常類似的對象,稱為Secret對象,它主要用來存儲敏感信息,例如密碼、密鑰、證書等等
Secret 是一個用于存儲敏感數(shù)據(jù)的資源,所有的數(shù)據(jù)要經(jīng)過base64編碼,數(shù)據(jù)實際會存儲在K8s中Etcd,然后通過創(chuàng)建Pod時引用該數(shù)據(jù)。
查詢 Secret 的時候是加密,在pod容器里面顯示的是解密后的內(nèi)容
Pod 使用 secret 數(shù)據(jù)有兩種方式:
- 變量注入
- 數(shù)據(jù)卷掛載
kubectl create secret 支持三種數(shù)據(jù)類型:
- docker--registry:存儲鏡像倉庫認(rèn)證信息
- generic:從文件、目錄或者字符串創(chuàng)建,例如存儲用戶名密碼
- ts:存儲證書,例如HTTPS證書
type字段
內(nèi)置類型 | 用法 |
Opaque | 用戶定義的任意數(shù)據(jù) |
kubernetes.io/service-account-token | 服務(wù)賬號令牌 |
kubernetes.io/dockercfg | ~/.dockercfg 文件的序列化形式 |
kubernetes.io/dockerconfigjson | ~/.docker/config.json 文件的序列化形式 |
kubernetes.io/basic-auth | 用于基本身份認(rèn)證的憑據(jù) |
kubernetes.io/ssh-auth | 用于 SSH 身份認(rèn)證的憑據(jù) |
kubernetes.io/tls | 用于 TLS 客戶端或者服務(wù)器端的數(shù)據(jù) |
bootstrap.kubernetes.io/token | 啟動引導(dǎo)令牌數(shù)據(jù) |
yaml 解析
# 準(zhǔn)備base64加密數(shù)據(jù) echo -n "admin" | base64 echo -n "123456" | base64
apiVersion: v1 kind: Secret metadata: name: secret namespace: dev type: Opaque data: # 下面存存儲敏感信息,已base64加密 # 這里 username 是一個文件名,這里 password 是一個文件名 username: YWRtaW4= password: MTIzNDU2 # mysql-root-password:"MTIzNDU2" stringData: #如果不手動進(jìn)行編碼加密,可以將這個操作讓k8s來做,使用stringdata username: admin password: "123456" # 不能直接寫數(shù)字,不然會報錯 cannot convert int64 to string,如果要寫數(shù)字就加""
注:如果同時使用data和stringData,那么data會被忽略
[root@master k8s]# kubectl apply -f secret.yaml secret/secret created [root@master k8s]# kubectl describe secrets -n study Name: secret Namespace: study Labels: <none> Annotations: <none> Type: Opaque Data ==== password: 6 bytes username: 5 bytes
數(shù)據(jù)卷掛載
apiVersion: v1
kind: Pod
metadata:
name: pod-secret
namespace: study
spec:
containers:
- name: nginx
image: nginx:1.17.1
volumeMounts:
- mountPath: /secret/config
name: secret-config
volumes:
- name: secret-config
secret:
secretName: secretkubectl exec -it -n study pod-secret -c nginx /bin/sh # cat /secret/config/password 123456
解碼 Secret
kubectl get secret db-user-pass -o jsonpath='{.data}' | base64變量注入
示例:將Mysql用戶密碼保存到Secret中存儲
[root@master cks]# echo -n "123456" | base64 MTIzNDU2
apiVersion: v1
kind: Secret
metadata:
name: mysql
type: Opaque
data:
mysql-root-password: "MTIzNDU2"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
spec:
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql-db
image: mysql:5.7.30
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql
key: mysql-root-password將 secret 的mysql中的mysql-root-passwd變量注入到 容器中的 MYSQL_ROOT_PASSWORD 變量
[root@master cks]# kubectl exec -it mysql-6c8b6c4d74-vbflz /bin/sh # mysql -uroot -p123456 mysql: [Warning] Using a password on the command line interface can be insecure. Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 4 Server version: 5.7.30 MySQL Community Server (GPL) Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql>
鏡像拉取密碼
imagePullSecret:Pod拉取私有鏡像倉庫的時使用的賬戶密碼,會傳遞給kubelet,然后kubelet就可以拉取有密碼的倉庫里面的鏡像
kubectl create secret docker-registry docker-harbor-registrykey --docker-server=192.168.18.119:85 \
--docker-username=admin --docker-password=Harbor12345 \
--docker-email=1900919313@qq.com
apiVersion: v1
kind: Pod
metadata:
name: redis
spec:
containers:
- name: redis
image: 192.168.18.119:85/yuncloud/redis # 這是Harbor的鏡像私有倉庫地址
imagePullSecrets:
- name: docker-harbor-registrykey總結(jié)以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
訪問k8s集群部署的微服務(wù)內(nèi)部服務(wù)接口問題
文章介紹了在Kubernetes集群中訪問微服務(wù)內(nèi)部服務(wù)接口的幾種方法,包括通過Pod的localhost訪問、使用服務(wù)名和DNS解析、以及通過注冊中心的地址訪問2026-02-02
詳解Kubernetes 中容器跨主機(jī)網(wǎng)絡(luò)
這篇文章主要為大家介紹了Kubernetes中容器跨主機(jī)網(wǎng)絡(luò)是怎么樣的,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04
kubernetes?k8s?CRD自定義資源學(xué)習(xí)筆記
這篇文章主要介紹了kubernetes?k8s?CRD自定義資源學(xué)習(xí)筆記,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05
Kubernetes刪除ns實現(xiàn)方式(namespace)
文章介紹了在Kubernetes中刪除無法正常刪除的命名空間(namespace)的幾種方法,首先,通過查看和修改命名空間的JSON文件來刪除`finalizers`,然后使用`kubectl proxy`命令來繞過Kubernetes的安全機(jī)制,最終成功刪除了命名空間2026-01-01
k8s:pod has unbound PersistentVolumeClaims問題及
部署redis-ha時,Pod因PVC未綁定報錯,原因在于value.yaml中storageClassName為空,且未啟用DefaultDefaultStorageClass,解決方法是手動指定PVC的StorageClassName為現(xiàn)有存儲類,確保PV分配成功2025-07-07

