目录
2.Persistent Volume Claim(PVC)
3.3 创建StorageClass,指定provisioner
当pod的存储方案设定为emptydir的时候,pod启动时,就会在pod所在节点的磁盘空间开辟出一块空卷,最开始里面是什么都没有的,pod启动后容器产生的数据会存放到那个空卷中。
一旦pod容器消失,节点上开辟出的这个临时卷就会随着pod的销毁而销毁
一般来说emptydir的用途都是用来充当临时存储空间,例如一些不需要数据持久化的微服务,我们都可以用emptydir来当做微服务pod的存储方案
- # 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类型是映射宿主机文件系统中的文件或者目录到pod里。但当pod漂移到其他node节点的时候,pod不会跨节点的去读取目录。所以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重新调度时,未调度到上次调度的节点,而是调度到新的节点,也会导致数据丢失
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的路径
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
简单来说,要使用持久化存储,就需要使用pvc去跟pv去申请,然后pv查看自己有没有合适的存储空间卷,有合适的就与pvc进行绑定。pv与pvc是一一对应绑定的。
创建顺序:后端存储—pv—pvc—pod
NFS 是什么? nfs(network file system) 网络文件系统,是FreeBSD支持的文件系统中的一种,允许网络中的计算机之间通过TCP/IP网络共享资源
找一台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节点上启动。
pod中数据存储目录创建一个index.html,然后到nfs服务器上看有没有。
pod创建并写入文件
nfs服务器查看文件
nfs中数据存储目录创建一个文件,然后到pod中看有没有。
nfs中创建文件:
进入pod查看文件是否存在:
将pod删除,重新拉起之后进入pod查看文件是否存在
当 PV 不再需要时,可通过删除 PVC 回收
必须先删除pod再删除pvc,因为pod绑定了pvc,如果先删pvc会出现pvc一直处于删除状态而无法删除
PV 生命周期总共四个阶段 :
Available(可用)—— 可用状态,尚未被 PVC 绑定。
Bound(已绑定)—— 绑定状态,已经与某个 PVC 绑定。
Released(已释放)—— 与之绑定的 PVC 已经被删除,但资源尚未被集群回收。
Failed(失败)—— 当删除 PVC 清理资源,自动回收卷时失败,所以处于故障状态
PV空间的回收策略:通过PV定义中的persistentVolumeReclaimPolicy字段进行设置,可选
Recycle:会清除数据,自动回收。
Retain(默认):需要手动清理回收。
Delete:云存储专用的回收空间使用命令。
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)在一个大规模的Kubernetes集群里,可能有成千上万个PVC,这就意味着运维人员必须实现创建出这个多个PV,此外,随着项目的需要,会有新的PVC不断被提交,那么运维人员就需要不断的添加新的,满足要求的PV,否则新的Pod就会因为PVC绑定不到PV而导致创建失败。
2)通过 StorageClass 的定义,管理员可以将存储资源定义为某种类型的资源,比如快速存储、慢速存储等,用户根据 StorageClass 的描述就可以非常直观的知道各种存储资源的具体特性了,这样就可以根据应用的特性去申请合适的存储资源了。
在动态资源供应模式下,通过StorageClass和PVC完成资源动态绑定(系统自动生成PV),并供Pod使用的存储管理机制
要使用 StorageClass,我们就得安装对应的自动配置程序,比如我们这里存储后端使用的是 nfs,那么我们就需要使用到一个 nfs-client 的自动配置程序,我们也叫它 Provisioner(制备器),这个程序使用我们已经配置好的 nfs 服务器,来自动创建持久卷,也就是自动帮我们创建 PV。
自动创建的 PV 以${namespace}-${pvcName}-${pvName}这样的命名格式创建在 NFS 服务器上的共享数据目录中
而当这个 PV 被回收后会以archieved-${namespace}-${pvcName}-${pvName}这样的命名格式存在 NFS 服务器上。
每个 StorageClass 都有一个制备器(Provisioner),用来决定使用哪个卷插件制备 PV。 该字段必须指定。
官方支持的StorageClass
NFS 没有内部制备器,但可以使用外部制备器。 也有第三方存储供应商提供自己的外部制备器。
搭建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
确保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
创建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
创建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,向指定目录写文件
验证成功