共享存储的场景有两种,分为pod内容器的数据共享,和多pod之间的数据共享。pod内的容器,有时会因为业务需要进行数据共享。比如,pod中有两个容器,一个容器需要收集另一个容器的日志,此时,让俩个容器挂载同一个volumn,避免容器间本地通信,提高效率。多pod之间可能会涉及到数据的同步,但是通过共享存储,可以有效解决数据同步可能出现的问题。
k8s提供了以volumn插件为基础的存储系统,有很多存储实现了该插件,其中每一个插件都可以实现共享存储,只是实现功能上,会有一定的差异。现将其分类如下:
宿主机: emptyDir、HostPath
集群存储: configmap、secret
云存储: awsElasticBlockStore、gcePersistentDisk等
外部存储: glusterfs、Ceph等
emptyDir是宿主机上创建的临时目录,其优点是能方便地为Pod中的容器提供共享存储,不需要额外的配置。但它不具备持久性,且只能和一个pod绑定,如果Pod重启或删除,emptyDir也就没有了。根据这个特性,emptyDir特别适合Pod中容器需要临时共享存储空间的场景,比如生产者消费者用例。具体使用方式如下:
apiVersion: v1
kind: Pod
metadata:
name: test-emptydir
spec:
containers:
- image: gcr.io/google_containers/test-webserver
name: test-container
volumeMounts:
- mountPath: /cache
name: cache-volume
volumes:
- name: cache-volume
emptyDir: {}
hostpath会把宿主机的目录作为volumn挂载到pod的容器之中,比如一些容器需要在内部使用docker daemon, 则需要挂载宿主机的 /var/lib/docker 文件。Pod中的容器也可以共享hostpath存储,和emptyDir不同的是,如果pod重启或销毁,hostpath对应的目录仍然会存在。但缺点是pod和宿主机节点产生了耦合,当pod漂移后,如果想要继续使用宿主机上的文件,就需要给pod指定宿主机,这一定程度上,限制了Pod的使用。hostpath具体使用方式如下:
apiVersion: v1
kind: Pod
metadata:
name: test-hospath
spec:
containers:
- image: k8s.gcr.io/test-webserver
name: test-container
volumeMounts:
- mountPath: /var/lib/docker
name: hostpath-volume
volumes:
- name: hostpath-volume
hostPath:
path: /var/lib/docker
综上:宿主机类存储,emptryDir只能在一个pod中,共享临时数据,pod删除重启后,数据也会删除。hostpath 可以共享单pod、多pod的数据,pod删除或重启,数据也可以保留,但必须将pod和宿主机进行绑定,另外,当宿主机宕机,数据也会丢失。
configMap和secret可以作为volumn被pod使用,可以进行单pod,多pod数据共享,它们会在K8s集群的etcd中保存,但只适合存储配置信息和一些敏感文件。configmap具体使用方式如下:
//创建configmap
kind: ConfigMap
apiVersion: v1
metadata:
name: lykops-config
namespace: default
labels:
software: apache
project: lykops
app: configmap
version: v1
data:
PWD: /
user: lykops
mysql.config : |-
username: lykops
host: localhost
port: 3306
---------------------
//在pod中使用
apiVersion: v1
kind: Pod
metadata:
name: lykops-cm-pod
spec:
containers:
- name: lykops-cm-pod
image: web:apache
command: ['sh',/etc/run.sh]
env:
- name: SPECIAL_USER
valueFrom:
configMapKeyRef:
name: lykops-config
key: username
volumeMounts:
- name: config-volume
mountPath: /data/
volumes:
- name: config-volume
configMap:
name: lykops-config
Note:configmap和secret虽然可以满足共享存储的需求,数据也能持久化,但只适合存储配置信息,并不适合存储数据。
云存储是云提供商提供的网络存储方案,如awsElasticBlockStore, 使用时,只需将AWS EBS volumn 绑定到Pod中。 当删除Pod时,EBS volumn的数据将被保留,并且仅卸载volumn,volumn也可以在单pod、多pod间共享。但缺点是,正在运行pod的节点,必须是此云提供商的节点,这和云提供商深度耦合,也不可取。
具体的使用方式是需要创建先创建云存储volumn,然后在pod中使用,如下:
//创建 aws的volumn
aws ec2 create-volume --availability-zone=eu-west-1a --size=10 --volume-type=gp2
----------
//pod中使用aws volumn
apiVersion: v1
kind: Pod
metadata:
name: test-ebs
spec:
containers:
- image: k8s.gcr.io/test-webserver
name: test-container
volumeMounts:
- mountPath: /test-ebs
name: test-volume
volumes:
- name: test-volume
# This AWS EBS volume must already exist.
awsElasticBlockStore:
volumeID:
fsType: ext4
k8s中可以使用分布式存储系统glusterfs、ceph,完成单pod容器、多pod之间的数据共享。当pod挂了,volumn的数据将会保留。重新调度启动后,pod可能在不同的Node上启动,但连接的都是同一个分布式存储卷,此外,分布式存储还提供了副本机制、高可用、易扩展等特性,能最大程度的解决原来单点存储的不可靠性。相对于 emptyDir 和 hostPath,这些 Volume 类型的最大特点就是不依赖 Kubernetes。Volume 的底层基础设施由独立的存储系统管理,与 Kubernetes 集群是分离的。数据被持久化后,即使整个 Kubernetes 崩溃也不会受损。
我们这里只是简单的说下分布式存储,解决的问题,和使用方式,后面我们会详细说明ceph和glusterfs的功能和使用。
apiVersion: v1
kind: Pod
metadata:
name: test-ceph
spec:
containers:
- image: k8s.gcr.io/test-webserver
name: test-container
volumeMounts:
- mountPath: /test-ebs
name: ceph-volume
volumes:
- name: ceph-volume
cephfs:
path: /ceph/cephfs
monitor: "1.2.3.4:6789"
secretFile: /etc/ceph/admin.secret
nfs volumn和分布式存储一样,也可以完美的解决单pod容器、多pod之间的数据共享,但是nfs服务器只有单节点,且传输效率低下。
从上面我们知道,volumn提供了很好的数据持久化方案,但是使用起来很是麻烦,比如,我们后面需要使用的分布式存储,当要在pod中使用volumn时,就必须先在分布式存储中创建此目录。这意味着开发人员和存储系统耦合太高。此外,这种方式使用存储,并不能限制volumn的大小,设置访问模式和回收策略,功能很单一,所以k8s提供了pv和pvc机制。
①:PV是k8s对底层网络共享存储的抽象,将共享存储定义为一种"资源",比如节点(Node)是pod可以“消费”的资源。PV由管理员进行创建和配置,它与共享存储的具体实现直接相关,例如GlusterFS、Ceph、RBD或GCE/AWS公有云提供的共享存储。
②:PVC则是用户对于存储资源的一个“申请”。就像Pod“消费”Node的资源一样,PVC会“消费”PV资源。PVC通常由用户创建和维护。需要为 Pod 分配存储资源时,用户可以创建一个 PVC,指明存储资源的容量大小和访问模式(比如只读)等信息,k8s会查找并提供满足条件的PV供pod使用。
PV作为存储资源,主要包括存储能力、访问模式、存储类型、回收策略、后端存储类型等关键信息的设置。
示例:下面是一个存储大小为1G,访问模式为read-write,回收策略为自动回收、存储后端类型为nfs的pv:
kind: PersistentVolume
apiVersion: v1
metadata:
name: test-pv
labels:
type: local
spec:
storageClassName: nfs
capacity:
storage: 1Gi
persistentVolumeReclaimPolicy: Recycle
accessModes:
- ReadWriteOnce
nfs:
path: "/nfsdata/pv1"
server: 1.2.3.4
pv的核心设置和策略:
①:pv的访问模式:
ReadWriteOnce: PV 能以 read-write 模式 mount 到单个节点
ReadOnlyMany: PV 能以 read-only 模式 mount 到多个节点
ReadWriteMany: PV 能以 read-write 模式 mount 到多个节点
②:存储回收策略:
Retain:需要管理员手工回收。
Recycle:清除 PV 中的数据,效果相当于执行 rm -rf /thevolume/*。
Delete:删除 Storage Provider 上的对应存储资源,例如 AWS EBS、GCE PD、Azure Disk、OpenStack Cinder Volume 等。
③:存储类别(class)
PV可以设定其存储的类别(Class),通过storageClassName参数指定一个StorageClass资源对象的名称。具有特定“类别”的PV只能与请求了该“类别”的PVC进行绑定。未设定“类别”的PV则只能与不请求任何“类别”的PVC进行绑定。
④:后端存储类型:
k8s支持的PV类型很多,如GlusterFS、Ceph、nfs、云存储AzureFile、HostPath(宿主机目录,仅用于单机测试)等。每种存储类型都有各自的特点,在使用时需要根据它们各自的参数进行设置。
pvc是pod使用pv的方式,当需要在pod中使用pv时,只需要引入pvc,在pvc中定义需要使用的pv容量、访问模式、和pv类型。
示例:下面创建pvc申请pv资源,代表需要申请1GiB容量、访问模式为单点读写、存储类别为nfs的pv。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-test
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: nfs
pvc的核心设置和策略:
①:资源请求(Resources):描述对存储资源的请求,目前仅支持request.storage的设置,即存储空间大小。
②:访问模式(Access Modes):PVC也可以设置访问模式,用于描述用户应用对存储资源的访问权限。可以设置的三种访问模式与PV相同。
③:PV选择条件(Selector):通过Label Selector的设置,PVC可以对系统中已存在的PV进行筛选。系统将根据标签选择出合适的PV与该PVC进行绑定。选择条件可以使用matchLabels和matchExpressions进行设置。如果两个条件都设置了,则Selector的逻辑是两组条件同时满足才能完成匹配。
④:存储类别(Class):PVC在定义时可以设定需要的后端存储的"类别"(通过storageClassName字段指定),只有设置了该Class的PV才能被系统选出,并与该PVC进行绑定。PVC也可以不设置Class需求。如果不设置storageClassName字段的值被设置为空(storageClassName=""),则表示该PVC不要求特定的Class。此时,只会在没有class的pv中进行匹配。
PV可以看作可用的存储资源,PVC则是对存储资源的需求,PV和PVC的相互关系遵循下图所示的生命周期。
①:资源供应:创建pv,创建pv有两种方式,static(静态模式)和dynamic(动态模式)
静态模式:手动创建PV,在定义PV时需要将后端存储的特性进行设置。
动态模式:无须手动创建PV,而是通过StorageClass的设置对后端存储进行请求,此时要求PVC对存储类型进行声明,系统将自动完成PV的创建及与PVC的绑定。
②:资源绑定:定义好pvc后,k8s会根据pvc的定义,在已存在pv中,匹配满足条件的pv进行绑定。一旦找到,就将该pv和用户定义的pvc进行绑定,此时就可以在pod中使用pvc。如果没有找到满足条件的pv,该pvc会一直处于pending状态,直到等到符合条件的pv创建,pvc和pv进行绑定后,pv就不能被其他的pvc所绑定了,因此,手动创建pv的方式会造成,资源的浪费,推荐使用动态创建的pv,与pvc绑定。
③:资源使用:pod使用volumn的定义,将pvc挂载到容器的某个路径下使用。volume的类型为"persistentVolumeClaim"。这里需要注意的是,多个pod可以使用同一个PVC资源。
④:资源释放:pod对volumn使用完毕,我们可以删除pvc,此时,与pvc绑定的PV的状态会被释放,状态为release,但此时pv还不能与其他pvc进行绑定,因为pv上的数据还没有被清除,只有在清楚之后pv才能再次使用
⑤:资源回收:对于pv,可以设置指定的回收策略,比如有delete(自动删除数据),和retain(需要手工清理数据),回收后的pv可以再次被pvc使用。
storage class是k8s对存储资源更深层次的抽象,主要完成了动态创建pv的过程,而动态创建pv,与静态创建pv主要有如下优点:
①:用户不需要了解pvc申请存储的细节。
②:不用预先去创建pv让pvc进行绑定。
③:用户申请的pvc会和pv的容量完全匹配,不会造成资源的浪费。
storage核心参数和设置:
①:provisioner:后端资源的提供者,目前k8s支持的Provisioner都以"kubernetes.io/"为开头
②:parameters: 后端资源提供者的参数设置,不同的Provisioner会有不同的参数设置
示例:给出glusterfs的storage Class配置、并创建pvc在pod中使用:
// gulsterfs Class 配置
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: glusterfs-sc
provisioner: kubernetes.io/glusterfs
reclaimPolicy: Retain
parameters:
gidMax: "50000"
gidMin: "40000"
resturl: http://1.2.3.4:30088
volumetype: replicate:2
restauthenabled: "true"
restuser: "admin"
restuserkey: "123456"
# secretNamespace: "default"
# secretName: "my-secret"
创建pvc
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: kafka-pvc
namespace: kube-system
spec:
storageClassName: glusterfs-sc
accessModes:
- ReadWriteMany
resources:
requests:
storage: 20Gi
pod中使用pvc
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-use-pvc
namespace: kube-system
spec:
replicas: 1
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx:1.11.4-alpine
imagePullPolicy: IfNotPresent
name: nginx-use-pvc
volumeMounts:
- mountPath: /test
name: my-pvc
volumes:
- name: my-pvc
persistentVolumeClaim:
claimName: kafka-pvc
参考:https://kubernetes.io/docs/concepts/storage/persistent-volumes/