Kubernetes之持久化Volumes及StorageClass


  Kubernetes 官網對 Volumes的介紹說:On-disk files in a Container are ephemeral, which presents some problems for non-trivial applications when running in Containers. First, when a Container crashes, kubelet will restart it, but the files will be lost - the Container starts with a clean state. Second, when running Containers together in a Pod it is often necessary to share files between those Containers. The Kubernetes Volume abstraction solves both of these problems.

  其意思是:容器中的文件在磁盤上是臨時存放的,這給容器中運行的特殊應用程序帶來一些問題。 首先,當容器崩潰時,kubelet 將重新啟動容器,容器中的文件將會丟失——因為容器會以干凈的狀態重建。 其次,當在一個 Pod 中同時運行多個容器時,常常需要在這些容器之間共享文件。 Kubernetes 抽象出 Volume 對象來解決這兩個問題。

  在 官網的 pod的介紹章節  中也提到,pod內的所有容器共享pod的網絡與存儲:

  • Networking:Each Pod is assigned a unique IP address. Every container in a Pod shares the network namespace, including the IP address and network ports. Containers inside a Pod can communicate with one another using localhost. When containers in a Pod communicate with entities outside the Pod, they must coordinate how they use the shared network resources (such as ports).
  • Storage:A Pod can specify a set of shared storage Volumes. All containers in the Pod can access the shared volumes, allowing those containers to share data. Volumes also allow persistent data in a Pod to survive in case one of the containers within needs to be restarted. See Volumes for more information on how Kubernetes implements shared storage in a Pod.

Host類型volume實戰:

  其實這里的 volume 跟docker里面的有點像.

(1)創建資源定義一個Pod,其中包含兩個Container,都使用Pod的Volume :volume-pod.yaml

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

(2)查看pod的運行情況 kubectl get pods -o wide

(3)來到運行的worker節點:

  docker ps | grep volume :查看容器
  ls /tmp/volume-pod :查看該目錄下的文件,我這里事先准備了一個文本文件。
  docker exec -it containerid sh :進入容器
  ls /nginx-volume :查看nginx這個容器是否有同步。
  ls /busybox-volume :查看busybox是否同步數據。
  其實我們會發現,這個時候他們已經同時都同步了數據,且實現了數據共享,這個時候在這三個目錄下隨意更改都會導致其他兩個的內容發生變化。

(4)查看pod中的容器里面的hosts文件,是否一樣。發現是一樣的,並且都是由pod管理的

(5)所以一般container中的存儲或者網絡的內容,不要在container層面修改,而是在pod中修改,比如下面修改一下網絡:

spec:
  hostNetwork: true
  hostPID: true
  hostAliases:
    - ip: "192.168.1.101"
  hostnames:
    - "test.wuzz.com"
  containers:
    - name: nginx-container
    image: nginx

PersistentVolumes(PV):

  官網 :https://kubernetes.io/docs/concepts/storage/persistent-volumes/

  管理存儲與管理計算實例是不同的問題。PersistentVolume子系統為用戶和管理員提供了一個API,它從存儲的使用方式中抽象出存儲提供方式的細節。為此,我們引入了兩個新的API資源:PersistentVolume和PersistentVolumeClaim。持久性卷(PV)是集群中的一段存儲,由管理員提供或使用存儲類動態提供。它是集群中的資源,就像節點是集群資源一樣。PV是與卷類似的卷插件,但其生命周期獨立於使用PV的任何單獨Pod。這個API對象捕獲存儲的實現細節,無論是NFS、iSCSI還是特定於雲提供程序的存儲系統。

  PersistentVolumeClaim (PVC)是用戶對存儲的請求。它類似於pod。pod消耗節點資源,而pvc消耗PV資源。Pods可以請求特定級別的資源(CPU和內存)。索賠可以請求特定的大小和訪問模式(例如,它們可以掛載一次讀/寫或多次只讀)。雖然PersistentVolumeClaims允許用戶使用抽象的存儲資源,但是對於不同的問題,用戶通常需要具有不同屬性(例如性能)的持久卷。集群管理員需要能夠提供各種不同於大小和訪問模式的持久性卷,而不需要向用戶展示這些卷是如何實現的。對於這些需求,有StorageClass資源。

  說白了,PV是K8s中的資源,volume的plugin實現,生命周期獨立於Pod,封裝了底層存儲卷實現的細節。一個簡單的PV的定義如下:

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    # 遠端的服務器

  Kubernetes管理員可以指定附加的掛載選項,以便在節點上掛載持久卷。The following volume types support mount options:

  • AWSElasticBlockStore
  • AzureDisk
  • AzureFile
  • CephFS
  • Cinder (OpenStack block storage)
  • GCEPersistentDisk
  • Glusterfs
  • NFS
  • Quobyte Volumes
  • RBD (Ceph Block Device)
  • StorageOS
  • VsphereVolume
  • iSCSI

PersistentVolumeClaim(PVC):

  官網 :https://kubernetes.io/docs/concepts/storage/persistent-volumes/#persistentvolumeclaims

  有了PV,那Pod如何使用呢?為了方便使用,我們可以設計出一個PVC來綁定PV,然后把PVC交給Pod來使用即可,每個PVC包含一個規格和狀態,這是要求的規格和狀態:

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咯。

