Kubernetes 存儲卷
我們知道默認情况下容器的數據都是非持久化的,在容器消亡以後數據也跟著丢失,所以 Docker 提供了 Volume 機制以便將數據持久化存儲。類似的,Kubernetes 提供了更强大的 Volume 機制和豐富的插件,解决了容器數據持久化和容器間共享數據的問題。
與Docker 不同,Kubernetes Volume 的生命周期與 Pod 绑定
- 容器掛掉後Kubelet 再次重啟容器时,Volume 的数据依然還在
- 而 Pod 删除時,Volume 才會清理。數據是否丢失取决於具體的 Volume 類型,比如 emptyDir 的數據會丢失,而 PV 的數據則不會丢失
Volume 類型
目前,Kubernetes 支持以下 Volume 類型:
- emptyDir
- hostPath
- gcePersistentDisk
- awsElasticBlockStore
- nfs
- iscsi
- flocker
- glusterfs
- rbd
- cephfs
- gitRepo
- secret
- persistentVolumeClaim
- downwardAPI
- azureFileVolume
- azureDisk
- vsphereVolume
- Quobyte
- PortworxVolume
- ScaleIO
- FlexVolume
- StorageOS
- local
注意,這些 volume 並非全部都是持久化的,比如 emptyDir、secret、gitRepo 等,這些 volume 會随着 Pod 的消亡而消失。
emptyDir
如果 Pod 設置了 emptyDir 类型 Volume, Pod 被分配到 Node 上时候,會創建 emptyDir,只要 Pod 運行在 Node 上,emptyDir 都會存在(容器挂掉不會導致 emptyDir 丢失数据),但是如果 Pod 從 Node 上被删除(Pod 被删除,或者 Pod 發生遷移),emptyDir 也會被删除,並且永久丢失。
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: gcr.io/google_containers/test-webserver
name: test-container
volumeMounts:
- mountPath: /cache
name: cache-volume
volumes:
- name: cache-volume
emptyDir: {}
hostPath
hostPath 允許掛載 Node 上的文件系统到 Pod 里面去。如果 Pod 需要使用 Node 上的文件,可以使用 hostPath。
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: gcr.io/google_containers/test-webserver
name: test-container
volumeMounts:
- mountPath: /test-pd
name: test-volume
volumes:
- name: test-volume
hostPath:
path: /data
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: "/"
gcePersistentDisk
gcePersistentDisk 可以掛載 GCE 上的永久磁盤到容器,需要 Kubernetes 運行在 GCE 的 VM 中。
volumes:
- name: test-volume
# This GCE PD must already exist.
gcePersistentDisk:
pdName: my-data-disk
fsType: ext4
awsElasticBlockStore
awsElasticBlockStore 可以掛載 AWS 上的 EBS 盤到容器,需要 Kubernetes 運行在 AWS 的 EC2 上。
volumes:
- name: test-volume
# This AWS EBS volume must already exist.
awsElasticBlockStore:
volumeID: <volume-id>
fsType: ext4
gitRepo
gitRepo volume 将 git 代碼下拉到指定的容器路徑中
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
FlexVolume
如果内置的這些 Volume 不滿足要求,则可以使用 FlexVolume 實現自己的 Volume 插件。注意要把 volume plugin 放到/usr/libexec/kubernetes/kubelet-plugins/volume/exec/<vendor~driver>/<driver>
,plugin 要實現init/attach/detach/mount/umount
等命令(可参考 lvm 的示例)。
- name: test
flexVolume:
driver: "kubernetes.io/lvm"
fsType: "ext4"
options:
volumeID: "vol1"
size: "1000m"
volumegroup: "kube_vg"
Projected Volume
Projected volume 將多個 Volume 源映射到同一個目錄中,支持 secret、downwardAPI 和 configMap。
apiVersion: v1
kind: Pod
metadata:
name: volume-test
spec:
containers:
- name: container-test
image: busybox
volumeMounts:
- name: all-in-one
mountPath: "/projected-volume"
readOnly: true
volumes:
- name: all-in-one
projected:
sources:
- secret:
name: mysecret
items:
- key: username
path: my-group/my-username
- downwardAPI:
items:
- path: "labels"
fieldRef:
fieldPath: metadata.labels
- path: "cpu_limit"
resourceFieldRef:
containerName: container-test
resource: limits.cpu
- configMap:
name: myconfigmap
items:
- key: config
path: my-group/my-config
本地儲存限額
v1.7 + 支持對基於本地儲存(如 hostPath, emptyDir, gitRepo 等)的容量進行調度限額,可以通過--feature-gates=LocalStorageCapacityIsolation=true
來開啟這個特性。
為了支持這個特性,Kubernetes 將本地存儲分為兩類
storage.kubernetes.io/overlay
,即/var/lib/docker
的大小storage.kubernetes.io/scratch
,即/var/lib/kubelet
的大小
Kubernetes 根據storage.kubernetes.io/scratch
的大小來調度本地儲存空間,而根據storage.kubernetes.io/overlay
來調度容器的儲存。比如
為容器請求 64MB 的可寫層儲存空間
apiVersion: v1
kind: Pod
metadata:
name: ls1
spec:
restartPolicy: Never
containers:
- name: hello
image: busybox
command: ["df"]
resources:
requests:
storage.kubernetes.io/overlay: 64Mi
為empty 請求 64MB 的儲存空間
apiVersion: v1
kind: Pod
metadata:
name: ls1
spec:
restartPolicy: Never
containers:
- name: hello
image: busybox
command: ["df"]
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
emptyDir:
sizeLimit: 64Mi
Mount 傳遞
在 Kubernetes 中,Volume Mount 默認是私有的,但從v1.8 开始,Kubernetes 支持配置 Mount 傳遞(mountPropagation)。它支持兩種選項
- HostToContainer:這是開啟
MountPropagation=true
時的默認模式,等效於rslave
模式,即容器可以看到 Host 上面在该 volume 内的任何新 Mount 操作 - Bidirectional:等效於
rshared
模式,即 Host 和容器都可以看到對方在该 Volume 内的任何新 Mount 操作。該模式要求容器必須運行在特權模式(即securityContext.privileged=true
)
注意:
- 使用 Mount 傳遞需要開啟
--feature-gates=MountPropagation=true
rslave
和rshared
的说明可以参考内核文档
Volume 快照
v1.8 新增了 pre-alpha 版本的 Volume 快照,但還只是一个雛形,並且其實現不在 Kubernetes 核心代碼中,而是存放在kubernetes-incubator/external-storage中。
TODO: 補充 Volume 快照的設計原理和範例。
Windows Volume
Windows 容器暫時只支持 local、emptyDir、hostPath、AzureDisk、AzureFile 以及 flexvolume。注意 Volume 的路徑格式需要為mountPath: "C:\\etc\\foo"
或者mountPath: "C:/etc/foo"
。
apiVersion: v1
kind: Pod
metadata:
name: hostpath-pod
spec:
containers:
- name: hostpath-nano
image: microsoft/nanoserver:1709
stdin: true
tty: true
volumeMounts:
- name: blah
mountPath: "C:\\etc\\foo"
readOnly: true
nodeSelector:
beta.kubernetes.io/os: windows
volumes:
- name: blah
hostPath:
path: "C:\\AzureData"
apiVersion: v1
kind: Pod
metadata:
name: empty-dir-pod
spec:
containers:
- image: microsoft/nanoserver:1709
name: empty-dir-nano
stdin: true
tty: true
volumeMounts:
- mountPath: /cache
name: cache-volume
- mountPath: C:/scratch
name: scratch-volume
volumes:
- name: cache-volume
emptyDir: {}
- name: scratch-volume
emptyDir: {}
nodeSelector:
beta.kubernetes.io/os: windows