一切container和它之中的数据都是临时的。如果container重启,这些数据会丢失。Volume用于container保存需要持久化的数据。这些数据也可以用于container共享。
Docker中有volume的概念。在Docker中,volume是container中的一个目录。Volume没有生命周期的概念,volume中的数据只有储存在本地磁盘这一种形式。
Kubernetes的volume具有明确的生命空间。Volume生命周期比pod中运行的container长。Container重启之后,volume的数据仍会保留。但是,当Pod销毁之时,该pod关联的volume也会同时销毁。
使用volume的方法:在spec.volumes处声明volume,在spec.containers[*].volumeMount中挂载volume到container。
可以将ConfigMap的值注入到volume中。这样可以在pod中使用config map。同时,Volume中的值会随着ConfigMap的修改而自动更新。
在Volume中使用ConfigMap的用法如下:
apiVersion: v1
kind: Pod
metadata:
name: configmap-pod
spec:
containers:
- name: test
image: busybox
volumeMounts:
- name: config-vol
mountPath: /etc/config
volumes:
- name: config-vol
configMap:
name: log-config
items:
- key: log_level
path: log_level
这里例子将名字为log-config的config map中key为log_level的值,作为文件log_level的内容,放入config-vol的根目录。然后把config-vol挂载到/etc/config
,目录。也就是container中/etc/config/log_level
文件的内容为log-config中log_level的值。
可以使用DownWard API的方式,将k8s资源的spec信息作为文件内容放入到volume中。
例子如下:
apiVersion: v1
kind: Pod
metadata:
name: kubernetes-downwardapi-volume-example
labels:
zone: us-est-coast
cluster: test-cluster1
rack: rack-22
annotations:
build: two
builder: john-doe
spec:
containers:
- name: client-container
image: k8s.gcr.io/busybox
command: ["sh", "-c"]
args:
- while true; do
if [[ -e /etc/podinfo/labels ]]; then
echo -en '\n\n'; cat /etc/podinfo/labels; fi;
if [[ -e /etc/podinfo/annotations ]]; then
echo -en '\n\n'; cat /etc/podinfo/annotations; fi;
sleep 5;
done;
volumeMounts:
- name: podinfo
mountPath: /etc/podinfo
volumes:
- name: podinfo
downwardAPI:
items:
- path: "labels"
fieldRef:
fieldPath: metadata.labels
- path: "annotations"
fieldRef:
fieldPath: metadata.annotations
此处volume使用了downwardAPI。volume中labels文件的内容为pod metadata的labels配置项内容,即:
zone: us-est-coast
cluster: test-cluster1
rack: rack-22
annotation文件的内容为为pod metadata的annotations配置项内容,即:
build: two
builder: john-doe
生命周期和pod一样。emptyDir的初始状态为一个没有任何内容的volume。如果Pod从集群节点上移除,那么emptyDir类型的volume中的内容会被清除。
Container遇到崩溃或重启,这时候Pod本身不会受任何影响,这种情况下emptyDir volume中的数据会保留。Container重启之后数据仍然可见。
emptyDir volume具有的这种特性适合如下场景使用:
默认来说emptyDir类型volume的物理存储在硬盘,SSD或网络设备上。可以设置emptyDir.medium
为Memory
,这时候k8s会使用tempfs(基于内存的文件系统)。此时volume的容量限制收到container的内存配额的制约。
配置样例为:
apiVersion: v1
kind: Pod
metadata:
name: fortune
spec:
containers:
- image: luksa/fortune
name: html-generator
volumeMounts:
- name: html
mountPath: /var/htdocs
- image: nginx:alpine
name: web-server
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
readOnly: true
ports:
- containerPort: 80
protocol: TCP
volumes:
- name: html
emptyDir: {}
使用tempfs(内存)的emptyDir volume的配置样例如下;
volumes:
- name: html
emptyDir:
medium: Memory
gitRepo比较简单:先准备一个emptyDir类型的volume,再clone一个git repo到volume,然后把volume mount到container。
apiVersion: v1
kind: Pod
metadata:
name: gitrepo-volume-pod
spec:
containers:
- image: nginx:alpine
name: web-server
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
readOnly: true
ports:
- containerPort: 80
protocol: TCP
volumes:
- name: html
gitRepo:
repository: https://github.com/luksa/kubia-website-example.git
revision: master
directory: .
此模式挂载pod宿主机文件系统中的文件或目录到pod。
hostPath必须指定一个path参与,用于指定使用宿主机哪个目录。
除此之外还有一个可选的type参数,有如下值可供配置:
需要注意的是由于hostPath类型volume的数据和宿主机强绑定,如果pod停止后被schedule到其他节点,pod读取到的数据会有变化。
使用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
local模式支持使用磁盘,分区或者是目录。local还支持使用静态创建的PersistentVolume,不支持Dynamic provisioning(使用PVC的方式)。
与hostPath不同的是,local模式无需手动配置将pod调度到固定的node上。local模式系统通过volume的node affinity配置来感知volume的node限制(volume只能生成在特定的node上)。
使用例子:
apiVersion: v1
kind: PersistentVolume
metadata:
name: example-pv
spec:
capacity:
storage: 100Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Delete
storageClassName: local-storage
local:
path: /mnt/disks/ssd1
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- example-node
nodeAffinity必须配置。这个例子创建出的PersistentVolume必须创建在hostname包含example-node
的节点上。
VolumeMode默认值为Filesystem。可以配置为Block
,将volume作为块设备使用。
使用local模式的时候建议配套的StorageClass的volumeBindingMode
设置为WaitForFirstConsumer
。如下所示:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
延迟volume绑定可以允许在volume绑定的时候考虑到pod中配置的一些限制,例如node资源限制,node选择器和pod affinity以及pod anti-affinity。
使用PVC声明式的创建所需的PersistentVolume。这个稍后在PersistentVolume中介绍。
映射多个类型的配置(数据源)到volume中。相当于多种volume合并使用。
可以映射的volume类型为:
所有的volume数据源要求必须和使用它的pod在同一个namespace之下。
一个使用例子如下:
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
通常我们绑定volume时,映射的是volume的根目录。我们可以通过指定subPath参数,将volume中的其他目录挂载到container中。对于一个pod多个container,且多处使用同一个volume的场景最为适用。
例子如下:
apiVersion: v1
kind: Pod
metadata:
name: my-lamp-site
spec:
containers:
- name: mysql
image: mysql
env:
- name: MYSQL_ROOT_PASSWORD
value: "rootpasswd"
volumeMounts:
- mountPath: /var/lib/mysql
name: site-data
subPath: mysql
- name: php
image: php:7.0-apache
volumeMounts:
- mountPath: /var/www/html
name: site-data
subPath: html
volumes:
- name: site-data
persistentVolumeClaim:
claimName: my-lamp-site-data
这个例子中mysql和php两个container使用同一个volume site-data。Volume的mysql
目录映射到了mysql container的/var/lib/mysql
。Volume的html
目录映射到了php container的/var/www/html
。
emptyDir使用磁盘时的限制取决于kubelet所在的文件系统(/var/lib/kubelet)。emptyDir或者hostPath占用多大磁盘空间是没有限制的。