kubernetes 设计理念及主要概念之Volume(四)

Kubernetes存储卷

默认情况下容器的数据都是非持久化的,在容器消亡以后数据也跟着丢失,所以Docker提供了Volume机制以便将数据持久化存储。类似的,Kubernetes提供了更强大的Volume机制和丰富的插件,解决了容器数据持久化和容器间共享数据的问题。

与Docker不同,Kubernetes Volume的生命周期与Pod绑定

  • 容器挂掉后Kubelet再次重启容器时,Volume的数据依然还在
  • 而Pod删除时,Volume才会清理。数据是否丢失取决于具体的Volume类型,比如emptyDir的数据会丢失,而PV的数据不会丢失
  1. Volume类型

目前,Kubernetes支持以下Volume类型:

  • emptyDir
  • hostPath
  • gcePersistentDisk
  • awsElasticBlokStore
  • nfs
  • iscsi
  • flocker
  • glusterfs
  • rbd
  • cephfs
  • gitRepo
  • secret
  • persistentVolumeClaim
  • downwardAPI
  • azureFileVolume
  • vsphereVolume
  • Quobyte
  • PortworxVolume
  • ScaleIO
  • FlexVolume
  • StorageOS
  • local

这些Volume并非全部都是持久化的,emptyDir与hostPath数据节点级别的卷类型,emptyDir的生命周期与Pod资源相同,而使用hostPath卷的Pod一旦被重新调度至其他节点,那么它将无法再使用此前的数据。

再如,Secret和ConfigMap算得上是两种特殊的卷类型。

  • Secret用于向Pod传递敏感信息、如密码、私钥、证书文件等,这些信息如果直接定义在镜像中很容易导致泄露,有了Secret资源,用户可以将这些信息存储于集群中而后再由Pod进行挂载,从而实现将敏感数据与系统解耦。

  • ConfigMap资源则用于向Pod注入非敏感数据,使用时,用户将数据直接存储于ConfigMap对象中,而后直接在Pod中使用ConfigMap卷引用它即可,它可以帮助实现容器配置文件集中化定义和管理。

因此,类似于emptyDir、hostPath、secret、gitRepo等,这些Volume会随着Pod的消亡而消失。

  1. Volume的使用

在Pod中定义Volume由两部分组成:一是.spec.valumes,用于支持不同的Volume类型;二是.spec.containers.volumeMounts,用于定义挂载列表。

在Pod级别定义Volume,.spec.volumes字段可以定义多个Volume,例如下面定义了emptyDir类型和gitRepo类型,

1
2
3
4
5
6
7
8
9
10
spec:
...
volumes:
- name: logdata
emptyDir: {}
- name: example
gitRepo:
repository: https://github.com/barudisshu/kubernetes-pratise.git
revision: master
directory: .

定义好的Volume可以共享。当Pod中只有一个容器时,使用存储卷通常用于数据持久化。

.spec.containers.volumeMounts字段定义了Volume的挂载列表。它的挂载格式是固定的,如下,

1
2
3
4
5
6
7
8
9
10
11
 spec:
...
containers:
- name: <String>
...
volumeMounts:
- name <String> -required-
mountPath <string> -required-
readOnly <boolean>
subPath <string>
montPropagation <string>

其中,

  • name:指定要挂载的存储的名称,必选
  • mountPath:挂载点路径,容器文件系统上的路径,必选
  • readOnly:是否挂载为只读
  • subPath:子路径,即mountPath指定的路径下的一个字路径

例如,容器myapp将logdata存储卷挂载在/var/log/myapp,将example挂载到/webdata/example目录,

1
2
3
4
5
6
7
8
9
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v7
volumeMounts:
- name: logdata
mountPath: /var/log/myapp/
- name: example
mountPath: /webdata/example/
  1. emptyDir

emptyDir,如果Pod设置了emptyDir类型Volume,Pod被分配到Node上时候,会创建emptyDir,只要Pod运行在Node上,emptyDir都会存在(容器挂掉不会导致emptyDir丢失数据),但是如果Pod从Node上被删除(Pod被删除,或者Pod发生迁移),emptyDir也会被删除,并且永久丢失。

