1. 程式人生 > 實用技巧 >Kubernetes資料持久化方案

Kubernetes資料持久化方案

目錄

一、介紹

Volume是Pod中能夠被多個容器共享的磁碟目錄。我們知道,預設情況下Docker容器中的資料都是非持久化的,在容器消亡後資料也會消失。因此Docker提供了Volume機制以便實現資料的持久化。Kubernetes中Volume的概念與Docker中的Volume類似,但不完全相同。具體區別如下:

  • Kubernetes中的Volume與Pod的生命週期相同,但與容器的生命週期不相關。當容器終止或重啟時,Volume中的資料也不會丟失。
  • 當Pod被刪除時,Volume才會被清理。並且資料是否丟失取決於Volume的具體型別,比如emptyDir型別的Volume資料會丟失,而PV型別的資料則不會丟失。

二、資料卷Volume:

K8s提供了非常豐富的Volume型別,比如:emptyDir、hostPath、gcePersistentDisk。注意,這些 volume 並非全部都是持久化的,比如 emptyDir、secret、gitRepo 等,這些 volume 會隨著 Pod 的消亡而消失。

emptyDir

一個emptyDir volume是在Pod分配到Node時建立的。顧名思義,它的初始內容為空,在同一個Pod中的所有容器均可以讀寫這個emptyDir volume。當 Pod 從 Node 上被刪除(Pod 被刪除,或者 Pod 發生遷移),emptyDir 也會被刪除,並且資料永久丟失。
一個簡單的例子:

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: k8s.gcr.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir: {}

emptyDir型別的volume適合於以下場景:

臨時空間。例如某些程式執行時所需的臨時目錄,無需永久儲存。
一個容器需要從另一容器中獲取資料的目錄(多容器共享目錄)

hostPath

hostPath型別的volume允許使用者掛在Node上的檔案系統到Pod中,如果 Pod 需要使用 Node 上的檔案,可以使用 hostPath。
一個簡單的例子:

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: k8s.gcr.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-pd
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      # directory location on host
      path: /data
      # this field is optional
      type: Directory

hostPath支援type屬性,它的可選值如下:

hostPath.PNG
hostPath volume通常用於以下場景:

容器中的應用程式產生的日誌檔案需要永久儲存,可以使用宿主機的檔案系統進行儲存。
需要訪問宿主機上Docker引擎內部資料結構的容器應用,通過定義hostPath為/var/lib/docker目錄,使容器內應用可以直接訪問Docker的檔案系統。
在使用hostPath volume時,需要注意:

在不同的Node上具有相同配置的Pod,可能會因為宿主機上的目錄和檔案不同,而導致對Volume上目錄和檔案的訪問結果不一致。

gcePersistentDisk

gcePersistentDisk 可以掛載 GCE(Google Compute Engine) 上的永久磁碟到容器,需要 Kubernetes 執行在 GCE 的 VM 中。需要注意的是,你必須先建立一個永久磁碟(Persistent Disk,PD)才能使用gcePersistentDisk volume。

volumes:
  - name: test-volume
    # This GCE PD must already exist.
    gcePersistentDisk:
      pdName: my-data-disk
      fsType: ext4
awsElasticBlockStore

與gcePersistentDisk類似,awsElasticBlockStore 使用Amazon Web Service(AWS)提供的EBS Volume,掛在到Pod中。需要 Kubernetes 執行在 AWS 的 EC2 上。另外,需要先建立一個EBS Volume才能使用awsElasticBlockStore。


volumes:
  - name: test-volume
    # This AWS EBS volume must already exist.
    awsElasticBlockStore:
      volumeID: <volume-id>
      fsType: ext4

nfs

NFS 是 Network File System 的縮寫,即網路檔案系統。Kubernetes 中通過簡單地配置就可以掛載 NFS 到 Pod 中,而 NFS 中的資料是可以永久儲存的,同時 NFS 支援同時寫操作。

volumes:
- name: nfs
  nfs:
    # FIXME: use the right hostname
    server: 10.254.234.223
    path: "/"

gitRepo

通過掛在一個空目錄,並從GIT倉庫克隆一個repository供Pod使用。

 volumes:
  - name: git-volume
    gitRepo:
      repository: "git@somewhere:me/my-git-repository.git"
      revision: "22f1d8406d464b0c0874075539c1f2e96c253775"

subPath

Pod 的多個容器使用同一個 Volume 時,subPath 非常有用。

apiVersion: v1
kind: Pod
metadata:
  name: my-lamp-site
spec:
    containers:
    - name: mysql
      image: mysql
      volumeMounts:
      - mountPath: /var/lib/mysql
        name: site-data
        subPath: mysql
    - name: php
      image: php
      volumeMounts:
      - mountPath: /var/www/html
        name: site-data
        subPath: html
    volumes:
    - name: site-data
      persistentVolumeClaim:
        claimName: my-lamp-site-data

三、雲服務的資料卷物件

上文我們學習了Kubernetes中的Volume,我們可以發現前文中的Volume(無論何種型別)和使用它的Pod都是一種靜態繫結關係,在Pod定義檔案中,同時定義了它使用的Volume。
在這種情況下,Volume是Pod的附屬品,我們無法像建立其他資源(例如Pod,Node,Deployment等等)一樣建立一個Volume。
因此Kubernetes提出了PersistentVolume(PV)的概念。
PersistentVolume和Volume一樣,代表了叢集中的一塊儲存區域,然而Kubernetes將PersistentVolume抽象成了一種叢集資源,類似於叢集中的Node物件,這意味著我們可以使用Kubernetes API來建立PersistentVolume物件。PV與Volume最大的不同是PV擁有著獨立於Pod的生命週期。
而PersistentVolumeClaim(PVC)代表了使用者對PV資源的請求。使用者需要使用PV資源時,只需要建立一個PVC物件(包括指定使用何種儲存資源,使用多少GB,以何種模式使用PV等資訊),Kubernetes會自動為我們分配我們所需的PV。

PV的靜態建立

示例:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv0003
  labels:
    name: pv0003
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: slow
  mountOptions:
    - hard
    - nfsvers=4.1
  nfs:
    path: /tmp
    server: 172.17.0.2

有三個引數需要注意,accessModes、mountOptions、persistentVolumeReclaimPolicy
1.accessModes:是PV的訪問模式,有三種:

  • ReadWriteOnce(RWO):是最基本的方式,可讀可寫,但只支援被單個 Pod 掛載。
  • ReadOnlyMany(ROX):可以以只讀的方式被多個 Pod 掛載。
  • ReadWriteMany(RWX):這種儲存可以以讀寫的方式被多個 Pod 共享。
    注意:不是每一種PV都支援這三種方式,例如ReadWriteMany,目前支援的還比較少。在 PVC 繫結 PV 時通常根據兩個條件來繫結,一個是儲存的大小,另一個就是訪問模式。

2.mountOptions中指定PV的底層資料卷型別,例如上文中建立的PV,底層使用nfs儲存。目前Kuberntes支援以下型別:

GCEPersistentDisk
AWSElasticBlockStore
AzureFile
AzureDisk
FC (Fibre Channel)**
FlexVolume
Flocker
NFS
iSCSI
RBD (Ceph Block Device)
CephFS
Cinder (OpenStack block storage)
Glusterfs
VsphereVolume
Quobyte Volumes
HostPath (Single node testing only – local storage is not supported in any way and WILL NOT WORK in a multi-node cluster)
VMware Photon
Portworx Volumes
ScaleIO Volumes
StorageOS

3.persistentVolumeReclaimPolicy指定PVC 釋放卷的時候 PV 該如何操作)也有三種:

  • Retain,不清理, 保留 Volume(需要手動清理)
  • Recycle,刪除資料,即 rm -rf /thevolume/*(只有 NFS 和 HostPath 支援)
  • Delete,刪除儲存資源,比如刪除 AWS EBS 卷(只有 AWS EBS, GCE PD, Azure Disk 和 Cinder 支援)
  • PVC釋放卷是指使用者刪除一個PVC物件時,那麼與該PVC物件繫結的PV就會被釋放。

PVC的建立

當我們定義好一個PV後,我們希望像使用Volume那樣使用這個PV,那麼我們需要做的就是建立一個PVC物件,並在Pod定義中使用這個PVC。
定義一個PVC:

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: myclaim
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 8Gi
  storageClassName: slow
  selector:
    matchLabels:
      release: "stable"

Pod通過掛載Volume的方式應用PVC:

kind: Pod
apiVersion: v1
metadata:
  name: mypod
spec:
  containers:
    - name: myfrontend
      image: dockerfile/nginx
      volumeMounts:
      - mountPath: "/var/www/html"
        name: mypd
  volumes:
    - name: mypd
      persistentVolumeClaim:
        claimName: myclaim

通過示例我們可以看到PVC和PV的繫結,不是簡單的通過Label來進行。而是要綜合storageClassName,accessModes,matchLabels以及storage來進行繫結。
storageClassName指定了使用哪個資料卷,就是在建立pv時指定的pv的名稱。
accessModes設定了需要請求的pv的讀寫模式
Label標籤的意思,這個就不多說了。
總的來說,就是通過這三個引數,請求滿足storageClassName=slow,accessModes = ReadWriteOnce並且擁有Label:release: "stable"的PV、

PV的動態建立

上文中我們通過PersistentVolume描述檔案建立了一個PV。這樣的建立方式我們成為靜態建立。
這樣的建立方式有一個弊端,那就是假如我們建立PV時指定大小為50G,而PVC請求80G的PV,那麼此PVC就無法找到合適的PV來繫結。
因此產生了了PV的動態建立。PV的動態建立依賴於StorageClass物件,我們不需要手動建立任何PV,所有的工作都由StorageClass為我們完成。
1)叢集管理員預先建立儲存類(StorageClass);
2)使用者建立使用儲存類的持久化儲存宣告(PVC:PersistentVolumeClaim);
3)儲存持久化宣告通知系統,它需要一個持久化儲存(PV: PersistentVolume);
4)系統讀取儲存類的資訊;
5)系統基於儲存類的資訊,在後臺自動建立PVC需要的PV;
6)使用者建立一個使用PVC的Pod;
7)Pod中的應用通過PVC進行資料的持久化;
8)而PVC使用PV進行資料的最終持久化處理。
聽起來如此美好,所以要花錢,就是說雲服務商提供一個api給K8S,在pod需要pvc的時候,請求到雲服務商那裡,之後就不需要你管啦。

1.定義儲存類

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: slow
provisioner: kubernetes.io/aws-ebs
parameters:
  type: io1
  zones: us-east-1d, us-east-1c
  iopsPerGB: "10"

這個例子使用AWS提供的外掛( kubernetes.io/aws-ebs)建立了一個基於AWS底層儲存的StorageClass。
這意味著使用這個StorageClass,那麼所有的PV都是AWSElasticBlockStore型別的。
StorageClass的定義包含四個部分:
provisioner:指定 Volume 外掛的型別,包括內建外掛(如 kubernetes.io/aws-ebs)和外部外掛(如 external-storage 提供的 ceph.com/cephfs)。
mountOptions:指定掛載選項,當 PV 不支援指定的選項時會直接失敗。比如 NFS 支援 hard 和 nfsvers=4.1 等選項。
parameters:指定 provisioner 的選項,比如 kubernetes.io/aws-ebs 支援 type、zone、iopsPerGB 等引數。
reclaimPolicy:指定回收策略,同 PV 的回收策略。
手動建立的PV時,我們指定了storageClassName=slow的配置項,然後Pod定義中也通過指定storageClassName=slow,從而完成繫結。而通過StorageClass實現動態PV時,
我們只需要指定StorageClass的metadata.name。

回到上文中建立PVC的例子,此時PVC指定了storageClassName=slow。那麼Kubernetes會在叢集中尋找是否存在metadata.name=slow的StorageClass,
如果存在,此StorageClass會自動為此PVC建立一個accessModes = ReadWriteOnce,並且大小為8GB的PV。

2.供應者
provisioner此引數域決定PV使用什麼儲存卷外掛。

儲存卷 內建供應者 配置例子
AWSElasticBlockStore AWS
AzureFile Azure File
AzureDisk Azure Disk
CephFS
Cinder OpenStack Cinder
FC
FlexVolume
Flocker
GCEPersistentDisk GCE
Glusterfs Glusterfs
iSCSI
PhotonPersistentDisk
Quobyte Quobyte
NFS
RBD Ceph RBD
VsphereVolume vSphere
PortworxVolume Portworx Volume
ScaleIO ScaleIO
StorageOS StorageOS
Local Local

參考文件:Kubernetes物件之PersistentVolume,StorageClass和PersistentVolumeClaim
Kubernetes-基於StorageClass的動態儲存供應