1. 概念:
1.1. PersistentVolume (PV):
- 是由管理员设置的存储,它是群集的一部分。就像节点是集群中的资源一样,PV也是集群中的资源。PV是Volume之类的卷插件,但具有独立于使用PV的Pod的生命周期。此API对象包含存储实现的细节,即
NFS、iSCSI或特定于云供应商的存储系统。
1.2. PersistentVolumeclaim (PVC)
- 是用户存储的请求。它与Pod相似。Pod消耗节点资源,PVC消耗PV资源。Pod可以请求特定级别的资源(CPU和内存)。PVC可以请求特定的大小和访问模式(例如,可以以读/写一次或只读多次模式挂载)
1.3. PV和PVC如何绑定
master中的控制环路监视新的PVC,寻找匹配的PV(如果可能),并将它们绑定在一起。如果为新的PVC动态调配PV,则该环路将始终将该PV绑定到PVC。否则,用户总会得到他们所请求的存储,但是容量可能超出要求的数量。一旦PV和PVC绑定后,PersistentVolumeClaim绑定是排他性的,不管它们是如何绑定的。PVC跟PV绑定是一对一的映射。
1.4. 持久化卷声明的保护
- PVC保护的目的是确保由pod正在使用的PVC不会从系统中移除,因为如果被移除的话可能会导致数据丢失。
- 注意:当pod状态为
Pending并且pod已经分配给节点或pod为Running状态时,PVC处于活动状态。 - 当启用PVC保护
alpha功能时,如果用户删除了一个pod正在使用的PVC,则该PVC不会被立即删除。PVC的删除将被推迟,直到PVC不再被任何pod使用。
1.5. 持久化卷类型:
-
PersistentVolume类型以插件形式实现。Kubernetes目前支持以下插件类型:GCEPersistentDisk、AWSElasticBlockStore、AzureFile、AzureDisk、FC(Fibre Channel)FlexVolume、Flocker、NFS、iSCSI、RBD(Ceph Block Device)、CephFSCinder(OpenStack block storage)、Glusterfs、VsphereVolume、Quobyte VolumesHostPath、VMware Photon、Portworx Volumes、Scalelo Volumes、Storageos
1.6. PV的访问模式:
-
PersistentVolume可以以资源提供者支持的任何方式挂载到主机上。如下表所示,供应商具有不同的功能,每个PV的访问模式都将被设置为该卷支持的特定模式。例如,NFS可以支持多个读/写客户端,但特定的NFS PV可能以只读方式导出到服务器上。每个PV都有一套自己的用来描述特定功能的访问模式:ReadWriteOnce一一允许一个Pod以读写模式访问PV。这是最常见的访问策略,适用于大多数应用程序ReadOnlyMany一一允许多个Pod以只读模式访问同一个PV。这也是通过NFS实现的,但Pod只能读取共享目录,不能写入ReadWriteMany一一允许多个Pod同时以读写模式访问同一个PV。这通常是通过网络文件系统(NFS)实现的,允许多个Pod在同一个共享目录上进行读写操作
-
在命令行中,访问模式缩写为:
RWO--ReadWriteOnceROX--ReadOnlyManyRWX--ReadWriteMany
- 一个卷一次只能使用一种访问模式挂载,即使它支持很多访问模式。例如,
GCEPersistentDisk可以由单个节点作为ReadWriteonce模式挂载,或由多个节点以ReadonlyMany模式挂载,但不能同时挂载
| Volume插件 | ReadWriteOnce | ReadOnlyMany | ReadWriteMany |
|---|---|---|---|
| AWSElasticBlockStore | √ | - | - |
| AzureFile | √ | √ | √ |
| AzureDisk | √ | - | - |
| CephFS | √ | √ | √ |
| Cinder | √ | - | - |
| FC | √ | √ | - |
| FlexVolume | √ | √ | - |
| Flocker | √ | - | - |
| GCEPersistentDisk | √ | √ | - |
| Glusterfs | √ | √ | √ |
| HostPath | VsphereVolume | ||
| iSCSI | √ | √ | - |
| PhotonPersistentDisk | √ | - | - |
| Quobyte | √ | √ | √ |
| NFS | √ | √ | √ |
| RBD | √ | √ | - |
| VsphereVolume | √ | - | -(当pod并列时有效) |
| Portworx Volumes | √ | - | √ |
| ScalelO | √ | √ | - |
| StorageOS | √ | - | - |
1.7. PV的回收策略:
- Retain(保留)一一手动回收
- Recycle(回收)一一基本擦除(rm-rf/thevolume/*)
- Delete(删除)一一关联的存储资产(例如
AWS EBS、GCE PD、Azure Disk和OpenStack Cinder卷)将被刷除 - 当前,只有NFS和HostPath支持回收策路。
AWS EBS、GCE PD、Azure Disk和OpenStack Cinder卷支持删除策略
1.8. PV的状态
-
PV卷可以处于以下的某种状态:
Available(可用):PV 可以被 PersistentVolumeClaim(PVC)请求绑定Bound(已绑定):PV 已经与 PVC 绑定,可以供 Pod 使用Released(已释放):PVC 与 PV 的绑定关系已经解除,但是 PV 的底层存储资源尚未被清理。Failed(失败):PV 创建或绑定过程中出现错误,无法使用。
-
命令行会显示绑定到PV的PVC的名称
2. K8S+NFS静态存储模式
- 静态存储:手动方式静态创建所需要的PV和PVC
2.1. 实例一:
2.1.1. 安装NFS服务
[root@localhost ~]# yum -y install nfs-common nfs-utils.x86_64 rpcbind.x86_64
//创建共享目录
[root@localhost ~]# mkdir /nfsdata
//修改权限
[root@localhost ~]# chmod 777 /nfsdata
[root@localhost ~]# chown nfsnobody /nfsdata/
//编辑/etc/exports文件
[root@localhost ~]# vim /etc/exports
/nfsdata *(rw,sync,no_root_squash,no_all_squash,sync)
//启动服务
[root@localhost ~]# systemctl start rpcbind.service && systemctl start nfs
//在挂载目录创建一个html文件
[root@localhost ~]# cd /nfsdata/
[root@localhost nfsdata]# echo "Hello World" >> index.html
//所有的K8S节点安装nfs客户端
yum -y install nfs-utils.x86_64 rpcbind.x86_64
//搜索网络中可用的共享文件
[root@master1 yaml]# showmount -e 192.168.2.50
Export list for 192.168.2.50:
/nfsdata *
2.1.2. 部署PV
vim nfs-pv.yaml
apiVersion: v1 #指定 Kubernetes API 的版本
kind: PersistentVolume #指定资源类型,这里是 PersistentVolume,表示持久卷
metadata: #元数据部分,包含持久卷的名称等信息
name: nfs-pv1 #指定pv的名称为nfs-pv1
spec: #指定持久卷的详细规格
capacity: #指定持久卷的容量,这里是 10Gi
storage: 10Gi
accessModes: #指定持久卷的访问模式,这里是 ReadWriteMany,表示多个 Pod 可以同时读写该持久卷
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain #指定持久卷回收策略,这里是 Retain,表示在持久卷被释放后保留数据
storageClassName: nfs #指定持久卷的存储类名称,这里是 nfs
nfs: #指定 NFS 卷的相关配置
path: /nfsdata #指定 NFS 服务器上共享目录的路径,这里是 /nfsdata
server: 192.168.2.50 #指定 NFS 服务器的 IP 地址或主机名,这里是 192.168.2.50
readOnly: false #指定是否以只读模式挂载 NFS 卷,这里是 false,表示可读可写
//创建PV
[root@master1 yaml]# kubectl apply -f nfs-pv.yaml
persistentvolume/nfs-pv1 created
[root@master1 yaml]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
nfs-pv1 1Gi RWX Retain Available nfs 5s
2.1.3. 创建PVC
vim pvc.yaml
apiVersion: v1 #指定使用的Kubernetes API版本
kind: PersistentVolumeClaim #指定创建的资源类型为PVC
metadata: #用于指定PVC的元数据,包括名称等
name: nfs-pvc1 #指定PVC的名称为nfs-pvc1
spec: #指定PVC的规格
accessModes: #指定PVC的访问模式。在这个示例中,设置为ReadWriteMany,表示可以同时被多个Pod以读写方式访问
- ReadWriteMany
resources: #用于指定PVC所需的资源
requests: #指定需要的存储容量
storage: 1Gi #设置PVC需要的存储容量为1GB
storageClassName: nfs #指定使用的存储类名称为nfs。存储类定义了存储的类型和配置
//创建pvc
[root@master1 pv]# kubectl apply -f pvc.yaml
persistentvolumeclaim/nfs-pvc1 created
//查看pvc的信息
[root@master1 pv]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
nfs-pvc1 Bound nfs-pv1 10Gi RWX nfs 3s
//查看pv的信息
[root@master1 pv]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
nfs-pv1 10Gi RWX Retain Bound default/nfs-pvc1 nfs 2m24s
---可以看的出来pv和pvc已经绑定成功。
2.1.4. 使用deployment创建pod关联pvc
vim nginx.deployment.yaml
apiVersion: apps/v1 #指定Deployment的API版本
kind: Deployment #指定对象类型为Deployment
metadata: #指定Deployment的元数据,包括名称等。
name: nfs-nginx #指定Deployment的名称
spec: #指定 deployment 对象的配置信息
replicas: 4 #指定需要运行的副本数,这里是 4
selector: #指定选择器,用于选择需要管理的 Pod
matchLabels: #匹配标签,这里是 app: my-nginx,表示选择具有 app=my-nginx 标签的Pod
app: my-nginx
template: #指定 Pod 的模板,用于创建和管理 Pod
metadata: #指定Pod 元数据信息,包括标签等信息
labels: #Pod 标签,这里是 app: my-nginx
app: my-nginx
spec: #指定Pod 配置信息
containers: #容器列表,这里只有一个 Nginx 容器
- name: nfs-nginx #指定容器名称
image: nginx #指定容器使用的镜像
imagePullPolicy: IfNotPresent #镜像拉取策略:本地不存在镜像时才尝试拉取新镜像
volumeMounts: #这是一个包含卷挂载配置的列表
- name: nginx-html #这是卷的名称需要和volume.name的内容一样
mountPath: /usr/share/nginx/html/ #指定卷在容器内的挂载路径
volumes: #卷配置的列表
- name: nginx-html #这是卷的名称,必须与 volumeMounts.name 中的内容相匹配
persistentVolumeClaim: #指定使用的持久卷声明
claimName: nfs-pvc1 #指定pvc的名称
---
apiVersion: v1 #定义了Kubernetes API的版本,这里使用的是v1版本
kind: Service #指定要创建的对象类型,这里是Service
metadata: #指定Service的元数据,包括名称等
name: nginx-service #指定Service的名称
spec: #指定 service对象的配置信息
type: NodePort #指定Service类型为NodePort
selector: #选择器,用于选择要关联的Pod
app: my-nginx #匹配具有指定标签的Pod
ports: #Service暴露的端口列表
- name: http #端口名称
protocol: TCP #端口协议
port: 80 #Service的端口号
targetPort: 80 #将流量转发到Pod的端口号
nodePort: 30080 #节点端口,用于访问Service的端口
//创建
[root@master1 pv]# kubectl apply -f nginx.deployment.yaml
deployment.apps/nfs-nginx created
service/nginx-service created
//查看pod状态
[root@master1 pv]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nfs-nginx-78d5d6b9f5-m4zn2 1/1 Running 0 3s
nfs-nginx-78d5d6b9f5-m5kdb 1/1 Running 0 3s
nfs-nginx-78d5d6b9f5-wqjd4 1/1 Running 0 3s
nfs-nginx-78d5d6b9f5-z4nv4 1/1 Running 0 3s
//查看svc信息
[root@master1 pv]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.10.0.1 <none> 443/TCP 6d4h
nginx-service NodePort 10.10.76.216 <none> 80:30080/TCP 11s
//验证pod是否和pvc关联成功
[root@master1 pv]# curl 192.168.2.10:30080
Hello World ---这个内容是在nfs服务器上创建的。
2.2. 实例二:
- 清理实例一的操作,删除pvc、pv等。
2.2.1. 部署nfs服务:
[root@localhost ~]# yum -y install nfs-common nfs-utils.x86_64 rpcbind.x86_64
//创建共享目录
[root@localhost ~]# mkdir -p /data/nfs{1..4}
[root@localhost ~]# ls /data/
nfs1 nfs2 nfs3 nfs4
//修改权限
[root@localhost ~]# chmod 777 /data/nfs1/ /data/nfs2/ /data/nfs3/ /data/nfs4/
[root@localhost ~]# chown nfsnobody /data/nfs1/ /data/nfs2/ /data/nfs3/ /data/nfs4/
[root@localhost ~]# ll /data/
total 0
drwxrwxrwx 2 nfsnobody root 6 Jun 23 21:33 nfs1
drwxrwxrwx 2 nfsnobody root 6 Jun 23 21:33 nfs2
drwxrwxrwx 2 nfsnobody root 6 Jun 23 21:33 nfs3
drwxrwxrwx 2 nfsnobody root 6 Jun 23 21:33 nfs4
//编辑/etc/exports文件
[root@localhost ~]# vim /etc/exports
/data/nfs1 *(rw,sync,no_root_squash,no_all_squash,sync)
/data/nfs2 *(rw,sync,no_root_squash,no_all_squash,sync)
/data/nfs3 *(rw,sync,no_root_squash,no_all_squash,sync)
/data/nfs4 *(rw,sync,no_root_squash,no_all_squash,sync)
//启动服务
[root@localhost ~]# systemctl restart rpcbind.service && systemctl restart nfs
//在挂载目录创建html文件
[root@localhost ~]# echo "aaaa" > /data/nfs1/index.html
[root@localhost ~]# echo "bbbb" > /data/nfs2/index.html
[root@localhost ~]# echo "cccc" > /data/nfs3/index.html
[root@localhost ~]# echo "dddd" > /data/nfs4/index.html
//所有的K8S节点安装nfs客户端
yum -y install nfs-utils.x86_64 rpcbind.x86_64
//搜索网络中可用的共享文件
[root@master1 ~]# showmount -e 192.168.2.50
Export list for 192.168.2.50:
/data/nfs4 *
/data/nfs3 *
/data/nfs2 *
/data/nfs1 *
2.2.2. 部署PV
nfs-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv1
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs
nfs:
path: /data/nfs1
server: 192.168.2.50
readOnly: false
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv2
spec:
capacity:
storage: 2Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs
nfs:
path: /data/nfs2
server: 192.168.2.50
readOnly: false
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv3
spec:
capacity:
storage: 2Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs
nfs:
path: /data/nfs3
server: 192.168.2.50
readOnly: false
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv4
spec:
capacity:
storage: 2Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs
nfs:
path: /data/nfs4
server: 192.168.2.50
readOnly: false
//创建pv
[root@master1 yaml]# kubectl apply -f nfs-pv.yaml
persistentvolume/nfs-pv1 created
persistentvolume/nfs-pv2 created
persistentvolume/nfs-pv3 created
persistentvolume/nfs-pv4 created
//查看pv状态
[root@master1 yaml]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
nfs-pv1 5Gi RWX Retain Available nfs 6s
nfs-pv2 2Gi RWX Retain Available nfs 6s
nfs-pv3 2Gi RWX Retain Available nfs 6s
nfs-pv4 2Gi RWX Retain Available nfs 6s
2.2.3. 创建服务并使用PVC
vim nginx-pvc-service.yaml
apiVersion: v1 #定义使用的Kubernetes API版本
kind: Service #定义资源类型为Service
metadata: #定义Service的元数据,包括名称
name: nginx-service #定义service的名称为nginx-service
spec: #指定 service对象的配置信息
selector: #指定Pod的标签,用于将Service与Pod关联起来
app: my-nginx #匹配具有指定标签的Pod
ports: #Service暴露的端口列表
- port: 80 #Service的端口号
name: web #端口名称
clusterIP: None #表示这是一个Headless Service,没有集群内部的虚拟IP(ClusterIP),只有DNS记录
---
apiVersion: apps/v1 #定义使用的Kubernetes API版本
kind: StatefulSet #定义资源类型为StatefulSet
metadata: #定义StatefulSet的元数据,包括名称
name: my-nginx #定义StatefulSet名称为my-nginx
spec: #指定StatefulSet对象的配置信息
selector: #指定选择器,用于选择需要管理的 Pod
matchLabels: #匹配标签,这里是 app: my-nginx,表示选择具有 app=my-nginx 标签的Pod
app: my-nginx
serviceName: nginx-service #指定关联的Service的名称
replicas: 4 #定义StatefulSet中Pod的副本数量为4
template: #定义StatefulSet中Pod的模板,用于创建Pod
metadata: #定义Pod的元数据,包括标签
labels: #Pod 标签,这里是 app: my-nginx
app: my-nginx
spec: #定义Pod的规格
containers: #定义Pod中的容器,包括容器的名称、镜像、挂载的卷等
- name: my-nginx #定义容器名称
image: nginx:latest #定义容器使用的镜像
imagePullPolicy: IfNotPresent #定义镜像拉取策略,IfNotPresent表示如果镜像已经存在就不再拉取
volumeMounts: #定义容器的挂载点
- name: nginx-html #挂载点的名称
mountPath: /usr/share/nginx/html #在容器内的挂载点路径
volumeClaimTemplates: #定义持久卷申请模板
- metadata: #持久卷申请模板的元数据,包括名称
name: nginx-html #持久卷申请模板的名称,与容器的挂载点名称匹配
spec: #持久卷申请模板的规范,包括访问模式、存储类和资源请求。
accessModes: #定义持久化卷的访问模式,ReadWriteMany表示可以被多个Pod同时读写,这个策略必须和PV的访问策略一样。
- ReadWriteMany
resources: #定义持久化卷的资源请求,这里请求1Gi的存储空间,这里的请求空间不能比pv的空间大,可以相等。
requests:
storage: 1Gi
storageClassName: nfs #定义持久化卷的存储类名称,这里使用nfs存储类,这个需要和pv定义的存储类名称一样。
//创建:
[root@master1 yaml]# kubectl apply -f nginx-pvc-service.yaml
//查看pod创建情况
[root@master1 yaml]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
my-nginx-0 1/1 Running 0 13m 10.244.1.54 node1 <none> <none>
my-nginx-1 1/1 Running 0 13m 10.244.1.55 node1 <none> <none>
my-nginx-2 1/1 Running 0 13m 10.244.3.15 master3 <none> <none>
my-nginx-3 1/1 Running 0 13m 10.244.2.19 master2 <none> <none>
//查看pvc创建情况:
[root@master1 yaml]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
nginx-html-my-nginx-0 Bound nfs-pv2 2Gi RWX nfs 25m
nginx-html-my-nginx-1 Bound nfs-pv3 2Gi RWX nfs 25m
nginx-html-my-nginx-2 Bound nfs-pv4 2Gi RWX nfs 25m
nginx-html-my-nginx-3 Bound nfs-pv1 5Gi RWX nfs 25m
//验证:
[root@master1 yaml]# curl 10.244.1.54
bbbb
[root@master1 yaml]# curl 10.244.1.55
cccc
[root@master1 yaml]# curl 10.244.3.15
dddd
[root@master1 yaml]# curl 10.244.2.19
aaaa
2.2.4. 关于StatefulSet
- 匹配
Pod name(网络标识)的模式为:(StatefulSet名你)-(序号),比如上面的示例:my-nginx-0、my-nginx-1、my-nginx-2、my-nginx-3
[root@master1 yaml]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
my-nginx-0 1/1 Running 0 13m 10.244.1.54 node1 <none> <none>
my-nginx-1 1/1 Running 0 13m 10.244.1.55 node1 <none> <none>
my-nginx-2 1/1 Running 0 13m 10.244.3.15 master3 <none> <none>
my-nginx-3 1/1 Running 0 13m 10.244.2.19 master2 <none> <none>
-
StatefulSet为每个Pod副本创建了一个DNS域名,这个域名的格式为:
(podname).(headless server name),也就意味着服务间是通过Pod域名来通信而非Pod IP,因为当Pod所在Node发生故障时,Pod会飘逸到其它Nods上,Pod IP会发生变化,但是Pod域名不会有变化。 -
StatefulSet便用Headless服务来控制Pod的域名,这个域名的FQDN为:
(servicename).(namespace).svc.cluster.local,其中,"cluster.local"指的是集群的域名。
[root@master1 pvc]# dig -t A nginx-service.default.svc.cluster.local. @10.244.0.14
; <<>> DiG 9.11.4-P2-RedHat-9.11.4-26.P2.el7_9.13 <<>> -t A nginx-service.default.svc.cluster.local. @10.244.0.14
;; global options: +cmd
;; Got answer:
;; WARNING: .local is reserved for Multicast DNS
;; You are currently testing what happens when an mDNS query is leaked to DNS
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 56295
;; flags: qr aa rd; QUERY: 1, ANSWER: 4, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;nginx-service.default.svc.cluster.local. IN A
;; ANSWER SECTION:
nginx-service.default.svc.cluster.local. 30 IN A 10.244.1.60
nginx-service.default.svc.cluster.local. 30 IN A 10.244.2.21
nginx-service.default.svc.cluster.local. 30 IN A 10.244.1.61
nginx-service.default.svc.cluster.local. 30 IN A 10.244.0.17
;; Query time: 35 msec
;; SERVER: 10.244.0.14#53(10.244.0.14)
;; WHEN: Sat Jun 24 03:15:44 CST 2023
;; MSG SIZE rcvd: 288
- 跟据
volumeClaimTemplates,为每个Pod创建一个pvc,pvc的命名规则匹配模式:(volumeClaimTemplates.name)-(pod_name),比如上面的volumeMounts.name=nginx-html, Pod name=my-nginx-0-3,因此创建出来的PVC是nginx-html-my-nginx-0、nginx-html-my-nginx-1、nginx-html-my-nginx-2、nginx-html-my-nginx-3
[root@master1 pvc]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
nginx-html-my-nginx-0 Bound pvc-a27993f6-757b-4e8c-afe7-0ae4113b344c 1Gi RWX managed-nfs-storage 31m
nginx-html-my-nginx-1 Bound pvc-bf387cc5-accd-4baf-8e6d-fc405f62f563 1Gi RWX managed-nfs-storage 31m
nginx-html-my-nginx-2 Bound pvc-0fce0ac6-51b9-4ab7-940a-df853b39d4cd 1Gi RWX managed-nfs-storage 31m
nginx-html-my-nginx-3 Bound pvc-e6312dfe-8803-4937-a10f-22cc37d998c1 1Gi RWX managed-nfs-storage 31m
-
删除Pod不会脚除其PVC,手动删除pvc将自动释放PV
-
Statefulset的启停顺序:
- 有序部署:部署StatefulSet时,如果有多个Pod副本,它们会被顺序地创建从(0到N-1)并且在下一个Pod运行之前所有之前的Pod须都是Runningl和Ready状态。
- 有序删除:当Pod被删除时,它们被终止的顺序是从(N-1到0)。
- 有序扩展:当对Pod执行扩展操作时,与部署一样,它前面的Pod必须都处于Running和Ready状态。
3. K8S+NFS动态存储模式
-
动态存储: 通过创建PVC动态地创建对应PV,无需手动创建PV。
-
动态创建持久卷(PV)的原理是通过使用动态存储类(StorageClass)和持久卷声明(PVC)来实现。
- 动态存储类(StorageClass):动态存储类是定义存储插件和参数的对象。它指定了用于创建持久卷的存储插件、存储参数和其他选项。创建动态存储类时,Kubernetes将根据所定义的存储插件和参数创建持久卷。
- 持久卷声明(PVC):持久卷声明是对所需存储资源的请求。它指定了所需的存储类、访问模式和存储大小。当创建一个持久卷声明后,Kubernetes将根据所指定的存储类和其他参数自动创建一个符合要求的持久卷。
-
当创建一个持久卷声明时,Kubernetes将根据存储类的定义,与存储插件交互并动态创建一个持久卷。这样,无需手动创建和管理每个PV,而是根据需求自动创建符合要求的PV。这种动态创建的方式可以简化存储管理,并提高资源的利用率和灵活性。

-
使用StorageClass有什么好处呢?除了由存储系统动态创建,节省了管理员的时间,还有一个好处是可以封装不同类型的存储供PVC选用。
-
在StorageClass出现以前,PVC绑定一个PV只能根据两个条件,一个是存储的大小,另一个是访问模式。在StorageClass出现后,等于增加了一个绑定维度。如下为动态PV存储操作方式:
3.1. 安装NFS服务:
[root@localhost ~]# yum -y install nfs-common nfs-utils.x86_64 rpcbind.x86_64
//创建共享目录
[root@localhost ~]# mkdir -p /data/nfs
//修改权限
[root@localhost ~]# chmod 777 /data/nfs
[root@localhost ~]# chown nfsnobody /data/nfs
//修改exports
[root@localhost ~]# vim /etc/exports
/data/nfs *(rw,sync,no_root_squash,no_all_squash,sync)
//启动服务
[root@localhost ~]# systemctl start rpcbind.service && systemctl start nfs
//所有的K8S节点安装nfs客户端
yum -y install nfs-utils.x86_64 rpcbind.x86_64
//搜索网络中可用的共享文件
[root@master1 yaml]# showmount -e 192.168.2.50
Export list for 192.168.2.50:
/data/nfs *
3.2. 创建命名空间和动态存储类(StorageClass)
vim class.yaml
apiVersion: v1 #指定了所使用的Kubernetes API版本
kind: Namespace #指定了对象的类型为Namespace
metadata: #该部分包含了Namespace的元数据,如名称(name)等
name: nfs #指定了Namespace的名称为nfs
---
apiVersion: storage.k8s.io/v1 #指定了所使用的Kubernetes API版本
kind: StorageClass #指定了对象的类型为StorageClass
metadata: #该部分包含了存储类的元数据,如名称(name)等
name: managed-nfs-storage #指定存储类的名称,用于标识和引用该存储类
namespace: nfs #指定存储类所属的命名空间,这里是 nfs
annotations: #存储类的注解,用于添加附加信息
storageclass.beta.kubernetes.io/is-default-class: 'true' #表示这个存储类是否是默认的存储类,这里设置为 true
storageclass.kubernetes.io/is-default-class: 'true' #同上,这里同样设置为 true
provisioner: fuseim.pri/ifs #指定了用于创建持久卷的存储插件。在这个示例中,使用的是fuseim.pri/ifs插件
reclaimPolicy: Retain #指定持久卷的回收策略,这里设置为 Retain,表示在删除持久卷后保留数据
volumeBindingMode: Immediate #定持久卷绑定模式,这里设置为 Immediate,表示立即绑定持久卷
parameters: #这是存储插件的参数部分,用于配置插件的行为
archiveOnDelete: "true" #指定了当持久卷被删除时是否进行归档。"true"表示删除时进行归档
//创建动态存储类
[root@master1 pvc]# kubectl apply -f class.yaml
namespace/nfs created
storageclass.storage.k8s.io/managed-nfs-storage created
//查看命名空间是否创建成功
[root@master1 pvc]# kubectl get namespaces
NAME STATUS AGE
default Active 6d8h
kube-node-lease Active 6d8h
kube-public Active 6d8h
kube-system Active 6d8h
kubernetes-dashboard Active 6d4h
nfs Active 13s
//查看存储类是否创建成功
[root@master1 pvc]# kubectl get sc -n nfs
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
managed-nfs-storage (default) fuseim.pri/ifs Retain Immediate false 28s
3.3. 创建RBAC权限:
vim rbac.yaml
apiVersion: v1 #指定了所使用的Kubernetes API版本
kind: ServiceAccount #指定了对象的类型为ServiceAccount
metadata: #该部分包含了ServiceAccount的元数据,如名称(name)等
name: nfs-client-provisioner #定义了ServiceAccount的名称
namespace: nfs # 指定服务账户所属的命名空间
---
kind: ClusterRole #指定了对象的类型为ClusterRole
apiVersion: rbac.authorization.k8s.io/v1 #指定了所使用的Kubernetes API版本
metadata: #该部分包含了集群角色的元数据,如名称(name)等
name: nfs-client-provisioner-runner #定义集群角色的名称
rules: #定义了集群角色的权限规则
- apiGroups: [""] #资源所属的组,""表示核心API组
resources: ["persistentvolumes"] #定义了可以访问的资源为PV
verbs: ["get", "list", "watch", "create", "delete"] #定义了可以执行的操作,比如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: ["create", "update", "patch"]
---
kind: ClusterRoleBinding #指定了对象的类型ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1 #指定了所使用的Kubernetes API版本
metadata: #该部分包含了集群角色绑定的元数据,如名称(name)等
name: run-nfs-client-provisioner #定义了集群角色绑定的名称
subjects: #定义了角色绑定的主体,即与角色关联的服务账户
- kind: ServiceAccount #主体的类型,这里是ServiceAccount
name: nfs-client-provisioner #主体的名称,即之前定义的服务账户nfs-client-provisioner
namespace: nfs #主体所属的命名空间,与之前的服务账户相同
roleRef: #定义了与角色绑定关联的角色
kind: ClusterRole #角色的类型,这里是ClusterRole
name: nfs-client-provisioner-runner #角色的名称,即之前定义的集群角色nfs-client-provisioner-runner
apiGroup: rbac.authorization.k8s.io #角色所属的API组,这里是rbac.authorization.k8s.io
---
kind: Role #指定了对象的类型为Role
apiVersion: rbac.authorization.k8s.io/v1 #指定了所使用的Kubernetes API版本
metadata: #该部分包含了Role的元数据,如名称(name)等
name: leader-locking-nfs-client-provisioner #定义了Role的名称
namespace: nfs #定义Role所属的命名空间
rules: #定义了角色的权限规则,这里允许对endpoints资源进行操作
- apiGroups: [""] #资源所属的组,""表示核心API组
resources: ["endpoints"] #定义了可以访问的资源为endpoints
verbs: ["get", "list", "watch", "create", "update", "patch"] #定义了可以执行的操作,比如get、list、watch、create和patch
---
kind: RoleBinding #指定了对象的类型为RoleBinding
apiVersion: rbac.authorization.k8s.io/v1 #指定了所使用的Kubernetes API版本
metadata: #该部分包含了RoleBinding的元数据,如名称(name)等
name: leader-locking-nfs-client-provisioner #定义了RoleBinding的名称
namespace: nfs #角色绑定所属的命名空间,与之前的服务账户相同
subjects: #定义了角色绑定的主体,即与角色关联的服务账户,与之前的角色绑定相同。
- kind: ServiceAccount
name: nfs-client-provisioner
namespace: nfs
roleRef: #定义了与角色绑定关联的角色,与之前的角色相同
kind: Role
name: leader-locking-nfs-client-provisioner
apiGroup: rbac.authorization.k8s.io
//创建:
[root@master1 pvc]# 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
3.4. 部署NFS客户端供应程序
vim nfs-deployment.yaml
apiVersion: apps/v1 #这指定了使用的Kubernetes API版本。
kind: Deployment #指定了对象的类型为deployment
metadata: #指定了deployment的元数据,包含name,标签等。
name: nfs-client-provisioner #指定了deployment的名称
labels: #Deployment对象定义的标签为app: nfs-client-provisioner
app: nfs-client-provisioner
namespace: nfs #定义所属的命名空间为nfs
spec: #指定deployment对象的配置信息
replicas: 1 #指定需要运行的副本数,这里是1
strategy: # 这表示在更新Deployment时使用的策略是重新创建
type: Recreate
selector: #指定选择器,用于选择需要管理的 Pod
matchLabels: #匹配标签,这里是nfs-client-provisioner,表示选择具有nfs-client-provisioner标签的Pod
app: nfs-client-provisioner
template: #指定 Pod 的模板,用于创建和管理 Pod
metadata: #Pod 元数据信息,包括标签等信息
labels: #Pod 标签,这里是nfs-client-provisioner
app: nfs-client-provisioner
spec: #Pod 配置信息
serviceAccountName: nfs-client-provisioner #这指定了Pod使用的服务账户
containers: #容器列表
- name: nfs-client-provisioner #指定容器名称
image: quay.io/external_storage/nfs-client-provisioner:latest #指定容器使用的镜像
volumeMounts: #这是一个包含卷挂载配置的列表
- name: nfs-client-root #卷的名称,必须和volumes.name一样
mountPath: /persistentvolumes #指定卷在容器内的挂载路径
env: #这是用于设置容器环境变量的名称和值。
- name: PROVISIONER_NAME
value: fuseim.pri/ifs
- name: NFS_SERVER #这是NFS服务器的IP地址
value: 192.168.2.50
- name: NFS_PATH #这是NFS共享的路径
value: /data/nfs
volumes: #卷配置的列表
- name: nfs-client-root #卷的名称,需和volumeMounts.name内容一样
nfs: #指定NFS服务器的IP地址和共享的路径
server: 192.168.2.50
path: /data/nfs
//创建:
[root@master1 pvc]# kubectl apply -f nfs-deployment.yaml
deployment.apps/nfs-client-provisioner created
[root@master1 pvc]# kubectl get pod -n nfs
NAME READY STATUS RESTARTS AGE
nfs-client-provisioner-65dbff946-xh6tj 1/1 Running 0 12m
3.5. 创建K8S POD案例,自动获取PV资源即可
vim nfs-service-pod.yaml
apiVersion: v1 #指定使用的Kubernetes API版本
kind: Service #指定对象类型为Service
metadata: #指定service的元数据,包括名称和标签
name: nginx-service #指定service的名称
labels: #标签用于标识和选择相关的Pod
app: my-nginx
spec: #Service的规范,包括端口和选择器
ports: #定义Service暴露的端口
- port: 80 #服务将监听的端口号
name: nginx #端口的名称
selector: #选择器用于选择要与该Service关联的Pod
app: my-nginx #选择要与Service关联的Pod的标签
clusterIP: None #指定Service没有Cluster IP,这意味着该Service将不会被集群内部的其他服务访问
---
apiVersion: apps/v1 #指定使用的Kubernetes API版本
kind: StatefulSet #指定对象类型为StatefulSet
metadata: #对象的元数据,包括名称
name: nginx-nfs #指定对象的名称
spec: #StatefulSet的规范,包括选择器、副本数和Pod模板
selector: #选择器用于选择要与该StatefulSet关联的Pod
matchLabels: #选择要与StatefulSet关联的Pod的标签
app: my-nginx
serviceName: "nginx-service" #指定关联的Service的名称
replicas: 4 #指定要创建的Pod副本数
template: #定义创建Pod的模板
metadata: #Pod模板的元数据,包括标签
labels: #Pod模板的标签,与StatefulSet的选择器匹配
app: my-nginx
spec: #Pod模板的规范,包括镜像、容器和持久卷
containers: #容器列表
- name: nginx-nfs #定义容器名称
image: nginx #定义容器使用的镜像
ports: #定义容器暴露的端口
- containerPort: 80 #容器将监听的端口号
name: web #端口的名称
volumeMounts: #定义容器的挂载点
- name: nginx-html #挂载点的名称
mountPath: /usr/share/nginx/html #指定容器挂载点的路径
volumeClaimTemplates: #定义持久卷申请模板
- metadata: #持久卷申请模板的元数据,包括名称
name: nginx-html #持久卷申请模板的名称,与容器的挂载点名称匹配
spec: #持久卷申请模板的规范,包括访问模式、存储类和资源请求
accessModes: [ "ReadWriteMany" ] #指定持久卷的访问模式为多个读写
storageClassName: "managed-nfs-storage" #指定持久卷的存储类名称
resources: #指定持久卷的资源请求
requests: #指定存储资源的请求量
storage: 1Gi #指定存储资源的请求大小为1GB
//创建:
[root@master1 pvc]# kubectl apply -f nfs-service-pod.yaml
service/nginx-service created
statefulset.apps/nginx-nfs created
[root@master1 pvc]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-nfs-0 0/1 Pending 0 8m33s
- 如果POD容器一直处于Pending状态,可以查看其LOG日志信息,报错信息假如为:
Claim reference: selfLink was empty, can‘t make refere,解决方法:
#在/etc/kubernetes/manifests/kube-apiserver.yaml配置文件中加入如下代码:
vim /etc/kubernetes/manifests/kube-apiserver.yaml
...
- --feature-gates=RemoveSelfLink=false
...
//重新应用apiserver yaml文件
kubectl apply -f /etc/kubernetes/manifests/kube-apiserver.yaml
---假如是高可用集群需要每个master都修改apiserver的内容。
//查看pod创建是否成功:
[root@master1 manifests]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-nfs-0 1/1 Running 0 32m
nginx-nfs-1 1/1 Running 0 15m
nginx-nfs-2 1/1 Running 0 15m
nginx-nfs-3 1/1 Running 0 15m
//查看pvc信息:
[root@master1 manifests]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
nginx-html-nginx-nfs-0 Bound pvc-378e7c94-a53d-4ad0-af36-29fb483b9ee3 1Gi RWX managed-nfs-storage 32m
nginx-html-nginx-nfs-1 Bound pvc-6f39322d-3e7a-43a2-831b-aa07886ff132 1Gi RWX managed-nfs-storage 16m
nginx-html-nginx-nfs-2 Bound pvc-1254fd7e-be8c-407f-8735-8ce2ba35562e 1Gi RWX managed-nfs-storage 16m
nginx-html-nginx-nfs-3 Bound pvc-d5e3656f-26d3-4e5f-9696-60e6707e2e3f 1Gi RWX managed-nfs-storage 15m
//查看pv信息
[root@master1 manifests]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-1254fd7e-be8c-407f-8735-8ce2ba35562e 1Gi RWX Retain Bound default/nginx-html-nginx-nfs-2 managed-nfs-storage 16m
pvc-378e7c94-a53d-4ad0-af36-29fb483b9ee3 1Gi RWX Retain Bound default/nginx-html-nginx-nfs-0 managed-nfs-storage 16m
pvc-6f39322d-3e7a-43a2-831b-aa07886ff132 1Gi RWX Retain Bound default/nginx-html-nginx-nfs-1 managed-nfs-storage 16m
pvc-d5e3656f-26d3-4e5f-9696-60e6707e2e3f 1Gi RWX Retain Bound default/nginx-html-nginx-nfs-3 managed-nfs-storage 16m











