一.系统环境

服务器版本 docker软件版本 Kubernetes(k8s)集群版本 CPU架构
CentOS Linux release 7.4.1708 (Core) Docker version 20.10.12 v1.21.9 x86_64

Kubernetes集群架构:k8scloude1作为master节点,k8scloude2,k8scloude3作为worker节点

服务器 操作系统版本 CPU架构 进程 功能描述
k8scloude1/192.168.110.130 CentOS Linux release 7.4.1708 (Core) x86_64 docker,kube-apiserver,etcd,kube-scheduler,kube-controller-manager,kubelet,kube-proxy,coredns,calico k8s master节点
k8scloude2/192.168.110.129 CentOS Linux release 7.4.1708 (Core) x86_64 docker,kubelet,kube-proxy,calico k8s worker节点
k8scloude3/192.168.110.128 CentOS Linux release 7.4.1708 (Core) x86_64 docker,kubelet,kube-proxy,calico k8s worker节点

二.前言

Kubernetes(k8s)数据卷volumes类型众多,本文介绍使用存储类StorageClass动态制备持久卷Persistent Volume,关于静态制备持久卷Persistent Volume,请查看博客《Kubernetes(k8s)存储管理之数据卷volumes(四):持久卷Persistent Volume》https://www.cnblogs.com/renshengdezheli/p/16972289.html。

使用数据卷volumes的前提是已经有一套可以正常运行的Kubernetes集群,关于Kubernetes(k8s)集群的安装部署,可以查看博客《Centos7 安装部署Kubernetes(k8s)集群》https://www.cnblogs.com/renshengdezheli/p/16686769.html

三.静态制备和动态制备

创建持久卷Persistent Volume有两种方法:静态制备和动态制备 。

静态制备(集群管理员创建若干 PV 卷。这些卷对象带有真实存储的细节信息, 并且对集群用户可用。PV 卷对象存在于 Kubernetes API 中,可供用户消费使用),简言之就是需要先创建pv,然后才能创建PVC

动态制备 (如果管理员所创建的所有静态 PV 卷都无法与用户的 PersistentVolumeClaim 匹配, 集群可以尝试为该 PVC 申领动态制备一个存储卷。 这一制备操作是基于 StorageClass 来实现的:PVC 申领必须请求某个 存储类, 同时集群管理员必须已经创建并配置了该类,这样动态制备卷的动作才会发生。 如果 PVC 申领指定存储类为 "",则相当于为自身禁止使用动态制备的卷)。

为了基于存储类完成动态的存储制备,集群管理员需要在 API 服务器上启用 DefaultStorageClass 准入控制器。 举例而言,可以通过保证 DefaultStorageClass 出现在 API 服务器组件的 --enable-admission-plugins 标志值中实现这点;该标志的值可以是逗号分隔的有序列表。

简言之就是我们可以使用存储类StorageClass实现动态制备,不需要提前创建PV,只要创建好存储类StorageClass,用户创建PVC之后,存储类StorageClass会自动创建一个PV和PVC进行绑定。

四.存储类StorageClass

4.1 存储类StorageClass概览

StorageClass 为管理员提供了描述存储 "类" 的方法。 不同的类型可能会映射到不同的服务质量等级或备份策略,或是由集群管理员制定的任意策略。 Kubernetes 本身并不清楚各种类代表的什么。这个类的概念在其他存储系统中有时被称为 "配置文件"。

4.2 StorageClass 资源

每个 StorageClass 都包含 provisioner、parameters 和 reclaimPolicy 字段, 这些字段会在 StorageClass 需要动态制备 PersistentVolume 时会使用到。

存储制备器provisioner:每个 StorageClass 都有一个制备器(Provisioner),用来决定使用哪个卷插件制备 PV。 该字段必须指定。

回收策略reclaimPolicy:由 StorageClass 动态创建的 PersistentVolume 会在类的 reclaimPolicy 字段中指定回收策略,可以是 Delete 或者 Retain。如果 StorageClass 对象被创建时没有指定 reclaimPolicy,它将默认为 Delete。通过 StorageClass 手动创建并管理的 PersistentVolume 会使用它们被创建时指定的回收策略。

允许卷扩展allowVolumeExpansion:PersistentVolume 可以配置为可扩展。将此功能设置为 true 时,允许用户通过编辑相应的 PVC 对象来调整卷大小。此功能仅可用于扩容卷,不能用于缩小卷。

挂载选项mountOptions:由 StorageClass 动态创建的 PersistentVolume 将使用类中 mountOptions 字段指定的挂载选项。如果卷插件不支持挂载选项,却指定了挂载选项,则制备操作会失败。 挂载选项在 StorageClass 和 PV 上都不会做验证,如果其中一个挂载选项无效,那么这个 PV 挂载操作就会失败。