Pod中如何使用PVC:

  官網 :https://kubernetes.io/docs/concepts/storage/persistent-volumes/#claims-as-volumes

  Pods通過使用聲明作為卷訪問存儲。Claim必須與使用Claim的Pod存在相同的名稱空間。集群在Pod的名稱空間中找到聲明,並使用它獲得支持聲明的PersistentVolume。然后被安裝到host和進入pod。

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

Pod中使用PVC實戰:

  使用nginx持久化存儲演示

(1)共享存儲使用nfs,我這邊只有2台阿里雲,比如nfs選擇在 master節點 (2)創建pv和pvc (3)nginx pod中使用pvc

master節點搭建NFS:

  NFS(network file system)網絡文件系統,是FreeBSD支持的文件系統中的一種,允許網絡中的計算機之間通過TCP/IP網絡共享資源。在master節點上搭建一個NFS服務器,目錄為/nfs/data

01 選擇master節點作為nfs的server,所以在master節點上
  # 安裝nfs 這個最好在w1節點也運行一下
  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
02 所有node上安裝客戶端
  yum -y install nfs-utils
  systemctl start nfs && systemctl enable nfs

 創建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: 172.18.113.141

---
# 定義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: nginx
        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   kubectl get pvc

  kubectl get pods -o wide :查看下pods

  kubectl describe pod nginx-77945f44db-fjbv8 :查看描述

(4)測試持久化存儲:

01 在/nfs/data/nginx新建文件1.html,寫上內容 我這里寫入 Hello PVC,
02 kubectl get pods -o wide  得到nginx-pod的ip地址 如上圖是 192.168.190.71。切到w1節點
03 curl 192.168.190.71/1.html
04 kubectl exec -it nginx-pod bash  進入/usr/share/nginx/html目錄查看
05 kubectl delete pod nginx-pod
06 查看新nginx-pod的ip並且訪問nginx-pod-ip/1.html

   我們會發現在我們刪除后k8s幫我們生成的新的pod內也有這么一個1.html:

   整個流程可以用下圖表示:

 

StorageClass :

  上面手動管理PV的方式還是有點low,能不能更加靈活一點呢?

  官網 :https://kubernetes.io/docs/concepts/storage/storage-classes/

  每個 StorageClass 都包含 provisionerparameters 和 reclaimPolicy 字段, 這些字段會在StorageClass需要動態分配 PersistentVolume 時會使用到。StorageClass 對象的命名很重要,用戶使用這個命名來請求生成一個特定的類。 當創建 StorageClass 對象時,管理員設置 StorageClass 對象的命名和其他參數,一旦創建了對象就不能再對其更新。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或者StorageClass只能對應一種后端存儲,也就是比如不能既使用 NFS 又使用 aws-ebs
  • 對於手動的情況,一般我們會創建很多的PV,等有PVC需要使用的時候就可以直接使用了,而對於自動的情況,那么就由StorageClass來自動管理創建
  • 如果Pod想要使用共享存儲,一般會在創建PVC,PVC中描述了想要什么類型的后端存儲、空間等,K8s從而會匹配對應的PV,如果沒有匹配成功,Pod就會處於Pending狀態。Pod中使用只需要像使用volumes一樣,指定名字就可以使用了
  • 一個Pod可以使用多個PVC,一個PVC也可以給多個Pod使用
  • 一個PVC只能綁定一個PV,一個PV只能對應一種后端存儲

 

   有了StorageClass之后的PVC可以變成這樣

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: test-claim1
spec:
accessModes:
    - ReadWriteMany
resources:
  requests:
    storage: 1Mi
    storageClassName: nfs

  StorageClass之所以能夠動態供給PV,是因為Provisioner,也就是Dynamic Provisioning但是NFS這種類型,K8s中默認是沒有Provisioner插件的,需要自己創建,但是Github 上給我們提供了這一需求。nfs github :https://github.com/kubernetes-incubator/external-storage/tree/master/nfs

StorageClass實戰:

(1)准備好NFS服務器[並且確保nfs可以正常工作],創建持久化需要的目錄

path: /nfs/data/wuzz 比如mkdir -p /nfs/data/wuzz

server: 192.168.1.101

(2)根據rbac.yaml文件(基於角色的訪問控制)創建資源 kubectl apply -f 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

(3)根據deployment.yaml文件創建資源 kubectl apply -f deployment.yaml

  Service account是為了方便Pod里面的進程調用Kubernetes API或其他外部服務而設計的。

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.1.101 
            - name: NFS_PATH
              value: /nfs/data/wuzz
      volumes:
        - name: nfs-client-root
          nfs:
            server: 192.168.1.101 
            path: /nfs/data/wuzz

(4)根據class.yaml創建資源  kubectl apply -f class.yaml

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: example-nfs
provisioner: example.com/nfs

(5)根據pvc.yaml創建資源  kubectl apply -f my-pvc.yaml

  kubectl get pvc

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: my-pvc
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1Mi
  # 這個名字要和上面創建的storageclass名稱一致
  storageClassName: example-nfs

(6)根據nginx-pod創建資源

  kubectl apply -f nginx-pod.yaml

  kubectl exec -it nginx bash

  cd /usr/wuzz # 進行同步數據測試

kind: Pod
apiVersion: v1
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx
    volumeMounts:
      - name: my-pvc
        mountPath: "/usr/wuzz"
  restartPolicy: "Never"
  volumes:
    - name: my-pvc
      persistentVolumeClaim:
        claimName: my-pvc

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM