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的動態儲存供應