卷绑定模式volumeBindingMode:volumeBindingMode 字段控制了卷绑定和动态制备应该发生在什么时候。

  • Immediate 模式:默认情况下,Immediate 模式表示一旦创建了 PersistentVolumeClaim 也就完成了卷绑定和动态制备。 对于由于拓扑限制而非集群所有节点可达的存储后端,PersistentVolume 会在不知道 Pod 调度要求的情况下绑定或者制备。
  • WaitForFirstConsumer 模式:集群管理员可以通过指定 WaitForFirstConsumer 模式来解决此问题。 该模式将延迟 PersistentVolume 的绑定和制备,直到使用该 PersistentVolumeClaim 的 Pod 被创建。 PersistentVolume 会根据 Pod 调度约束指定的拓扑来选择或制备。 这些包括但不限于资源需求、 节点筛选器、 Pod 亲和性和互斥性、 以及污点和容忍度。

注意:如果你选择使用 WaitForFirstConsumer,请不要在 Pod 规约中使用 nodeName 来指定节点亲和性。 如果在这种情况下使用 nodeName,Pod 将会绕过调度程序,PVC 将停留在 pending 状态。相反,在这种情况下,你可以使用节点选择器nodeSelector指定主机名。

参数parameters:Storage Classes 的参数描述了存储类的卷。取决于制备器,可以接受不同的参数。 例如,参数 type 的值 io1 和参数 iopsPerGB 特定于 EBS PV。 当参数被省略时,会使用默认值。一个 StorageClass 最多可以定义 512 个参数。这些参数对象的总长度不能超过 256 KiB, 包括参数的键和值。

五.创建存储类StorageClass

5.1 配置NFS服务端以及共享目录

在一台机器上安装NFS服务端,k8s的两个worker安装NFS客户端。

etcd1机器作为NFS的服务端,安装NFS。

[root@etcd1 ~]# yum -y install nfs-utils

[root@etcd1 ~]# rpm -qa | grep nfs
libnfsidmap-0.25-19.el7.x86_64
nfs-utils-1.3.0-0.68.el7.2.x86_64

启动NFS

#使nfs开机自启动并现在就启动
[root@etcd1 ~]# systemctl enable nfs-server --now
Created symlink from /etc/systemd/system/multi-user.target.wants/nfs-server.service to /usr/lib/systemd/system/nfs-server.service. #查看nfs状态
[root@etcd1 ~]# systemctl status nfs-server
● nfs-server.service - NFS server and services
Loaded: loaded (/usr/lib/systemd/system/nfs-server.service; enabled; vendor preset: disabled)
Active: active (exited) since 二 2022-01-18 17:24:24 CST; 8s ago
Process: 1469 ExecStartPost=/bin/sh -c if systemctl -q is-active gssproxy; then systemctl reload gssproxy ; fi (code=exited, status=0/SUCCESS)
Process: 1453 ExecStart=/usr/sbin/rpc.nfsd $RPCNFSDARGS (code=exited, status=0/SUCCESS)
Process: 1451 ExecStartPre=/usr/sbin/exportfs -r (code=exited, status=0/SUCCESS)
Main PID: 1453 (code=exited, status=0/SUCCESS)
CGroup: /system.slice/nfs-server.service 1月 18 17:24:24 etcd1 systemd[1]: Starting NFS server and services...
1月 18 17:24:24 etcd1 systemd[1]: Started NFS server and services.

先在NFS服务端创建/dongtaijuandongying,并把目录/dongtaijuandongying共享出去

#在NFS服务端创建共享目录/dongtaijuandongying
[root@etcd1 ~]# mkdir /dongtaijuandongying [root@etcd1 ~]# vim /etc/exports #把/dongtaijuandongying目录共享出去
[root@etcd1 ~]# cat /etc/exports
/dongtaijuandongying *(rw,async,no_root_squash) [root@etcd1 ~]# exportfs -arv
exporting *:/dongtaijuandongying

5.2 配置NFS客户端

在k8s集群的worker节点安装nfs的客户端

[root@k8scloude3 ~]# yum -y install nfs-utils

 #安装nfs的客户端
[root@k8scloude2 ~]# yum -y install nfs-utils

查看etcd1(192.168.110.133)机器共享出来的目录是哪个?

[root@k8scloude2 ~]# showmount -e 192.168.110.133
Export list for 192.168.110.133:
/dongtaijuandongying *

5.3 配置StorageClass支持NFS

