K8S存储总结&持久化存储解决方案(以NFS为例)
当pod的存储方案设定为emptydir的时候,pod启动时,就会在pod所在节点的磁盘空间开辟出一块空卷,最开始里面是什么都没有的,pod启动后容器产生的数据会存放到那个空卷中。一旦pod容器消失,节点上开辟出的这个临时卷就会随着pod的销毁而销毁一般来说emptydir的用途都是用来充当临时存储空间,例如一些不需要数据持久化的微服务,我们都可以用emptydir来当做微服务pod的存储方案。
目录
2.Persistent Volume Claim(PVC)
3.3 创建StorageClass,指定provisioner
K8S存储
一、emptydir
1. emptydir是什么
当pod的存储方案设定为emptydir的时候,pod启动时,就会在pod所在节点的磁盘空间开辟出一块空卷,最开始里面是什么都没有的,pod启动后容器产生的数据会存放到那个空卷中。
一旦pod容器消失,节点上开辟出的这个临时卷就会随着pod的销毁而销毁
一般来说emptydir的用途都是用来充当临时存储空间,例如一些不需要数据持久化的微服务,我们都可以用emptydir来当做微服务pod的存储方案
2. emptydir例子
# emptydir.yaml
---
apiVersion: v1
kind: Namespace
metadata:
name: dev
---
apiVersion: v1
kind: Pod
metadata:
name: test-emptydir
namespace: dev
spec:
containers:
- image: nginx:1.20.0
name: test-emptydir
volumeMounts:
- mountPath: /usr/share/nginx/html #挂载到容器中的路径
name: cache-volume
volumes:
- name: cache-volume
emptyDir: {} #指定存储方式为emptydir
验证emptydir
1.在调度节点g1-mix-online2-222上操作
docker ps -a | grep empty
2.查看容器详细信息
docker inspect c3c0fa73aa9b
3.进入宿主机挂载路径并写入文件
4.测试访问
curl 10.14.57.123
5.删除pod,查看文件是否被删除
emptydir是随pod创建而创建,然后再随pod删除而删除
二、hostpath
hostpath 是什么
hostPath类型是映射宿主机文件系统中的文件或者目录到pod里。但当pod漂移到其他node节点的时候,pod不会跨节点的去读取目录。所以hostpath只能算一种半持久化的存储方式
hostpath 例子
# hostpath.yaml
apiVersion: v1
kind: Pod
metadata:
name: test-hostpath
namespace: dev
spec:
containers:
- name: test-hostpath
image: nginx:1.20.0
volumeMounts:
- name: test-hostpath # 取个名字
mountPath: /usr/share/nginx/html # 挂载到容器中的路径
volumes:
- name: test-hostpath # 取个名字必须与上面volumeMounts中name的一致
hostPath:
# directory location on host
path: /data # node节点上宿主机的路径
# this field is optional
type: DirectoryOrCreate # path的属性
接下来就是验证:g1-mix-online2-222节点和pod内目录是否进行了关联
宿主机
1.data目录已经自动创建
2.进入data文件夹,并写入测试文件
echo "cat" > /data/index.html
3.访问测试
查看 Pod内部:
1.查找容器
docker ps -a | grep test-hostpath
2.查看容器详细信息
docker inspect 3e5431569426
3.进入容器查看文件是否存在
docker exec -it 3e5431569426 /bin/bash
接下来,删除Pod,重新拉起一个Pod,看会不会还存在这个index.html文件
此时,宿主机上的文件仍然存在
重新拉起pod,pod调度到g1-mix-online2-222节点
进入容器查看文件是否存在,可见文件仍然存在
弊端
1.宿主机宕机会导致数据全部丢失
2.pod重新调度时,未调度到上次调度的节点,而是调度到新的节点,也会导致数据丢失
三、持久化存储
1.Persistent Volume(PV)
PV 描述的,是持久化存储数据卷。这个 API 对象主要定义的是一个持久化存储在宿主机上的目录,比如一个 NFS 的挂载目录。
通常情况下,PV 对象是由运维人员事先创建在 Kubernetes 集群里待用的。比如,运维人员可以定义这样一个 NFS 类型的 PV,如下所示:
apiVersion: v1
kind: PersistentVolume # pv可以全局共享,不用指定命名空间
metadata:
name: nfs
spec:
storageClassName: manual
capacity: # 容量
storage: 1Gi # pv可用的大小
accessModes: # 访问模式
- ReadWriteMany #读写权限
nfs:
server: 10.244.1.4 # NFS服务器地址
path: "/" # NFS的路径
2.Persistent Volume Claim(PVC)
PVC 描述的,则是 Pod 所希望使用的持久化存储的属性。比如,Volume 存储的大小、可读写权限等等。
PVC 对象通常由开发人员创建,比如,开发人员可以声明一个 1 GiB 大小的 PVC
apiVersion: v1
kind: PersistentVolumeClaim #需与使用pvc的pod在同一个namespace下
metadata:
name: nfs
spec:
accessModes: # 访问模式
- ReadWriteMany #读写权限
storageClassName: manual
resources:
requests:
storage: 1Gi # PVC允许申请的大小
而用户创建的 PVC 要真正被容器使用起来,就必须先和某个符合条件的 PV 进行绑定。
这里要检查的条件,包括两部分:
第一个条件,是 PV 和 PVC 的 spec 字段。比如,PV 的存储(storage)大小,就必须满足 PVC 的要求。
第二个条件,是 PV 和 PVC 的 storageClassName 字段需要相同。
PS: 也可以不使用storageClassName字段,通过PVC定义的 accessModes
读写权限,和storage
定义的1G内存,PVC会自动找到符合这些配置的PV进行绑定。一个PV被PVC绑定后,不能被别的PVC绑定。
在成功地将 PVC 和 PV 进行绑定之后,Pod 就能够像使用 hostPath 等常规类型的 Volume 一样,在自己的 YAML 文件里声明使用这个 PVC 了,如下所示:
apiVersion: v1
kind: Pod # 如果前面的PVC指定了命名空间这里必须指定与PVC一致的命名空间,否则PVC不可用
metadata:
labels:
role: web-frontend
spec:
containers:
- name: web
image: nginx
ports:
- name: web
containerPort: 80
volumeMounts:
- name: nfs # 取个名字,与下面的volumes name 要一致
mountPath: "/usr/share/nginx/html" # 容器中的路径
volumes:
- name: nfs #
persistentVolumeClaim:
claimName: nfs # 引用前面声明的PVC
3.PV/PVC结合NFS使用实践(持久存储)
简单来说,要使用持久化存储,就需要使用pvc去跟pv去申请,然后pv查看自己有没有合适的存储空间卷,有合适的就与pvc进行绑定。pv与pvc是一一对应绑定的。
创建顺序:后端存储—pv—pvc—pod
1. NFS服务器搭建
NFS 是什么? nfs(network file system) 网络文件系统,是FreeBSD支持的文件系统中的一种,允许网络中的计算机之间通过TCP/IP网络共享资源
(1)搭建 NFS服务器
找一台centos 7机器,执行以下脚本,搭建 NFS服务器:
# 操作节点为NFS服务器
# 安装nfs
yum -y install nfs-utils rpcbind
# 创建nfs目录
mkdir -p /nfs/data/
mkdir -p /nfs/data/k8s
# 授予权限
chmod -R 777 /nfs/data
# 编辑export文件
vim /etc/exports
/nfs/data *(rw,no_root_squash,sync) # 这里给的是root权限---生产环境不推荐
# 或者/nfs/data 0.0.0.0/0(rw,sync,all_squash) # 所有用户权限被映射成服务端上的普通用户nobody,权限被压缩
# 使得配置生效
exportfs -r
# 查看生效
exportfs
# 启动rpcbind、nfs服务
systemctl start rpcbind && systemctl enable rpcbind #端口是111
systemctl start nfs && systemctl enable nfs # 端口是 2049
# 查看rpc服务的注册情况
rpcinfo -p localhost
# showmount测试
showmount -e ip(ip地址)
验证nfs是否启动
(2)在K8S集群所有node节点上安装NFS客户端
# 操作节点为k8s集群所有node节点
yum -y install nfs-utils rpcbind
# systemctl start nfs && systemctl enable nfs
2. 创建pv、pvc、pod
# vim nginx-pv-demo.yaml
# 定义PV
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nginx-pv # pv的名称
spec:
accessModes: # 访问模式
- ReadWriteMany # PV以read-write挂载到多个节点
capacity: # 容量
storage: 2Gi # pv可用的大小
nfs:
path: /nfs/data/ # NFS的挂载路径
server: 10.16.216.221 # NFS服务器地址
---
# 定义PVC,用于消费PV
# 通过PVC定义的 accessModes 读写权限,和storage定义的2G内存,PVC会自动找到符合这些配置的PV进行绑定。一个PV被PVC绑定后,不能被别的PVC绑定。
apiVersion: v1
kind: PersistentVolumeClaim # 类型
metadata:
name: nginx-pvc # PVC 的名字
namespace: dev # 命名空间
spec:
accessModes: # 访问模式
- ReadWriteMany # PVC以read-write挂载到多个节点
resources:
requests:
storage: 2Gi # PVC允许申请的大小
---
# 定义Pod,指定需要使用的PVC
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-pvc
namespace: dev # 如果前面的PVC指定了命名空间这里必须指定与PVC一致的命名空间,否则PVC不可用
spec:
selector:
matchLabels:
app: nginx-pvc
template:
metadata:
labels:
app: nginx-pvc
spec:
containers:
- name: nginx-test-pvc
image: nginx:1.20.0
imagePullPolicy: IfNotPresent
ports:
- name: web-port
containerPort: 80
protocol: TCP
volumeMounts:
- name: nginx-persistent-storage # 取个名字,与下面的volumes的名字要一致
mountPath: /usr/share/nginx/html # 容器中的路径
volumes:
- name: nginx-persistent-storage
persistentVolumeClaim:
claimName: nginx-pvc # 引用前面声明的PVC
注意:如果在NFS服务器上定义的路径是/nfs/data/nginx
首先我们需要去 /nfs/data
下创建 nginx
的文件夹,不然pod是启动不起来的 接下来,在master节点上启动。
3. 验证PV-PVC-NFS存储结果
(1)pod中创建文件,NFS服务器查看文件
pod中数据存储目录创建一个index.html,然后到nfs服务器上看有没有。
pod创建并写入文件
nfs服务器查看文件
(2) NFS数据存储目录里创建文件,在Pod中查看文件
nfs中数据存储目录创建一个文件,然后到pod中看有没有。
nfs中创建文件:
进入pod查看文件是否存在:
(3)删除Pod,NFS服务器查看文件
将pod删除,重新拉起之后进入pod查看文件是否存在
4.回收PV
当 PV 不再需要时,可通过删除 PVC 回收
必须先删除pod再删除pvc,因为pod绑定了pvc,如果先删pvc会出现pvc一直处于删除状态而无法删除
PV 生命周期总共四个阶段 :
Available(可用)—— 可用状态,尚未被 PVC 绑定。
Bound(已绑定)—— 绑定状态,已经与某个 PVC 绑定。
Released(已释放)—— 与之绑定的 PVC 已经被删除,但资源尚未被集群回收。
Failed(失败)—— 当删除 PVC 清理资源,自动回收卷时失败,所以处于故障状态
PV空间的回收策略:通过PV定义中的persistentVolumeReclaimPolicy字段进行设置,可选
-
Recycle:会清除数据,自动回收。
-
Retain(默认):需要手动清理回收。
-
Delete:云存储专用的回收空间使用命令。
4、StorageClass
1.StorageClass
1.1 什么是StorageClass
StorageClass 对象的作用,其实就是创建 PV 的模板,具体地说,StorageClass 对象会定义如下两个部分内容:
第一,PV 的属性。比如,存储类型、Volume 的大小等等。
第二,创建这种 PV 需要用到的存储插件。比如,Ceph 等等,即存储制备器。
有了这样两个信息之后,Kubernetes 就能够根据用户提交的 PVC,找到一个对应的 StorageClass 了。然后,Kubernetes 就会调用该 StorageClass 声明的存储插件,创建出需要的 PV。
StorageClass 对象的命名很重要,用户使用这个命名来请求生成一个特定的类。 当创建 StorageClass 对象时,管理员设置 StorageClass 对象的命名和其他参数,一旦创建了对象就不能再对其更新。
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: managed-nfs-storage #一旦创建,StorageClass名字不可更改
provisioner: nfs-storage # 存储插件的名字,这里的名称要和provisioner配置文件中的环境变量PROVISIONER_NAME保持一致
# 若为公有云,此处填写公有云存储插件的名,例:provisioner: kubernetes.io/aws-ebs
parameters: #具体存储插件相关信息
server: "10.16.216.221 "
path: "/nfs/data/k8s"
readOnly: "false"
1.2 为什么需要StorageClass
1)在一个大规模的Kubernetes集群里,可能有成千上万个PVC,这就意味着运维人员必须实现创建出这个多个PV,此外,随着项目的需要,会有新的PVC不断被提交,那么运维人员就需要不断的添加新的,满足要求的PV,否则新的Pod就会因为PVC绑定不到PV而导致创建失败。
2)通过 StorageClass 的定义,管理员可以将存储资源定义为某种类型的资源,比如快速存储、慢速存储等,用户根据 StorageClass 的描述就可以非常直观的知道各种存储资源的具体特性了,这样就可以根据应用的特性去申请合适的存储资源了。
在动态资源供应模式下,通过StorageClass和PVC完成资源动态绑定(系统自动生成PV),并供Pod使用的存储管理机制
1.3 运行原理
要使用 StorageClass,我们就得安装对应的自动配置程序,比如我们这里存储后端使用的是 nfs,那么我们就需要使用到一个 nfs-client 的自动配置程序,我们也叫它 Provisioner(制备器),这个程序使用我们已经配置好的 nfs 服务器,来自动创建持久卷,也就是自动帮我们创建 PV。
-
自动创建的 PV 以${namespace}-${pvcName}-${pvName}这样的命名格式创建在 NFS 服务器上的共享数据目录中
-
而当这个 PV 被回收后会以archieved-${namespace}-${pvcName}-${pvName}这样的命名格式存在 NFS 服务器上。
2. StorageClass 资源
2.1 存储制备器
每个 StorageClass 都有一个制备器(Provisioner),用来决定使用哪个卷插件制备 PV。 该字段必须指定。
官方支持的StorageClass
NFS 没有内部制备器,但可以使用外部制备器。 也有第三方存储供应商提供自己的外部制备器。
3.NFS StorageClass 实战
3.1 部署流程
搭建StorageClass + NFS,大致有以下几个步骤:
-
创建一个可用的NFS Server
-
创建Service Account,这是用来管控NFS Provisioner 在k8s集群中运行的权限
-
创建StorageClass,负责建立PV并调用NFS provisioner进行工作,并让PV与PVC建立关联
-
创建NFS provisioner
NFS provisioner是一个provisioner相关的插件,需要从网络上下载,我们需要下载下来放到镜像库中 下载以后需要以pod方式运行通过deployment部署导入到本地环境中
提前下载镜像(选择一个下载即可):
镜像1 docker pull registry.cn-hangzhou.aliyuncs.com/open-ali/nfs-client-provisioner
镜像2 docker pull lizhenliang/nfs-client-provisioner
3.2 创建Service Account
(1) 准备好NFS服务器
确保nfs可以正常工作,创建持久化需要的目录。
path: /nfs/data/class
server: 10.16.216.221
(2)开启rbac权限
# rbac.yaml:#唯一需要修改的地方只有namespace,根据实际情况定义
apiVersion: v1
kind: ServiceAccount # 创建一个账户,主要用来管理NFS provisioner在k8s集群中运行的权限
metadata:
name: nfs-client-provisioner
namespace: default
---
kind: ClusterRole # 创建集群角色
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-client-provisioner-runner # 角色名
rules: # 角色权限
- apiGroups: [""]
resources: ["persistentvolumes"] # 操作的资源 #pv
verbs: ["get", "list", "watch", "create", "delete"] # 对该资源的操作权限
- apiGroups: [""]
resources: ["persistentvolumeclaims"] #pvc
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"] #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
---
kind: Role # 创建角色
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner # 角色名
namespace: default # Role需要指定名称空间,ClusterRole全局可用不需要指定namespace
rules: # 角色权限
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding # 角色绑定
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
subjects: # 角色绑定对象
- kind: ServiceAccount
name: nfs-client-provisioner
namespace: default
roleRef: # 绑定哪个角色
kind: Role
name: leader-locking-nfs-client-provisioner
apiGroup: rbac.authorization.k8s.io
3.3 创建StorageClass,指定provisioner
创建nfs-client-provisioner.yaml文件
# 创建NFS资源的StorageClass
---
apiVersion: storage.k8s.io/v1
kind: StorageClass # 创建StorageClass
metadata:
name: managed-nfs-storage
provisioner: nfs-storage #这里的名称要和provisioner配置文件中的环境变量PROVISIONER_NAME保持一致
parameters:
server: "10.16.216.221 "
path: "/nfs/data/k8s"
readOnly: "false"
---
# 创建NFS provisioner
apiVersion: apps/v1
kind: Deployment # 部署nfs-client-provisioner
metadata:
name: nfs-client-provisioner
labels:
app: nfs-client-provisioner
namespace: default #与RBAC文件中的namespace保持一致
spec:
replicas: 1
selector:
matchLabels:
app: nfs-client-provisioner
strategy:
type: Recreate
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner # 指定serviceAccount!
containers:
- name: nfs-client-provisioner
image: registry.cn-hangzhou.aliyuncs.com/open-ali/nfs-client-provisioner #镜像地址
volumeMounts: # 挂载数据卷到容器指定目录
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME # 配置provisioner的Name
value: nfs-storage # 确保该名称与 StorageClass 资源中的provisioner名称保持一致
- name: NFS_SERVER #绑定的nfs服务器
value: 10.16.216.221
- name: NFS_PATH #绑定的nfs服务器目录
value: /nfs/data/k8s
volumes: # 申明nfs数据卷
- name: nfs-client-root
nfs:
server: 10.16.216.221
path: /nfs/data/k8s
3.4 创建pod测试
创建pod,声明PVC进行测试
# 申明一个PVC,指定StorageClass
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: test-claim
spec:
storageClassName: managed-nfs-storage
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Mi
---
# 创建测试pod,查看是否可以正常挂载
kind: Pod
apiVersion: v1
metadata:
name: test-pod
spec:
containers:
- name: test-pod
image: nginx:1.20.0
imagePullPolicy: IfNotPresent
volumeMounts:
- name: nfs-pvc # 挂载数据卷
mountPath: "/usr/share/nginx/html"
restartPolicy: "Never"
volumes:
- name: nfs-pvc
persistentVolumeClaim: # 数据卷挂载的是pvc
claimName: test-claim #与PVC名称保持一致
进入pod,向指定目录写文件
验证成功
更多推荐
所有评论(0)