上節課我們學習了 PV 的使用,但是在我們真正使用的時候是使用的 PVC,就類似於我們的服務是通過 Pod 來運行的,而不是 Node,只是 Pod 跑在 Node 上而已,所以這節課我們就來給大家講解下 PVC 的使用方法。
准備工作
在使用 PVC 之前,我們還得把其他節點上的 nfs 客戶端給安裝上,比如我們這里:
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
master Ready master 61d v1.10.0
node01 Ready <none> 61d v1.10.0 node03 Ready <none> 41d v1.10.0
我們需要在所有節點安裝 nfs 客戶端程序,安裝方法和上節課的安裝方法一樣的。必須在所有節點都安裝 nfs 客戶端,否則可能會導致 PV 掛載不上的問題
新建 PVC
同樣的,我們來新建一個數據卷聲明,我們來請求 1Gi 的存儲容量,訪問模式也是 ReadWriteOnce,YAML 文件如下:(pvc-nfs.yaml)
kind: PersistentVolumeClaim apiVersion: v1 metadata: name: pvc-nfs spec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi
我們可以看到我們這里的聲明方法幾乎和新建 PV 是一樣的,在新建 PVC 之前,我們可以看下之前創建的 PV 的狀態:
kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pv-nfs 1Gi RWO Recycle Available 19m
我們可以看到當前 pv-nfs 是在 Available 的一個狀態,所以這個時候我們的 PVC 可以和這個 PV 進行綁定:
$ kubectl create -f pvc-nfs.yaml
persistentvolumeclaim "pvc-nfs" created $ kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE pvc-nfs Bound pv-nfs 1Gi RWO 12s
我們可以看到 pvc-nfs 創建成功了,狀態是 Bound 狀態了,這個時候我們再看下 PV 的狀態呢:
$ kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pv-nfs 1Gi RWO Recycle Bound default/pvc-nfs 23m
同樣我們可以看到 PV 也是 Bound 狀態了,對應的聲明是 default/pvc-nfs,就是 default 命名空間下面的 pvc-nfs,證明我們剛剛新建的 pvc-nfs 和我們的 pv-nfs 綁定成功了。
有的同學可能會覺得很奇怪,我們並沒有在 pvc-nfs 中指定關於 pv 的什么標志,它們之間是怎么就關聯起來了的呢?其實這是系統自動幫我們去匹配的,他會根據我們的聲明要求去查找處於 Available 狀態的 PV,如果沒有找到的話那么我們的 PVC 就會一直處於 Pending 狀態,找到了的話當然就會把當前的 PVC 和目標 PV 進行綁定,這個時候狀態就會變成 Bound 狀態了。比如我們新建一個 PVC,如下:(pvc2-nfs.yaml)
kind: PersistentVolumeClaim apiVersion: v1 metadata: name: pvc2-nfs spec: accessModes: - ReadWriteOnce resources: requests: storage: 2Gi selector: matchLabels: app: nfs
我們這里聲明一個 PV 資源的請求,邀請訪問模式是 ReadWriteOnce,存儲容量是 2Gi,最后我們還要求匹配具有標簽 app=nfs 的 PV,這樣要求的 PV 有嗎?我們先查看下當前系統的所有 PV:
$ kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pv-nfs 1Gi RWO Recycle Bound default/pvc-nfs 43m pv001 1Gi RWO Recycle Bound default/www-web-0 13d pv002 1Gi RWO Recycle Bound default/www-web-1 13d
都是 Bound 狀態,並沒有 Available 狀態的 PV,所以我們可以想象到我們上面新建的 PVC 是沒辦法選擇到合適的 PV 的,我們創建一下看看:
$ kubectl create -f pvc2-nfs.yaml
persistentvolumeclaim "pvc2-nfs" created $ kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE pvc-nfs Bound pv-nfs 1Gi RWO 23m pvc2-nfs Pending 14s
很顯然是 Pending 狀態,因為並沒有合適的 PV 給你使用,現在我們來新建一個 PV,讓上面的 PVC 有合適的 PV 使用:(pv2-nfs.yaml)
apiVersion: v1 kind: PersistentVolume metadata: name: pv2-nfs labels: app: nfs spec: capacity: storage: 2Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Recycle nfs: server: 10.151.30.57 path: /data/k8s
我們這里新建一個名為 pv2-nfs 的 PV,具有標簽 app=nfs,容量也是 2Gi,訪問模式是 ReadWraiteOnce,看上去這一切都很適合上面的 PVC,新建試一試:
$ kubectl create -f pv2-nfs.yaml
persistentvolume "pv2-nfs" created $ kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pv-nfs 1Gi RWO Recycle Bound default/pvc-nfs 51m pv2-nfs 2Gi RWO Recycle Bound default/pvc2-nfs 12s
創建完 pv2-nfs 后,是不是很快就發現該 PV 是 Bound 狀態了,對應的 PVC 是 default/pvc2-nfs,證明上面的 pvc2-nfs 終於找到合適的 PV 進行綁定上了:
$ kubectl get pvc
kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
pvc-nfs Bound pv-nfs 1Gi RWO 30m
pvc2-nfs Bound pv2-nfs 2Gi RWO 7m
成功了,對吧!有的同學可能又會說了,我們的 pv2-nfs 聲明的容量是 2Gi,如果我 pvc2-nfs 這里聲明的容量是 1Gi 的話呢?還能正常綁定嗎?如果可以正常綁定的話,那剩下的 1Gi 容量還能使用嗎?其實我也不清楚,怎么辦?我們去實際測試下就知道了吧,先刪除上面的 pvc2-nfs,然后我們把該 PVC 里面的容量改成 1Gi,再新建試一試呢:
$ kubectl delete pvc pvc2-nfs
persistentvolumeclaim "pvc2-nfs" deleted $ cat pvc2-nfs.yaml ... resources: requests: storage: 1Gi ... $ kubectl create -f pvc2-nfs.yaml persistentvolumeclaim "pvc2-nfs" created $ kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE pvc2-nfs Bound pv2-nfs 2Gi RWO 7s
我們可以看到上面的 PVC 依然可以正常的綁定,仔細看 CAPACITY 這一列的數據:2Gi,也就是說我們聲明的 1Gi 是沒什么用的,我 PV 是 2Gi,你這里聲明 1Gi 是不行的,你必須得使用 2Gi。
如果我們這里容量聲明是 3Gi 呢?還可以正常綁定嗎?大家可以思考一下,如果聲明的容量大於了 PV 里面的容量的話,是沒辦法進行綁定的,大家可以下去自己測試一下。
使用 PVC
上面我們已經知道怎么創建 PV 和 PVC 了,現在我們就來使用下我們的 PVC,這里我們同樣使用之前的 nginx 的鏡像來測試下:(nfs-pvc-deploy.yaml)
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: nfs-pvc spec: replicas: 3 template: metadata: labels: app: nfs-pvc spec: containers: - name: nginx image: nginx:1.7.9 imagePullPolicy: IfNotPresent ports: - containerPort: 80 name: web volumeMounts: - name: www mountPath: /usr/share/nginx/html volumes: - name: www persistentVolumeClaim: claimName: pvc2-nfs --- apiVersion: v1 kind: Service metadata: name: nfs-pvc labels: app: nfs-pvc spec: type: NodePort ports: - port: 80 targetPort: web selector: app: nfs-pvc
我們這里使用 nginx 鏡像,將容器的 /usr/share/nginx/html 目錄通過 volume 掛載到名為 pvc2-nfs 的 PVC 上面,然后創建一個 NodePort 類型的 Service 來暴露服務:
$ kubectl create -f nfs-pvc-deploy.yaml
deployment.extensions "nfs-pvc" created service "nfs-pvc" created $ kubectl get pods kubectl get pods NAME READY STATUS RESTARTS AGE ... nfs-pvc-57c9945bd9-5r4r6 1/1 Running 0 19s nfs-pvc-57c9945bd9-gz6p9 1/1 Running 0 19s nfs-pvc-57c9945bd9-x6mvc 1/1 Running 0 19s ... $ kubectl get svc kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ... nfs-pvc NodePort 10.98.246.155 <none> 80:30769/TCP 1m ...
然后我們就可以通過任意節點的 IP:30769 端口來訪問我們這里的 Nginx 服務了,但是這個時候我們來訪問會出現403,這是為什么?我們再去看看 nfs 共享數據目錄下面有沒有數據呢?
nginx 403
$ ls /data/k8s
我們發現並沒有任何數據,這是因為我們把容器目錄/user/share/nginx/html和掛載到了pvc2-nfs這個 PVC 上面,這個 PVC 就是對應着我們上面的 nfs 的共享數據目錄的,該目錄下面還沒有任何數據,所以我們訪問就出現了403,現在我們在/data/k8s這個目錄下面新建一個 index.html 的文件:
$ echo "<h1>Hello Kubernetes~</h1>" >> /data/k8s/index.html $ ls /data/k8s/ index.html
我們可以看到共享數據目錄中已經有一個 index.html 的文件了,由於我們掛載了 pvc2-nfs 到上面的 nginx 容器中去,是不是這個時候容器目錄/user/share/nginx/html下面也有index.html這個文件了啊?所以這個時候我們再來訪問下服務,任一節點IP:30769:
nginx 200
現在是不是正常了啊,但是我們可以看到我們容器中的數據是直接放到共享數據目錄根目錄下面的,如果以后我們又有一個新的 nginx 容器也做了數據目錄的掛載,是不是就會有沖突了啊,所以這個時候就不太好區分了,這個時候我們可以在 Pod 中使用一個新的屬性:subPath,該屬性可以來解決這個問題,我們只需要更改上面的 Pod 的 YAML 文件即可:
... volumeMounts: - name: www subPath: nginxpvc-test mountPath: /usr/share/nginx/html ...
更改完 YAML 文件后,我們重新更新即可:
$ kubectl apply -f nfs-pvc-deploy.yaml
Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply
deployment.extensions "nfs-pvc" configured Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply service "nfs-pvc" configured
更新完后,我們再去看看 nfs 的數據共享目錄:
$ ls /data/k8s/ index.html nginxpvc-test $ ls /data/k8s/nginxpvc-test/
我們可以預想到現在我們訪問上面的服務,是不是又會得到403的結果啊,因為nginxpvc-test目錄下面還沒有任何文件呢,我們把根目錄下面的 index.html 文件移動到 nginxpvc-test 目錄下面去是不是又可以訪問了:
$ mv /data/k8s/index.html /data/k8s/nginxpvc-test/
現在快去驗證下吧,看看能不能得到正確結果。
到這里我們就算完整的使用了一次 PVC 了,現在我們再來驗證下我們的數據是否會丟失,怎么驗證?首先我們把上面的 Deployment 刪除掉,這樣是不是他下面管理的3個 Pod 也會被一起刪除掉啊:
$ kubectl delete deployment nfs-pvc
deployment.extensions "nfs-pvc" deleted
Deployment 被刪除掉了,但是 nfs 的數據共享目錄下面的數據呢?
$ ls /data/k8s/nginxpvc-test/ index.html
還在吧?當然了如果不在了,我們用他就沒有任何意義了吧,現在我們再來重新創建上面的 Deployment,看看訪問服務還能得到上面的正常輸出結果嗎:
$ kubectl create -f nfs-pvc-deploy.yaml
deployment.extensions "nfs-pvc" created Error from server (AlreadyExists): error when creating "nfs-pvc-deploy.yaml": services "nfs-pvc" already exists
可以看到 nfs-pvc 這個 Deployment 創建成功了,由於 Service 我們之前沒有刪除掉,所以這里提示已經存在,我們忽略就可以了,現在同樣我們用任一節點 IP:30769 來訪問我們這里的服務,是不是依然可以在頁面上看到Hello Kubernetes~這里的輸出信息啊,這證明我們的數據持久化是成功的吧!
注意事項
上面我們演示了數據持久化,如果這個時候我們把 PV 給刪除了,上面持久化的數據還會存在嗎?如果是刪除的 PVC 呢?在實際使用的工程中,是很有可能出現這種情況的吧?下面我們來實際驗證下。
我們先刪除上面使用的 PV:
$ kubectl delete pv pv2-nfs persistentvolume "pv2-nfs" deleted
然后再看看之前創建的 PVC 還會存在嗎:
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
... pvc2-nfs Bound pv2-nfs 2Gi RWO 1h ...
是不是覺得很奇怪,pvc2-nfs 仍然是 Bound 的狀態,也就意外着我們還可以正常使用這個 PVC,但是如果我們有一個新的 Pod 來使用這個 PVC 會是怎樣的情況呢?大家下去自己驗證下
如有 Pod 正在使用此 pvc2-nfs 這個 PVC 的話,那么新建的 Pod 則仍可使用,如無 Pod 使用,則創建 Pod 掛載此 PVC 時會出現失敗。大家自己去驗證下吧
現在我們在恢復到最開始的狀態,把 PV 和 PVC 添加回來,如果現在我們把使用 pvc2-nfs 關聯的 Pod 都刪除,然后再刪除該 PVC 的話,那么我們的持久化數據還存在嗎?
$ kubectl delete -f nfs-pvc-deploy.yaml
deployment.extensions "nfs-pvc" deleted service "nfs-pvc" deleted $ kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE ... pvc2-nfs Bound pv2-nfs 2Gi RWO 5m ... $ kubectl delete pvc pvc2-nfs persistentvolumeclaim "pvc2-nfs" deleted $ kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE ... pv2-nfs 2Gi RWO Recycle Released default/pvc2-nfs 6m ... $ ls /data/k8s/
我們可以看到 pv2-nfs 這個 PV 的狀態已經變成了 Released 狀態了,這個狀態是不是表示 PVC 已經被釋放了,現在可以被重新綁定了,由於我們設置的 PV 的回收策略是 Recycle,所以我們可以很明顯的發現 nfs 的共享數據目錄下面已經沒有了數據了,這是因為我們把 PVC 給刪除掉了,然后回收了數據。
不過大家要注意,並不是所有的存儲后端的表現結果都是這樣的,我們這里使用的是 nfs,其他存儲后端肯能會有不一樣的結果。
大家在使用 PV 和 PVC 的時候一定要注意這些細節,不然一不小心就把數據搞丟了。