在开始介绍k8s持久化存储前,我们有必要了解一下k8s的emptydir和hostpath、configmap以及secret的机制和用途
1、Emptydir
EmptyDir是一个空目录,他的生命周期和所属的 Pod 是完全一致的,pod删掉目录消失
2、Hostpath
Hostpath会把宿主机上的指定卷加载到容器之中,如果 Pod 发生跨主机的重建,其内容就难保证了
3、Configmap
ConfigMap跟Secrets类似,但是ConfigMap可以更方便的处理不包含敏感信息的字符串。
当ConfigMap以数据卷的形式挂载进Pod的时,这时更新ConfigMap(或删掉重建ConfigMap),Pod内挂载的配置信息会热更新。这时可以增加一些监测配置文件变更的脚本,然后reload对应服务
4、Secret
Secret来处理敏感数据,比如密码、Token和密钥,相比于直接将敏感数据配置在Pod的定义或者镜像中,Secret提供了更加安全的机制(Base64加密),防止数据泄露。Secret的创建是独立于Pod的,以数据卷的形式挂载到Pod中,Secret的数据将以文件的形式保存,容器通过读取文件可以获取需要的数据
==========================================================
下面我们来介绍一下k8s的持久化存储方案,目前k8s支持的存储方案主要如下:
分布式文件系统:NFS/GlusterFS/CephFS
公有云存储方案:AWS/GCE/Auzre
这里使用nfs测试一下
我们这里为了演示方便,决定使用相对简单的 NFS 这种存储资源,接下来我们在节点10.20.2.116上来安装 NFS 服务,数据目录:/data/k8s/
具体安装方法百度有好多,安装完nfs挂载到k8s的node节点上面就行
完成之后就可以部署pv,和pvc
什么是PV,pvc
PV 的全称是:PersistentVolume(持久化卷),是对底层的共享存储的一种抽象,主要是定义一个磁盘的大小
PVC 的全称是:PersistentVolumeClaim(持久化卷声明),PVC 是用户存储的一种声明,PVC 和 Pod 比较类似,Pod 消耗的是节点,PVC 消耗的是 PV 资源
动态pv:StorageClass
PVC 请求到一定的存储空间也很有可能不足以满足应用对于存储设备的各种需求,而且不同的应用程序对于存储性能的要求可能也不尽相同,比如读写速度、并发性能等,为了解决这一问题,Kubernetes 又为我们引入了一个新的资源对象:StorageClass,通过 StorageClass 的定义,用户根据 StorageClass 的描述就可以非常直观的知道各种存储资源的具体特性了,这样就可以根据应用的特性去申请合适的存储资源了
PV 作为存储资源,主要包括存储能力、访问模式、存储类型、回收策略等关键信息
1G 的存储空间,访问模式为 ReadWriteOnce,回收策略为 Recyle
状态是 Available,表示 pv1 就绪,可以被 PVC 申请。我们来分别对上面的属性进行一些解读。
Capacity(存储能力)
一般来说,一个 PV 对象都要指定一个存储能力,通过 PV 的 capacity属性来设置的,目前只支持存储空间的设置,就是我们这里的 storage=1Gi,不过未来可能会加入 IOPS、吞吐量等指标的配置。
====================================================================================
AccessModes(访问模式)
AccessModes 是用来对 PV 进行访问模式的设置,用于描述用户应用对存储资源的访问权限,访问权限包括下面几种方式:
ReadWriteOnce(RWO):读写权限,但是只能被单个节点挂载
ReadOnlyMany(ROX):只读权限,可以被多个节点挂载
ReadWriteMany(RWX):读写权限,可以被多个节点挂载
persistentVolumeReclaimPolicy(回收策略)
=======================================================================================
我这里指定的 PV 的回收策略为 Recycle,目前 PV 支持的策略有三种:
Retain(保留)- 保留数据,需要管理员手工清理数据
Recycle(回收)- 清除 PV 中的数据,效果相当于执行 rm -rf /thevoluem/*
Delete(删除)- 与 PV 相连的后端存储完成 volume 的删除操作,当然这常见于云服务商的存储服务,比如 ASW EBS。
不过需要注意的是,目前只有 NFS 和 HostPath 两种类型支持回收策略。当然一般来说还是设置为 Retain 这种策略保险一点。
=======================================================================================
状态
一个 PV 的生命周期中,可能会处于4中不同的阶段:
Available(可用):表示可用状态,还未被任何 PVC 绑定
Bound(已绑定):表示 PVC 已经被 PVC 绑定
Released(已释放):PVC 被删除,但是资源还未被集群重新声明
Failed(失败): 表示该 PV 的自动回收失败
接下来创建pvc
请求 1Gi 的存储容量,访问模式也是 ReadWriteOnce,YAML 文件如下:(pvc-nfs.yaml)
PV 也是 Bound 状态了,对应的声明是 default/pvc-nfs,就是 default 命名空间下面的 pvc-nfs,证明我们刚刚新建的 pvc-nfs 和我们的 pv-nfs 绑定成功了
为啥会pv和pvc绑定呢,这是系统自动绑定的,他们都在默认的命名空间default下面系统自动给绑定
为了验证我们创建一个pvc测试一下效果对比一下
说明pvc2没有合适的pv可以绑定
接下来创建pv
系统自动给我们pv和pvc绑定的,
容量大于了 PV 里面的容量的话,是没办法进行绑定的,大家可以下去自己测试一下。
所以容量一定要对应起来
修改相同之后就对应起来
我们已经知道怎么创建 PV 和 PVC 了,现在我们就来使用下我们的 PVC,接下来使用nginx 的镜像来测试下:(nfs-pvc-deploy.yaml)
cat nfs-pvc-deploy.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nfs-pvc
spec:
replicas: 3
template:
metadata:
labels:
app: nfs-pvc
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:- containerPort: 80
name: web
volumeMounts: - name: www
mountPath: /usr/share/nginx/html
volumes:
- containerPort: 80
- name: www
persistentVolumeClaim:
claimName: pvc2-nfs
apiVersion: v1
kind: Service
metadata:
name: nfs-pvc
labels:
app: nfs-pvc
spec:
type: NodePort
ports:
- port: 80
targetPort: web
selector:
app: nfs-pvc
使用 nginx 镜像,将容器的 /usr/share/nginx/html 目录通过 volume 挂载到名为 pvc2-nfs 的 PVC 上面,然后创建一个 NodePort 类型的 Service 来暴露服务
访问一下
为啥403没有资源呢,因为我们把nginx默认的目录映射到了pvc上面,pvc上面没东西,接下来我们创建点东西试试
========================================================================================================================
如果我们又有一个新的 nginx 容器也做了数据目录的挂载,是不是就会有冲突了啊,所以这个时候就不太好区分了,这个时候我们可以在 Pod 中使用一个新的属性:subPath,该属性可以来解决这个问题,我们只需要更改上面的 Pod 的 YAML 文件即可
然后创建
两个nginx容器的内容是完全不一样的对吧
静态pv和pvc就演示到这里,后面开始动态的pv和pvc
注意事项
如果这个时候我们把 PV 给删除了,上面持久化的数据还会存在吗?如果是删除的 PVC 呢?在实际使用的工程中,是很有可能出现这种情况的吧?大家在使用 PV 和 PVC 的时候一定要注意这些细节,不然一不小心就把数据搞丢了
========================================================================================================================
要使用 StorageClass,我们就得安装对应的自动配置程序,比如我们这里存储后端使用的是 nfs,那么我们就需要使用到一个 nfs-client 的自动配置程序,我们也叫它 Provisioner,这个程序使用我们已经配置好的 nfs 服务器,来自动创建持久卷,也就是自动帮我们创建 PV。
后接下来我们部署 nfs-client 即可,我们也可以直接参考 nfs-client 的文档:https://github.com/kubernetes-incubator/external-storage/tree/master/nfs-client,进行安装即可
第一步:配置 Deployment,将里面的对应的参数替换成我们自己的 nfs 配置(nfs-client.yaml)、
第二步:将环境变量 NFS_SERVER 和 NFS_PATH 替换,当然也包括下面的 nfs 配置,我们可以看到我们这里使用了一个名为 nfs-client-provisioner 的serviceAccount,所以我们也需要创建一个 sa,然后绑定上对应的权限:(nfs-client-sa.yaml)
cat nfs-client-sa.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-client-provisioner-runner
rules:
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "delete"] - apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "update"] - apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"] - apiGroups: [""]
resources: ["events"]
verbs: ["list", "watch", "create", "update", "patch"] - apiGroups: [""]
resources: ["endpoints"]
verbs: ["create", "delete", "get", "list", "watch", "patch", "update"]
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
namespace: default
roleRef:
kind: ClusterRole
name: nfs-client-provisioner-runner
apiGroup: rbac.authorization.k8s.io
我们这里新建的一个名为 nfs-client-provisioner 的ServiceAccount,然后绑定了一个名为 nfs-client-provisioner-runner 的ClusterRole,而该ClusterRole声明了一些权限,其中就包括对persistentvolumes的增、删、改、查等权限,所以我们可以利用该ServiceAccount来自动创建 PV。
第三步:nfs-client 的 Deployment 声明完成后,我们就可以来创建一个StorageClass对象了:(nfs-client-class.yaml)
我们声明了一个名为 course-nfs-storage 的StorageClass对象,注意下面的provisioner对应的值一定要和上面的Deployment下面的 PROVISIONER_NAME 这个环境变量的值一样。
上面把StorageClass资源对象创建成功了,接下来我们来通过一个示例测试下动态 PV,首先创建一个 PVC 对象:(test-pvc.yaml)
我们这里声明了一个 PVC 对象,采用 ReadWriteMany 的访问模式,请求 1Mi 的空间,但是我们可以看到上面的 PVC 文件我们没有标识出任何和 StorageClass 相关联的信息,那么如果我们现在直接创建这个 PVC 对象能够自动绑定上合适的 PV 对象吗?显然是不能的(前提是没有合适的 PV),我们这里有两种方法可以来利用上面我们创建的 StorageClass 对象来自动帮我们创建一个合适的 PV:就是上面圈红圈的是第一种方式
第二种方式:
kubectl patch storageclass course-nfs-storage -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
最好用第一种,直接添加就行然后直接create
我们可以看到一个名为 test-pvc 的 PVC 对象创建成功了,状态已经是 Bound 了,是不是也产生了一个对应的 VOLUME 对象,最重要的一栏是 STORAGECLASS,现在是不是也有值了,就是我们刚刚创建的 StorageClass 对象 course-nfs-storage。以看到是不是自动生成了一个关联的 PV 对象,访问模式是 RWX,回收策略是 Delete,这个 PV 对象并不是我们手动创建的吧,这是通过我们上面的 StorageClass 对象自动创建的。这就是 StorageClass 的创建方法。
接下来测试一下
这个 Pod 非常简单,就是用一个 busybox 容器,在 /mnt 目录下面新建一个 SUCCESS 的文件,然后把 /mnt 目录挂载到上面我们新建的 test-pvc 这个资源对象上面了,要验证很简单,只需要去查看下我们 nfs 服务器上面的共享数据目录下面是否有 SUCCESS 这个文件即可
另外我们可以看到我们这里是手动创建的一个 PVC 对象,在实际工作中,使用 StorageClass 更多的是 StatefulSet 类型的服务,StatefulSet 类型的服务我们也可以通过一个 volumeClaimTemplates 属性来直接使用 StorageClass,如下:(test-statefulset-nfs.yaml)
实际上 volumeClaimTemplates 下面就是一个 PVC 对象的模板,就类似于我们这里 StatefulSet 下面的 template,实际上就是一个 Pod 的模板,我们不单独创建成 PVC 对象,而用这种模板就可以动态的去创建了对象了,这种方式在 StatefulSet 类型的服务下面使用得非常多。
去nfs上面的pv看看有没有数据
多出来了3个目录 信息
其实上面的都是单独联系的,生产中需要多磨炼多学习才好
学习的时候出现任何问题可以私信和联系我