Kubernetes的故事之持久化儲存(十)

童話述說我的結局發表於2022-01-26

一、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策略

相關文章