NFS类型的StorageClass没有内部制备器provisioner,但可以使用外部制备器。 也有第三方存储供应商提供自己的外部制备器。Kubernetes 不包含内部 NFS 驱动。你需要使用外部驱动为 NFS 创建 StorageClass。

由于k8s默认不支持nfs类型的StorageClass,需要修改参数然后自定义nfs StorageClass

[root@k8scloude1 volume]# vim /etc/kubernetes/manifests/kube-apiserver.yaml 

#添加参数如下
[root@k8scloude1 volume]# cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep RemoveSelfLink
- --feature-gates=RemoveSelfLink=false

重启kubelet使配置生效

[root@k8scloude1 volume]# systemctl restart kubelet

[root@k8scloude1 volume]# systemctl status kubelet
● kubelet.service - kubelet: The Kubernetes Node Agent
Loaded: loaded (/usr/lib/systemd/system/kubelet.service; enabled; vendor preset: disabled)
Drop-In: /usr/lib/systemd/system/kubelet.service.d
└─10-kubeadm.conf
Active: active (running) since 三 2022-01-19 18:11:09 CST; 6s ago
Docs: https://kubernetes.io/docs/
Main PID: 89887 (kubelet)
Memory: 37.4M
CGroup: /system.slice/kubelet.service
├─89887 /usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf --config=/var/lib/kubelet/config.yaml --network-plugin=cni --pod-in...
└─90152 [find]

下载git

[root@k8scloude1 volume]# yum -y install git

下载扩展存储卷yaml文件

[root@k8scloude1 volume]# git clone https://github.com/kubernetes-incubator/external-storage.git

[root@k8scloude1 volume]# ls
external-storage [root@k8scloude1 volume]# cd external-storage/nfs-client/ [root@k8scloude1 nfs-client]# cd deploy/ [root@k8scloude1 deploy]# ls
class.yaml deployment-arm.yaml deployment.yaml objects rbac.yaml test-claim.yaml test-pod.yaml

因为deployment.yaml文件里需要NFS制备器镜像:nfs-client-provisioner:latest镜像,可以提前下载下来镜像

[root@k8scloude1 deploy]# cat deployment.yaml | grep image
image: quay.io/external_storage/nfs-client-provisioner:latest #在master和worker上都下载好镜像
[root@k8scloude1 deploy]# docker pull quay.io/external_storage/nfs-client-provisioner:latest [root@k8scloude2 ~]# docker pull quay.io/external_storage/nfs-client-provisioner:latest [root@k8scloude3 ~]# docker pull quay.io/external_storage/nfs-client-provisioner:latest [root@k8scloude1 deploy]# docker images | grep nfs-client-provisioner
quay.io/external_storage/nfs-client-provisioner latest 16d2f904b0d8 3 years ago 45.5MB

安装NFS制备器

[root@k8scloude1 deploy]# pwd
/root/volume/external-storage/nfs-client/deploy #当前的namespace为:volume
[root@k8scloude1 deploy]# kubens
default
kube-node-lease
kube-public
kube-system
ns1
ns2
pod
volume [root@k8scloude1 deploy]# ls
class.yaml deployment-arm.yaml deployment.yaml objects rbac.yaml test-claim.yaml test-pod.yaml #修改namespace
[root@k8scloude1 deploy]# sed -i 's/namespace: default/namespace: volume/g' rbac.yaml [root@k8scloude1 deploy]# kubectl apply -f rbac.yaml
serviceaccount/nfs-client-provisioner created
clusterrole.rbac.authorization.k8s.io/nfs-client-provisioner-runner created
clusterrolebinding.rbac.authorization.k8s.io/run-nfs-client-provisioner created
role.rbac.authorization.k8s.io/leader-locking-nfs-client-provisioner created
rolebinding.rbac.authorization.k8s.io/leader-locking-nfs-client-provisioner created [root@k8scloude1 deploy]# grep image deployment.yaml
image: quay.io/external_storage/nfs-client-provisioner:latest [root@k8scloude1 deploy]# vim deployment.yaml #修改NFS的服务器地址和共享目录,设置镜像下载策略为: imagePullPolicy: IfNotPresent:本地有镜像就不下载
[root@k8scloude1 deploy]# cat deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-client-provisioner
labels:
app: nfs-client-provisioner
# replace with namespace where provisioner is deployed,命名空间要修改
namespace: volume
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: nfs-client-provisioner
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner
containers:
- name: nfs-client-provisioner
image: quay.io/external_storage/nfs-client-provisioner:latest
#镜像下载策略要修改
imagePullPolicy: IfNotPresent
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: fuseim.pri/ifs
#NFS服务器地址
- name: NFS_SERVER
value: 192.168.110.133
#NFS共享目录
- name: NFS_PATH
value: /dongtaijuandongying
volumes:
- name: nfs-client-root
nfs:
#NFS服务器地址
server: 192.168.110.133
#NFS共享目录
path: /dongtaijuandongying [root@k8scloude1 deploy]# kubectl apply -f deployment.yaml
deployment.apps/nfs-client-provisioner created #可以看到nfs制备器nfs-client-provisioner-76c576954d-5x7t2
[root@k8scloude1 deploy]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nfs-client-provisioner-76c576954d-5x7t2 1/1 Running 0 17s 10.244.112.129 k8scloude2 <none> <none>

