一、Storage
1.1、Volume
官網網址:https://kubernetes.io/docs/concepts/storage/volumes/
通過官網說明大致總結下就是這個volumes在docker中的理解就是我僅僅是通過一個volumes技術可以聲名一個變數,然後可以通過這個變數將物理主機的路徑和虛擬路徑進行一個繫結;簡單來說這就是一個持久化技術;在k8s中就可以理解是跟pod的繫結持久化;這塊內容的實踐在前面的yaml檔案中有應用到,有興趣的可以看前面文章。但這玩意還是有問題的,例如,我應用現在在A節點上,資料持久化也持久化在A節點上,但是,服務遷移後我服務到了B節點上,這時問題就來了。
1.2、Host型別volume實戰
定義一個Pod,其中包含兩個Container,都使用Pod的Volume;pod命名是volume-pod.yml
apiVersion: v1 kind: Pod metadata: name: volume-pod spec: containers: - name: nginx-container image: nginx ports: - containerPort: 80 volumeMounts: - name: volume-pod mountPath: /nginx-volume - name: busybox-container image: busybox command: ['sh', '-c', 'echo The app is running! && sleep 3600'] volumeMounts: - name: volume-pod mountPath: /busybox-volume volumes: - name: volume-pod hostPath: path: /tmp/volume-pod
(1)建立資源
kubectl apply -f volume-pod.yaml
(2)檢視pod的執行情況
kubectl get pods -o wide
(3)來到執行的worker節點
用命令檢視容器是否存在
docker ps | grep volume
進入docker目錄看建立的目錄是否存在,會發現nginx-volume和busybox-volume目錄是存在的
docker exec -it containerid sh
(4)檢視pod中的容器裡面的hosts檔案,是否一樣。(將上面兩個資料夾裡面的東西和宿主機的volume-pod內容對比下就可以了)
(5)所以一般container中的儲存或者網路的內容,不要在container層面修改,而是在pod中修改
1.3 PersistentVolume
官網:https://kubernetes.io/docs/concepts/storage/persistent-volumes/
這個方案是生產環境推薦的方式;
前面說的volumes技術是將虛擬盤對映到物理機上,我也說過會有一個問題,那就是系統遷移到另一臺伺服器上時,虛擬盤和持久盤不在同一個物理主機上,那怎麼解決這個問題呢,其實也很簡單,我在持久化時不是持久化到物理主機上,而是持久化到一個固定的主機上,然後將固定的主機物理盤做高可用,這樣,你虛擬盤怎麼浪都可以找到固定的持久化盤位置了。yaml檔案如下
apiVersion: v1 kind: PersistentVolume metadata: name: my-pv spec: capacity: storage: 5Gi # 儲存空間大小 volumeMode: Filesystem accessModes: - ReadWriteOnce # 只允許一個Pod進行獨佔式讀寫操作 persistentVolumeReclaimPolicy: Recycle storageClassName: slow mountOptions: - hard - nfsvers=4.1 nfs: path: /tmp # 遠端伺服器的目錄 server: 172.17.0.2 # 遠端的伺服器
說白了,PV是K8s中的資源,volume的plugin實現,生命週期獨立於Pod,封裝了底層儲存卷實現的細節。
1.4、PersistentVolumeClaim
官網:https://kubernetes.io/docs/concepts/storage/persistent-volumes/#persistentvolumeclaims
有了PV,那Pod如何使用呢?為了方便使用,我們可以設計出一個PVC來繫結PV,然後把PVC交給Pod來使用即可
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: myclaim spec: accessModes: - ReadWriteOnce volumeMode: Filesystem resources: requests: storage: 8Gi storageClassName: slow selector: matchLabels: release: "stable" matchExpressions: - {key: environment, operator: In, values: [dev]}
說白了,PVC會匹配滿足要求的PV[**是根據size和訪問模式進行匹配的**],進行一一繫結,然後它們的狀態都會變成Bound。也就是PVC負責請求PV的大小和訪問方式,然後Pod中就可以直接使用PVC咯。
1.5、Pod中如何使用PVC
官網:https://kubernetes.io/docs/concepts/storage/persistent-volumes/#claims-as-volumes
apiVersion: v1 kind: Pod metadata: name: mypod spec: containers: - name: myfrontend image: nginx volumeMounts: - mountPath: "/var/www/html" name: mypd volumes: - name: mypd persistentVolumeClaim: claimName: myclaim
1.6、Pod中使用PVC實戰
場景背景:使用nginx持久化儲存演示
- 共享儲存使用nfs,比如選擇在m節點
- 建立pv和pvc
- nginx pod中使用pvc
1.6.1 master節點搭建nfs
在master節點上搭建一個NFS伺服器,目錄為/nfs/data
1)選擇master節點作為nfs的server,所以在master節點上安裝nfs
yum install -y nfs-utils
建立nfs目錄
mkdir -p /nfs/data/
mkdir -p /nfs/data/mysql
授予許可權
chmod -R 777 /nfs/data
編輯export檔案
vi /etc/exports
/nfs/data *(rw,no_root_squash,sync)
使得配置生效
exportfs -r
檢視生效
exportfs
啟動rpcbind、nfs服務
systemctl restart rpcbind && systemctl enable rpcbind
systemctl restart nfs && systemctl enable nfs
檢視rpc服務的註冊情況
rpcinfo -p localhost
showmount測試
showmount -e master-ip
2)所有node上安裝客戶端
yum -y install nfs-utils
systemctl start nfs && systemctl enable nfs
1.6.2 建立PV&PVC&Nginx
(1)在nfs伺服器建立所需要的目錄
mkdir -p /nfs/data/nginx
(2)定義PV,PVC和Nginx的yaml檔案;我取名為nginx-pv-demo.yaml
# 定義PV apiVersion: v1 kind: PersistentVolume metadata: name: nginx-pv spec: accessModes: - ReadWriteMany capacity: storage: 2Gi nfs: path: /nfs/data/nginx server: 121.41.10.13 --- # 定義PVC,用於消費PV apiVersion: v1 kind: PersistentVolumeClaim metadata: name: nginx-pvc spec: accessModes: - ReadWriteMany resources: requests: storage: 2Gi --- # 定義Pod,指定需要使用的PVC apiVersion: apps/v1beta1 kind: Deployment metadata: name: nginx spec: selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - image: nginx name: mysql ports: - containerPort: 80 volumeMounts: - name: nginx-persistent-storage mountPath: /usr/share/nginx/html volumes: - name: nginx-persistent-storage persistentVolumeClaim: claimName: nginx-pvc
(3)根據yaml檔案建立資源並檢視資源
kubectl apply -f nginx-pv-demo.yaml
kubectl get pv,pvc
kubectl get pods -o wide
(4)測試持久化儲存
- 在/nfs/data/nginx新建檔案1.html,寫上內容
- kubectl get pods -o wide 得到nginx-pod的ip地址
- curl nginx-pod-ip/1.html
- kubectl exec -it nginx-pod bash 進入/usr/share/nginx/html目錄檢視
- kubectl delete pod nginx-pod
- 檢視新nginx-pod的ip並且訪問nginx-pod-ip/1.html
1.7、 StorageClass
上面手動管理PV的方式還是有點low,下面來個更方便的
官網:https://kubernetes.io/docs/concepts/storage/storage-classes/
github:https://github.com/kubernetes-incubator/external-storage/tree/master/nfs
官網的話簡述下意思就是說StorageClass是宣告儲存外掛,用於自動建立PV的。說白了就是建立PV的模板,其中有兩個重要部分:PV屬性和建立此PV所需要的外掛。這樣PVC就可以按“Class”來匹配PV。可以為PV指定storageClassName屬性,標識PV歸屬於哪一個Class。
下面是官網上提供的模板檔案
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: standard provisioner: kubernetes.io/aws-ebs parameters: type: gp2 reclaimPolicy: Retain allowVolumeExpansion: true mountOptions: - debug volumeBindingMode: Immediate
官網太長我不一一解讀,可以自己看,但官網的文件說明了一個很重要的資訊,那就是StorageClass之所以能夠動態供給PV,是因為Provisioner,也就是Dynamic Provisioning,但是NFS這種型別,K8s中預設是沒有Provisioner外掛的,需要自己建立;但我們現在用的就是NFS這種模式,如果NFS不能支援,那我們當面玩的一切的一切就是白玩的,其實我覺得不用太擔心,為什麼呢,針對開發這種事,總有人願意挑戰,把不可能的事變成可能;下面我在實戰中我結合github中大佬提供的NFS外掛支援的做法實現上面的理論。
1.8 StorageClass實戰
github:https://github.com/kubernetes-incubator/external-storage/tree/master/nfs
(1)準備好NFS伺服器[並且確保nfs可以正常工作],建立持久化需要的目錄
cd /nfs/data/ghy
(2)由於建立資源需要APIServer的認證,所以要建立一個帳戶跟APIServer互動,所以根據rbac.yaml檔案建立資源
kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: nfs-provisioner-runner rules: - 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"] - apiGroups: [""] resources: ["services", "endpoints"] verbs: ["get"] - apiGroups: ["extensions"] resources: ["podsecuritypolicies"] resourceNames: ["nfs-provisioner"] verbs: ["use"] --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: run-nfs-provisioner subjects: - kind: ServiceAccount name: nfs-provisioner # replace with namespace where provisioner is deployed namespace: default roleRef: kind: ClusterRole name: nfs-provisioner-runner apiGroup: rbac.authorization.k8s.io --- kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: name: leader-locking-nfs-provisioner rules: - apiGroups: [""] resources: ["endpoints"] verbs: ["get", "list", "watch", "create", "update", "patch"] --- kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: leader-locking-nfs-provisioner subjects: - kind: ServiceAccount name: nfs-provisioner # replace with namespace where provisioner is deployed namespace: default roleRef: kind: Role name: leader-locking-nfs-provisioner apiGroup: rbac.authorization.k8s.io
kubectl apply -f rbac.yaml
(3)根據deployment.yaml檔案建立資源,裡面會有我們要訪問的伺服器,大家把裡面配置的ip和伺服器檔名改成自己的;看這個配置大家也很容易清楚這玩意就是跟NFS伺服器進行互動的玩意
apiVersion: v1 kind: ServiceAccount metadata: name: nfs-provisioner --- kind: Deployment apiVersion: extensions/v1beta1 metadata: name: nfs-provisioner spec: replicas: 1 strategy: type: Recreate template: metadata: labels: app: nfs-provisioner spec: serviceAccount: nfs-provisioner containers: - name: nfs-provisioner image: registry.cn-hangzhou.aliyuncs.com/open-ali/nfs-client-provisioner volumeMounts: - name: nfs-client-root mountPath: /persistentvolumes env: - name: PROVISIONER_NAME value: example.com/nfs - name: NFS_SERVER value: 192.168.0.21 - name: NFS_PATH value: /nfs/data/ghy volumes: - name: nfs-client-root nfs: server: 192.168.0.21 path: /nfs/data/ghy
kubectl apply -f deployment.yaml
(4)根據class.yaml建立資源
kind: StorageClass apiVersion: storage.k8s.io/v1 metadata: name: example-nfs provisioner: example.com/nfs
kubectl apply -f class.yaml
(5)經過前面的步驟,以前我們要建立PV的動作就可以不用了。因為有了storage自動幫我們生成了,接下來我們就應該建立PVC了,但現在又有個問題,那就是PVC不能自動繫結PV,原因就是因為我們現在的方式是不用建立Pv了,pv建立的這個動作由,storage自動幫我們建立,現在我們建立一個PVC然後根據my-pvc.yaml建立資源;
kind: PersistentVolumeClaim apiVersion: v1 metadata: name: my-pvc spec: accessModes: - ReadWriteMany resources: requests: storage: 1Mi # 這個名字要和上面建立的storageclass名稱一致,之所以要一至是因為我們要通過storageclass建立PV,所以要匹配storageclass然後讓他動態幫我們建立PV storageClassName: example-nfs
kubectl apply -f my-pvc.yaml
kubectl get pvc
(6)經過上面PV和PVC都有了,按照以前的方式就差個Pod了,下面根據nginx-pod.yaml建立資源
kind: Pod apiVersion: v1 metadata: name: nginx spec: containers: - name: nginx image: nginx volumeMounts: - name: my-pvc mountPath: "/usr/ghy" restartPolicy: "Never" volumes: - name: my-pvc persistentVolumeClaim: claimName: my-pvc
kubectl apply -f nginx-pod.yaml
kubectl exec -it nginx bash
然後用下面命令修改檔案進行驗證檔案同步問題
cd /usr/ghy
上面流程其實總結下可以用下面這張圖片總結
1.9 PV的狀態和回收策略
PV的狀態
- Available:表示當前的pv沒有被繫結
- Bound:表示已經被pvc掛載
- Released:pvc沒有在使用pv, 需要管理員手工釋放pv
- Failed:資源回收失敗
PV回收策略
- Retain:表示刪除PVC的時候,PV不會一起刪除,而是變成Released狀態等待管理員手動清理
- Recycle:在Kubernetes新版本就不用了,採用動態PV供給來替代
- Delete:表示刪除PVC的時候,PV也會一起刪除,同時也刪除PV所指向的實際儲存空間
注意:目前只有NFS和HostPath支援Recycle策略。AWS EBS、GCE PD、Azure Disk和Cinder支援Delete策略