volume
我們經(jīng)常會(huì)說(shuō):容器和 Pod 是短暫的。
其含義是它們的生命周期可能很短,會(huì)被頻繁地銷毀和創(chuàng)建。容器銷毀時(shí),保存在容器內(nèi)部文件系統(tǒng)中的數(shù)據(jù)都會(huì)被清除。
為了持久化保存容器的數(shù)據(jù),可以使用 Kubernetes Volume。
Volume 的生命周期獨(dú)立于容器,Pod 中的容器可能被銷毀和重建,但 Volume 會(huì)被保留。
本質(zhì)上,Kubernetes Volume 是一個(gè)目錄,這一點(diǎn)與 Docker Volume 類似。當(dāng) Volume 被 mount 到 Pod,Pod 中的所有容器都可以訪問(wèn)這個(gè) Volume。Kubernetes Volume 也支持多種 backend 類型,包括 emptyDir、hostPath、GCE Persistent Disk、AWS Elastic Block Store、NFS、Ceph 等,完整列表可參考 https://kubernetes.io/docs/concepts/storage/volumes/#types-of-volumes
Volume 提供了對(duì)各種 backend 的抽象,容器在使用 Volume 讀寫數(shù)據(jù)的時(shí)候不需要關(guān)心數(shù)據(jù)到底是存放在本地節(jié)點(diǎn)的文件系統(tǒng)中呢還是云硬盤上。對(duì)它來(lái)說(shuō),所有類型的 Volume 都只是一個(gè)目錄。
emptyDir
emptyDir 是最基礎(chǔ)的 Volume 類型。一個(gè) emptyDir Volume 是 Host 上的一個(gè)空目錄。
emptyDir Volume 對(duì)于容器來(lái)說(shuō)是持久的,對(duì)于 Pod 則不是。當(dāng) Pod 從節(jié)點(diǎn)刪除時(shí),Volume 的內(nèi)容也會(huì)被刪除。但如果只是容器被銷毀而 Pod 還在,則 Volume 不受影響。
也就是說(shuō):emptyDir Volume 的生命周期與 Pod 一致。
Pod 中的所有容器都可以共享 Volume,它們可以指定各自的 mount 路徑。
下面通過(guò)例子來(lái)實(shí)踐 emptyDir
第一步:配置文件如下:

這里我們模擬了一個(gè) producer-consumer 場(chǎng)景。Pod 有兩個(gè)容器 producer和 consumer,它們共享一個(gè) Volume。producer 負(fù)責(zé)往 Volume中寫數(shù)據(jù),consumer 則是從 Volume 讀取數(shù)據(jù)。
① 文件最底部 volumes 定義了一個(gè) emptyDir 類型的 Volume shared-volume。
② producer 容器將 shared-volume mount 到 /producer_dir 目錄。
③ producer 通過(guò) echo 將數(shù)據(jù)寫到文件 hello 里。
④ consumer 容器將 shared-volume mount 到 /consumer_dir 目錄。
⑤ consumer 通過(guò) cat 從文件 hello 讀數(shù)據(jù)。
第二步:執(zhí)行如下命令創(chuàng)建 Pod:
[root@ken ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
producer-consumer 2/2 Running 0 2m1s
[root@ken ~]# kubectl logs producer-consumer consumer
hello world
注意:一個(gè)pod里面運(yùn)行多個(gè)容器的時(shí)候,查看容器日志,需要指定pod名稱及容器名
kubectl logs 顯示容器 consumer 成功讀到了 producer 寫入的數(shù)據(jù),驗(yàn)證了兩個(gè)容器共享 emptyDir Volume。
因?yàn)?emptyDir 是 Docker Host 文件系統(tǒng)里的目錄,其效果相當(dāng)于執(zhí)行了 docker run -v /producer_dir 和 docker run -v /consumer_dir。
第三步:通過(guò) docker inspect 查看容器的詳細(xì)配置信息,我們發(fā)現(xiàn)兩個(gè)容器都 mount 了同一個(gè)目錄:


這里 /var/lib/kubelet/pods/4dcc0366-245f-11e9-9172-000c292d5bb8/volumes/kubernetes.io~empty-dir/shared-volume 就是 emptyDir在 Host 上的真正路徑。
emptyDir 是 Host 上創(chuàng)建的臨時(shí)目錄,其優(yōu)點(diǎn)是能夠方便地為 Pod 中的容器提供共享存儲(chǔ),不需要額外的配置。但它不具備持久性,如果 Pod 不存在了,emptyDir 也就沒(méi)有了。根據(jù)這個(gè)特性,emptyDir 特別適合 Pod 中的容器需要臨時(shí)共享存儲(chǔ)空間的場(chǎng)景,比如前面的生產(chǎn)者消費(fèi)者用例。
hostpath
hostPath Volume 的作用是將 Docker Host 文件系統(tǒng)中已經(jīng)存在的目錄 mount 給 Pod 的容器。大部分應(yīng)用都不會(huì)使用 hostPath Volume,因?yàn)檫@實(shí)際上增加了 Pod 與節(jié)點(diǎn)的耦合,限制了 Pod 的使用。不過(guò)那些需要訪問(wèn) Kubernetes 或 Docker 內(nèi)部數(shù)據(jù)(配置文件和二進(jìn)制庫(kù))的應(yīng)用則需要使用 hostPath。
比如 kube-apiserver 和 kube-controller-manager 就是這樣的應(yīng)用,通過(guò)
kubectl edit –namespace=kube-system pod kube-apiserver-k8s-master
查看 kube-apiserver Pod 的配置,下面是 Volume 的相關(guān)部分:
**
這里定義了三個(gè) hostPath volume k8s、certs 和 pki,分別對(duì)應(yīng) Host 目錄 /etc/kubernetes、/etc/ssl/certs 和 /etc/pki。
如果 Pod 被銷毀了,hostPath 對(duì)應(yīng)的目錄也還會(huì)被保留,從這點(diǎn)看,hostPath 的持久性比 emptyDir 強(qiáng)。不過(guò)一旦 Host 崩潰,hostPath 也就沒(méi)法訪問(wèn)了。
示例:把節(jié)點(diǎn)上的tmp目錄掛載到容器中
第一步:編寫yml文件
[root@ken-node1 ~]# cat hostpath.yml
apiVersion: v1
kind: Pod
metadata:
name: myhostpath
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- name: hostpath
mountPath: /usr/share/nginx/html
volumes:
- name: hostpath
hostPath:
path: /tmp
type: Directory
啟動(dòng)nginx,把主機(jī)的/tmp目錄掛載到nginx容器的網(wǎng)站根目錄
這里共享的是tmp目錄,如果是掛載其他的目錄要確保節(jié)點(diǎn)有這個(gè)目錄
第二步:?jiǎn)?dòng)
[root@ken-node1 ~]# kubectl apply -f hostpath.yml
第三步:在tmp目錄下編寫測(cè)試文件
[root@ken-node1 ~]# echo "hostpath" > /tmp/index.html
第四步:查看podIP并嘗試訪問(wèn)
[root@ken-node1 ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READIN
myhostpath 1/1 Running 0 8m8s 10.244.1.59 ken-node2 <none> <none>
第五步:訪問(wèn)
[root@ken-node1 ~]# curl 10.244.1.59
hostpath
對(duì)接NFS
第一步:在控制節(jié)點(diǎn)部署NFS
[root@ken-node1 ~]# yum install nfs-utils rpcbind -y
[root@ken-node1 ~]# rm -rf /ken
[root@ken-node1 ~]# mkdir /ken
[root@ken-node1 ~]# chown -R nfsnobody: /ken
[root@ken-node1 ~]# echo "/ken *(rw)" > /etc/exports
[root@ken-node1 ~]# systemctl restart nfs rpcbind
第二步:編寫yml文件
[root@ken-node1 ~]# cat nfs.yml
apiVersion: v1
kind: Pod
metadata:
name: nfs
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- name: nfsback
mountPath: /usr/share/nginx/html
volumes:
- name: nfsback
nfs:
path: /ken
server: 192.168.163.132
第三步:節(jié)點(diǎn)需要下載nfs-utils去支持nfs文件類型,否則會(huì)報(bào)錯(cuò)
[root@ken-node2 ~]# yum install nfs-utils -y
第四步:執(zhí)行yml文件
[root@ken-node1 ~]# kubectl apply -f nfs.yml
第五步:查看pod的IP
[root@ken-node1 ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nfs 1/1 Running 0 3m3s 10.244.2.61 ken-node3 <none> <none>
第六步:編寫測(cè)試文件在/ken共享目錄下
[root@ken-node1 ~]# echo "nfs" > /ken/index.html
第七步: 訪問(wèn)測(cè)試
[root@ken-node1 ~]# curl 10.244.2.61
nfs
PV & PVC
PersistentVolume (PV) 是外部存儲(chǔ)系統(tǒng)中的一塊存儲(chǔ)空間,由管理員創(chuàng)建和維護(hù)。與 Volume 一樣,PV 具有持久性,生命周期獨(dú)立于 Pod。
PersistentVolumeClaim (PVC) 是對(duì) PV 的申請(qǐng) (Claim)。PVC 通常由普通用戶創(chuàng)建和維護(hù)。需要為 Pod 分配存儲(chǔ)資源時(shí),用戶可以創(chuàng)建一個(gè) PVC,指明存儲(chǔ)資源的容量大小和訪問(wèn)模式(比如只讀)等信息,Kubernetes 會(huì)查找并提供滿足條件的 PV。
有了 PersistentVolumeClaim,用戶只需要告訴 Kubernetes 需要什么樣的存儲(chǔ)資源,而不必關(guān)心真正的空間從哪里分配,如何訪問(wèn)等底層細(xì)節(jié)信息。這些 Storage Provider 的底層信息交給管理員來(lái)處理,只有管理員才應(yīng)該關(guān)心創(chuàng)建 PersistentVolume 的細(xì)節(jié)信息。
Kubernetes 支持多種類型的 PersistentVolume,比如 AWS EBS、Ceph、NFS 等,完整列表請(qǐng)參考 https://kubernetes.io/docs/concepts/storage/persistent-volumes/#types-of-persistent-volumes
NFS PV
第一步:需在 k8s-master 節(jié)點(diǎn)上搭建了一個(gè) NFS 服務(wù)器,目錄為 /nfsdata:
要更改目錄屬主,否則沒(méi)有寫入權(quán)限: chown -R nfsnobody.nfsnobody /nfsdata
注意:
- nfs可以安裝在任意節(jié)點(diǎn),不一定是在k8s集群當(dāng)中的一臺(tái)服務(wù)器
- k8s集群內(nèi)的每臺(tái)節(jié)點(diǎn)必須都要安裝nfs-utils否則識(shí)別不到nfs類型
[root@ken ~]# showmount -e
Export list for ken:
/nfsdata *
第二步:創(chuàng)建一個(gè) PV mypv1,配置文件 nfs-pv1.yml 如下:
[root@ken ~]# cat nfs-pv1.yml
apiVersion: v1
kind: PersistentVolume
metadata:
name: mypv1
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
nfs:
path: /nfsdata/pv1 #需要?jiǎng)?chuàng)建pv1目錄,否則pod起不來(lái)
server: 172.20.10.2
① capacity 指定 PV 的容量為 1G。
② accessModes 指定訪問(wèn)模式為 ReadWriteOnce,支持的訪問(wèn)模式有:
ReadWriteOnce – PV 能以 read-write 模式 mount 到單個(gè)節(jié)點(diǎn)。
ReadOnlyMany – PV 能以 read-only 模式 mount 到多個(gè)節(jié)點(diǎn)。
ReadWriteMany – PV 能以 read-write 模式 mount 到多個(gè)節(jié)點(diǎn)。
③ persistentVolumeReclaimPolicy 指定當(dāng) PV 的回收策略為 Recycle,支持的策略有:
Retain – 需要管理員手工回收。
Recycle – 清除 PV 中的數(shù)據(jù),效果相當(dāng)于執(zhí)行 rm -rf /thevolume/*。
Delete – 刪除 Storage Provider 上的對(duì)應(yīng)存儲(chǔ)資源,例如 AWS EBS、GCE PD、Azure Disk、OpenStack Cinder Volume 等。
④ storageClassName 指定 PV 的 class 為 nfs。相當(dāng)于為 PV 設(shè)置了一個(gè)分類,PVC 可以指定 class 申請(qǐng)相應(yīng) class 的 PV。
⑤ 指定 PV 在 NFS 服務(wù)器上對(duì)應(yīng)的目錄。
第三步:創(chuàng)建 mypv1:
[root@ken ~]# kubectl apply -f nfs-pv1.yml
persistentvolume/mypv1 created
第四步:查看pv
[root@ken ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
mypv1 1Gi RWO Recycle Available nfs 48s
STATUS 為 Available,表示 mypv1 就緒,可以被 PVC 申請(qǐng)。
第五步:接下來(lái)創(chuàng)建 PVC mypvc1,配置文件 nfs-pvc1.yml 如下:
注意:
1. 各個(gè)節(jié)點(diǎn)都需要下載nfs-utils
[root@ken-node1 ~]# cat pvc.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mypvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
volumeName: mypv #pv名字
PVC 就很簡(jiǎn)單了,只需要指定 PV 的容量,訪問(wèn)模式和 class。
[root@ken ~]# kubectl apply -f nfs-pvc1.yml
persistentvolumeclaim/mypvc1 created
第六步:查看pvc
[root@ken ~]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mypvc1 Bound mypv1 1Gi RWO nfs 112s
[root@ken ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
mypv1 1Gi RWO Recycle Bound default/mypvc1 nfs 6m5s
從 kubectl get pvc 和 kubectl get pv 的輸出可以看到 mypvc1 已經(jīng) Bound 到 mypv1,申請(qǐng)成功。
第七步:接下來(lái)就可以在 Pod 中使用存儲(chǔ)了,Pod 配置文件 pod1.yml 如下:

與使用普通 Volume 的格式類似,在 volumes 中通過(guò) persistentVolumeClaim 指定使用 mypvc1 申請(qǐng)的 Volume。
第八步:創(chuàng)建 mypod1:
[root@ken ~]# kubectl apply -f pod1.yml
pod/mypod1 created
[root@ken ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mypod1 1/1 Running 0 51s 10.244.1.59 host1 <none> <none>
第九步:驗(yàn)證 PV 是否可用:
[root@ken ~]# kubectl exec mypod1 touch /mydata/hello
[root@ken ~]# ls /nfsdata/pv1/
hello
可見(jiàn),在 Pod 中創(chuàng)建的文件 /mydata/hello 確實(shí)已經(jīng)保存到了 NFS 服務(wù)器目錄 /nfsdata/pv1 中。
回收PV
當(dāng) PV 不再需要時(shí),可通過(guò)刪除 PVC 回收。
當(dāng) PVC mypvc1 被刪除后,Kubernetes 啟動(dòng)了一個(gè)新 Pod recycler-for-mypv1,這個(gè) Pod 的作用就是清除 PV mypv1 的數(shù)據(jù)。此時(shí) mypv1 的狀態(tài)為 Released,表示已經(jīng)解除了與 mypvc1 的 Bound,正在清除數(shù)據(jù),不過(guò)此時(shí)還不可用。
當(dāng)數(shù)據(jù)清除完畢,mypv1 的狀態(tài)重新變?yōu)?Available,此時(shí)則可以被新的 PVC 申請(qǐng)。
因?yàn)?PV 的回收策略設(shè)置為 Recycle,所以數(shù)據(jù)會(huì)被清除,但這可能不是我們想要的結(jié)果。如果我們希望保留數(shù)據(jù),可以將策略設(shè)置為 Retain。
第一步:將策略設(shè)置為 Retain。
[root@ken ~]# cat nfs-pv1.yml
apiVersion: v1
kind: PersistentVolume
metadata:
name: mypv1
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs
nfs:
path: /nfsdata/pv1
server: 172.20.10.2
第二步:通過(guò) kubectl apply 更新 PV:
root@ken ~]# kubectl apply -f nfs-pv1.yml
persistentvolume/mypv1 configured
[root@ken ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
mypv1 1Gi RWO Retain Available nfs 65m
回收策略已經(jīng)變?yōu)?Retain,通過(guò)下面步驟驗(yàn)證其效果:
① 重新創(chuàng)建 mypvc1。
② 在 mypv1 中創(chuàng)建文件 hello。
③ mypv1 狀態(tài)變?yōu)?Released。
④ Kubernetes 并沒(méi)有啟動(dòng) Pod recycler-for-mypv1。
⑤ PV 中的數(shù)據(jù)被完整保留。
另外使用retain雖然刪除pod后的數(shù)據(jù)得到了保留,但其 PV 狀態(tài)會(huì)一直處于 Released,不能被其他 PVC 申請(qǐng)。為了重新使用存儲(chǔ)資源,可以刪除并重新創(chuàng)建 PV。刪除操作只是刪除了 PV 對(duì)象,存儲(chǔ)空間中的數(shù)據(jù)并不會(huì)被刪除。
新建的 mypv1 狀態(tài)為 Available,已經(jīng)可以被 PVC 申請(qǐng)。
小結(jié)
本章我們討論了 Kubernetes 如何管理存儲(chǔ)資源。
emptyDir 和 hostPath 類型的 Volume 很方便,但可持久性不強(qiáng),Kubernetes 支持多種外部存儲(chǔ)系統(tǒng)的 Volume。
PV 和 PVC 分離了管理員和普通用戶的職責(zé),更適合生產(chǎn)環(huán)境。
文章轉(zhuǎn)自: http://www.kendd.cn/?p=718