5.4 创建StorageClass

查看StorageClass

[root@k8scloude1 deploy]# kubectl get sc
No resources found

配置StorageClass

[root@k8scloude1 deploy]# vim class.yaml 

#archiveOnDelete参数表示:If it exists and has a false value, delete the directory. if onDelete exists, archiveOnDelete will be ignored.
[root@k8scloude1 deploy]# cat class.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: managed-nfs-storage
provisioner: fuseim.pri/ifs # or choose another name, must match deployment's env PROVISIONER_NAME'
parameters:
archiveOnDelete: "false"

创建storageclass

[root@k8scloude1 deploy]# kubectl apply -f class.yaml
storageclass.storage.k8s.io/managed-nfs-storage created [root@k8scloude1 deploy]# kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
managed-nfs-storage fuseim.pri/ifs Delete Immediate false 10s

5.5 创建持久卷申领PersistentVolumeClaim(PVC)

现在没有pv和PVC

[root@k8scloude1 deploy]# kubectl get pv
No resources found [root@k8scloude1 deploy]# kubectl get pvc
No resources found in volume namespace.

配置PVC

[root@k8scloude1 deploy]# vim pvc1.yaml 

#storageClassName要和刚才创建的storageClass一样
[root@k8scloude1 deploy]# cat pvc1.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mypvc
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 1Gi
storageClassName: managed-nfs-storage

创建PVC

[root@k8scloude1 deploy]# kubectl apply -f pvc1.yaml
persistentvolumeclaim/mypvc created [root@k8scloude1 deploy]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mypvc Bound pvc-4b73eeaa-1530-4599-b2c8-6057bb16658a 1Gi RWO managed-nfs-storage 6s

创建PVC之后,pv也自动创建了

[root@k8scloude1 deploy]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-4b73eeaa-1530-4599-b2c8-6057bb16658a 1Gi RWO Delete Bound volume/mypvc managed-nfs-storage 9s

查看pv的详细信息

[root@k8scloude1 deploy]# kubectl describe pv pvc-4b73eeaa-1530-4599-b2c8-6057bb16658a
Name: pvc-4b73eeaa-1530-4599-b2c8-6057bb16658a
Labels: <none>
Annotations: pv.kubernetes.io/provisioned-by: fuseim.pri/ifs
Finalizers: [kubernetes.io/pv-protection]
StorageClass: managed-nfs-storage
Status: Bound
Claim: volume/mypvc
Reclaim Policy: Delete
Access Modes: RWO
VolumeMode: Filesystem
Capacity: 1Gi
Node Affinity: <none>
Message:
Source:
Type: NFS (an NFS mount that lasts the lifetime of a pod)
Server: 192.168.110.133
Path: /dongtaijuandongying/volume-mypvc-pvc-4b73eeaa-1530-4599-b2c8-6057bb16658a
ReadOnly: false
Events: <none>

删除pvc,PV也自动删除

[root@k8scloude1 deploy]# kubectl delete pvc mypvc
persistentvolumeclaim "mypvc" deleted [root@k8scloude1 deploy]# kubectl get pv
No resources found [root@k8scloude1 deploy]# kubectl get pvc
No resources found in volume namespace.

重新创建PVC

[root@k8scloude1 deploy]# kubectl apply -f pvc1.yaml
persistentvolumeclaim/mypvc created [root@k8scloude1 deploy]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mypvc Bound pvc-7d17891d-f95a-417f-abf4-06f9e84dc82e 1Gi RWO managed-nfs-storage 6s [root@k8scloude1 deploy]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-7d17891d-f95a-417f-abf4-06f9e84dc82e 1Gi RWO Delete Bound volume/mypvc managed-nfs-storage 9s

六.把卷挂载到pod

配置pod把PVC挂载到容器的/xx目录

[root@k8scloude1 deploy]# vim pvcpod.yaml 