.spec.volumes.emptyDir嵌套字段包含有,

  • medium:存储介质的类型,默认为default,表示为使用节点默认存储介质;另一种是Memory,表示使用RAM的临时文件系统tmpfs,空间受限于内存,但性能非常好,通常用于作为缓存。
  • sizeLimit:当前Volume的空间限额,默认为nil,表示不限制;不过如果medium字段值为Memory时建议定义限额。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
apiVersion: v1
kind: Pod
metadata:
name: vol-emptydir-pod
spec:
volumes:
- name: html
emptyDir: {}
containers:
- name: vol-emptydir-pod
image: nginx:1.12-alpine
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /usr/share/nginx/html
name: html
- name: pagegen
image: alpine
volumeMounts:
- mountPath: /html
name: html
command:
- "/bin/sh"
- "-c"
args:
- while true; do
echo $(hostname) $(date) >> /html/index.html;
sleep 10;
done
restartPolicy: Always

容器pagegen每隔10秒向Volume上的index.html文件追加一行信息,

1
kubectl describe pods vol-emptydir-pod

创建Service或直接在集群访问Pod的信息,可看到,

边车(sidecar)容器pagegen每隔10秒生成一行信息追加到Volume上的index.html文件。另外emptyDir存储卷可以基于RAM创建tmpfs文件系统的存储卷,常用于缓存,

1
2
3
4
volumes:
- name: cache
emptyDir:
medium: Memory
  1. gitRepo

gitRepo存储卷是emptyDir的实际应用,它看做是将一份Git仓储中的数据克隆(clone)到创建的空目录(emptyDir),而后再创建容器并挂载该存储卷。

gitRepo存储卷自Kubernetes1.12开始已经废弃,所以这里不再陈述。

  1. hostPath

hostPath运行挂载Node上的文件系统到Pod里面去。如果Pod需要使用Node上的文件,可以使用hostPath。因为它独立于Pod资源的生命周期,因而具有持久性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
apiVersion: v1
kind: Pod
metadata:
name: vol-hostpath-pod
labels:
app: vol-hostpath-pod
spec:
containers:
- name: vol-hostpath-pod
image: ikubernetes/filebeat:5.6.7-alpine
env:
- name: REDIS_HOST
value: redis.kubernetes.io:6379
- name: LOG_LEVEL
value: info
volumeMounts:
- mountPath: /var/log
name: varlog
- mountPath: /var/run/docker.sock
name: socket
- mountPath: /var/lib/docker/containers
name: varlibdockercontainers
readOnly: true
imagePullPolicy: IfNotPresent
restartPolicy: Always
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
- name: socket
hostPath:
path: /var/run/docker.sock

这类Pod资源通常受控于daemonset类型的Pod控制器,它运行于集群中的每个工作节点之上,负责收集工作节点上系统级的相关数据,因此使用hostPath存储卷。这里的filebeat应用架构中,通过Redis进行资源收集,这些收集的日志信息会发往ELK进行统计展示。

hostPath上如果是不受控于Daemonset的无状态应用,重新调度节点运行时,无法确保此前创建的文件或目录是否存在。因此,hostPath虽能持久化数据,但对调度器来说并不适用,这时需要用到独立于集群节点的持久化存储卷,即网络存储卷。

网络存储卷,就是类似于NAS或SAN设备、分布式存储(GlusterFS、RBD)、云端存储(gcePersistentDisk、azureDisk、cinder和awsElasticBlockStore)以及构建在各类存储系统之上的抽象管理层(flocker、portworx Volume和vsphere Volume)等这类网络服务存储。

  1. NFS

NFS,即Network File System,网络文件系统。Kubernetes中通过简单配置可以挂在NFS到Pod中,而NFS中的数据是可以永久保存的,同时NFS支持同时写操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: v1
kind: Pod
metadata:
name: vol-nfs-pod
labels:
app: vol-nfs-pod
spec:
containers:
- name: vol-nfs-pod
image: redis:4-alpine
imagePullPolicy: IfNotPresent
ports:
- containerPort: 6379
name: redisport
volumeMounts:
- mountPath: /data
name: redisdata
restartPolicy: Always
volumes:
- name: redisdata
nfs:
path: /data/redis
server: nfs.kubernetes.io
readOnly: false
  1. RBD

