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)
、CephFS
Cinder(OpenStack block storage)
、Glusterfs
、VsphereVolume
、Quobyte Volumes
HostPath
、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--ReadWriteOnce
ROX--ReadOnlyMany
RWX--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