[root@k8scloude1 deploy]# cat pvcpod.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: pvcshare
name: pvcshare
spec:
#nodeName指定pod运行在k8scloude3节点
nodeName: k8scloude3
terminationGracePeriodSeconds: 0
volumes:
- name: v1
#卷类型为PVC
persistentVolumeClaim:
claimName: mypvc
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: h1
resources: {}
volumeMounts:
- name: v1
mountPath: /xx
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}

创建pod

[root@k8scloude1 deploy]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nfs-client-provisioner-76c576954d-5x7t2 1/1 Running 0 15m [root@k8scloude1 deploy]# kubectl apply -f pvcpod.yaml
pod/pvcshare created [root@k8scloude1 deploy]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nfs-client-provisioner-76c576954d-5x7t2 1/1 Running 0 15m 10.244.112.129 k8scloude2 <none> <none>
pvcshare 1/1 Running 0 5s 10.244.251.195 k8scloude3 <none> <none>

进入pod,创建文件

[root@k8scloude1 deploy]# kubectl exec -it pvcshare -- bash
root@pvcshare:/# ls /xx/ root@pvcshare:/# touch /xx/{1..10}.txt root@pvcshare:/# ls /xx/
1.txt 10.txt 2.txt 3.txt 4.txt 5.txt 6.txt 7.txt 8.txt 9.txt root@pvcshare:/# exit
exit

NFS服务器对应目录下也有文件

[root@etcd1 ~]# ls /dongtaijuandongying/volume-mypvc-pvc-7d17891d-f95a-417f-abf4-06f9e84dc82e/
10.txt 1.txt 2.txt 3.txt 4.txt 5.txt 6.txt 7.txt 8.txt 9.txt

删除pod

[root@k8scloude1 deploy]# kubectl get pods
NAME READY STATUS RESTARTS AGE
nfs-client-provisioner-76c576954d-5x7t2 1/1 Running 0 17m
pvcshare 1/1 Running 0 2m9s [root@k8scloude1 deploy]# kubectl delete -f pvcpod.yaml
pod "pvcshare" deleted [root@k8scloude1 deploy]# kubectl get pods
NAME READY STATUS RESTARTS AGE
nfs-client-provisioner-76c576954d-5x7t2 1/1 Running 0 18m

删除PVC

[root@k8scloude1 deploy]# kubectl delete -f pvc1.yaml
persistentvolumeclaim "mypvc" deleted [root@k8scloude1 deploy]# kubectl get pv
No resources found [root@k8scloude1 deploy]# kubectl get pvc
No resources found in volume namespace.

pvc被删除之后,NFS服务端文件也没了,是因为回收策略RECLAIM POLICY为Delete

[root@etcd1 ~]# ls /dongtaijuandongying/volume-mypvc-pvc-7d17891d-f95a-417f-abf4-06f9e84dc82e/
ls: 无法访问/dongtaijuandongying/volume-mypvc-pvc-7d17891d-f95a-417f-abf4-06f9e84dc82e/: 没有那个文件或目录

当配置了存储类StorageClass,PVC可以进行动态扩容,关于PVC动态扩容请查看博客《troubleshoot:PVC动态扩容报错》。

最新文章

  1. php 时间戳与日期的转换(转载)
  2. 如何将EXCEL表导入ORACLE数据库中?【转】
  3. Find linux下
  4. [Node.js] CommonJS Modules
  5. css三角形绘制
  6. linux杂谈(十七):iscsi存储分离技术
  7. PHP新手之学习类与对象(2)
  8. JS与浏览器的几个兼容性问题
  9. celery定时任务
  10. maven install 报错 No compiler is provided in this environment. Perhaps you are running on a JRE rather than a JDK?
  11. 最全的前端Git基础命令,看完保证你会!
  12. 从urllib2的内存泄露看python的GC python引用计数 对象的引用数 循环引用
  13. mybatis 插入返回自增后的id
  14. Python学习--Selenium模块简单介绍(1)
  15. 【bzoj1187】 HNOI2007—神奇游乐园
  16. splice() 的用法
  17. Boost asio基本概念
  18. 【RL系列】马尔可夫决策过程——Jack‘s Car Rental
  19. Python学习札记(三) I/O
  20. tcp 建立连接的三次握手,以及关闭连接的4次挥手

热门文章

  1. Centos7主机安装Cockpit管理其他主机
  2. do...while循环体
  3. 浅谈-动态路由之OSPF的理解
  4. 9. RabbitMQ系列之消息发布确认
  5. js排序的基础原理理解
  6. LeetCode------两数之和(3)【数组】
  7. HTML5和CSS3新特性
  8. ES6 学习笔记(十二)代理器Proxy的简单使用
  9. 线上Electron应用具备哪些特征?
  10. Django的简单使用