Ceph RBD1是一个分布式、弹性可扩展的、高可靠的、性能优异的存储系统平台。要配置Pod资源使用RBD存储卷,前提条件要满足:

  • Ceph RBD存储集群,
  • Ceph RBD集群中创建有满足Pod资源数据用到的存储image
  • Kubernetes集群内各个节点需要安装Ceph客户端程序包(ceph-common)

它的字段信息有,

  • monitors<[]string>:Ceph存储监视器,逗号分隔的字符串列表;必选字段
  • image<string>:rados image的名称,必选字段
  • pool<string>:rados存储池名称,默认为RBD
  • user<string>:rados用户名,默认为admin
  • keyring<string>:RBD用户认证时使用的keyring文件路径,默认为/etc/ceph/keyring
  • secretRef<Object>:RBD用户认证时使用的保存有相应认证信息的Secret对象,会覆盖由keyring字段提供的密钥信息
  • readOnly<boolean>:是否以只读的方式进行访问
  • fsType:要挂载的存储卷的文件系统类型,至少应该是节点操作系统支持的文件系统,如ext4、xfs、ntf等,默认为ext4

下面是使用RBD存储卷的Pod资源示例,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
apiVersion: v1
kind: Secret
metadata:
name: ceph-secret
type: "kubernetes.io/rbd"
data:
key: QVFENnNsNWMrQm52T2hBQXVYclJVeVNuUzBhOXVUVk00WnpiVFE9PQ==
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: ceph-rbd
provisioner: kubernetes.io/rbd
parameters:
monitors: 192.168.50.32,192.168.50.33,192.168.50.34
adminId: admin
adminSecretName: ceph-secret
pool: kube
userId: admin
userSecretName: ceph-secret
fsType: xfs
imageFormat: "2"
imageFeatures: "layering"
---
apiVersion: v1
kind: Pod
metadata:
name: vol-rbd-pod
labels:
app: vol-rbd-pod
spec:
containers:
- name: vol-rbd-pod
image: redis:4-alpine
ports:
- containerPort: 6379
name: redisport
volumeMounts:
- mountPath: /data
name: redis-rbd-vol
imagePullPolicy: IfNotPresent
volumes:
- name: redis-rbd-vol
persistentVolumeClaim:
claimName: ceph-claim
restartPolicy: Always
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ceph-claim
spec:
accessModes:
- ReadWriteOne
resources:
requests:
storage: 2Gi

该示例依赖于事先存在的一个Ceph存储集群,这里假设其监视器的地址为192.168.50.32、192.168.50.33、192.168.50.34三个主机,并且集群上的存储池kube中存在创建好的映像Redis,此映像拥有xfs文件系统。Ceph客户端访问集群时需要事先完成认证之后才能进行后续访问操作,认证信息保存于名为ceph-secret的Secret资源对象中。

更多关于reph的配置方式,参考这里

  1. GlusterFS 存储卷

GlusterFS(Gluster File System)是一个开源的分布式文件系统,是水平扩展存储解决方案Gluster的核心,具有强大的横向扩展能力,GlusterFS通过扩展能够支持数PB存储容量和处理数千客户端。要配置Pod资源使用GlusterFS存储卷,需要事先满足以下前提条件。

  • 存在某个可用集群,
  • 在GlusterFS集群创建了Pod需要使用的Volume,
  • Kubernetes集群各个节点安装GlusterFS客户端程序包(glusterfs和glusterfs-fuse),

Pod定义GlusterFS类型Volume,包含的字段有,

  • endpoint<string>:访问入口,必选字段,
  • path<string>:用到GlusterFS集群的卷路径,
  • readOnly<boolean>:是否为只读卷,

下面是一个定义在vol-glusterfs.yaml配置文件中的Pod资源示例,它使用了GlusterFS存储卷持久保存应用数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: v1
kind: Pod
metadata:
name: vol-glusterfs-pod
labels:
app: vol-glusterfs-pod
spec:
containers:
- name: redis
image: redis:alpine
ports:
- containerPort: 6379
name: redisport
volumeMounts:
- mountPath: /data
name: redisdata
imagePullPolicy: IfNotPresent
volumes:
- name: redisdata
glusterfs:
endpoints: glusterfs-endpoints
path: kube-redis
readOnly: false
restartPolicy: Always

另外创建Endpoints资源glusterfs-endpoints,以用于Pod资源访问,