一、PV理解
PersistentVolume(持久化卷),是对底层的存储的一种抽象,它和具体的底层的共享存储技术的实现方式有关,比如 Ceph、GlusterFS、NFS 等,都是通过插件机制完成与共享存储的对接。如:
#PV不需要定义所属空间,集群内所有资源可用
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
nfs:
path: /data/k8s
server: 121.204.157.52
- capacity,存储能力, 目前只支持存储空间的设置, 就是我们这里的 storage=1Gi,不过未来可能会加入 IOPS、吞吐量等指标的配置。
- accessModes,访问模式, 是用来对 PV 进行访问模式的设置,用于描述用户应用对存储资源的访问权限,访问权限包括下面几种方式:
- ReadWriteOnce(RWO):读写权限,但是只能被单个节点挂载
- ReadOnlyMany(ROX):只读权限,可以被多个节点挂载
- ReadWriteMany(RWX):读写权限,可以被多个节点挂载
- persistentVolumeReclaimPolicy,pv的回收策略, 目前只有 NFS 和 HostPath 两种类型支持回收策略
- Retain(保留)- 保留数据,需要管理员手工清理数据
- Recycle(回收)- 清除 PV 中的数据,效果相当于执行 rm -rf /thevolume/*
- Delete(删除)- 与 PV 相连的后端存储完成 volume 的删除操作,当然这常见于云服务商的存储服务,比如 ASW EBS。
因为PV是直接对接底层存储的,就像集群中的Node可以为Pod提供计算资源(CPU和内存)一样,PV可以为Pod提供存储资源。因此PV不是namespaced的资源,属于集群层面可用的资源。Pod如果想使用该PV,需要通过创建PVC挂载到Pod中。
二、PVC理解
PVC全写是PersistentVolumeClaim(持久化卷声明),PVC 是用户存储的一种声明,创建完成后,可以和PV实现一对一绑定。对于真正使用存储的用户不需要关心底层的存储实现细节,只需要直接使用 PVC 即可。
#PVC需要定义所属空间,与所挂载的POD属于同一个空间
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-nfs
namespace: default
spec:
accessModes:
- ReadWriteOnce #与所需挂载的PV读写方式一致
resources:
requests:
storage: 1Gi #与所需挂载的PV大小,不可超过
三、NFS的PV与PVC实战演示(非storageclass)
1、服务器上搭建nfs服务器
#master服务器
$ yum -y install nfs-utils rpcbind
# 共享目录
$ mkdir -p /data/k8s && chmod 755 /data/k8s
#授予所有连接访问权限
$ echo '/data/k8s *(insecure,rw,sync,no_root_squash)'>>/etc/exports
#启动服务与设置开机启动
$ systemctl enable rpcbind && systemctl start rpcbind
$ systemctl enable nfs && systemctl start nfs
#分别在各slave节点上安装nfs客户端
$ yum -y install nfs-utils rpcbind
$ mkdir /nfsdata
#验证挂载
$ mount -t nfs 10.3.153.200:/data/k8s /nfsdata
2、创建PV
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
nfs:
path: /data/k8s
server: 10.3.153.200
3、创建PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-nfs
namespace: default
spec:
accessModes:
- ReadWriteOnce #与所需挂载的PV读写方式一致
resources:
requests:
storage: 1Gi #与所需挂载的PV大小,不可超过
4、创建POD
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-pvc
namespace: default #必须与所挂载的pvc相同的命名空间
spec:
replicas: 1
selector: #指定Pod的选择器
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:alpine
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
name: web
volumeMounts: #挂载容器中的目录到pvc nfs中的目录
- name: www
mountPath: /usr/share/nginx/html
volumes:
- name: www
persistentVolumeClaim: #指定pvc
claimName: pvc-nfs
此方法缺点:
- 人手根据相关PV与PVC的参数,进行相关匹配,甚至在多PV与多PVC时,没有明确的关联关系指定,出现关联错乱的现象。
- 管理困难,无法实现高效的自动化关联与人员权限控制。
四、StorageClass原理
通过storageClass + provisioner的方式来实现通过PVC自动创建并绑定PV,解决以上方案的缺点,整体过程如下:
- 管理员在K8S上安装相关存储的provisioner插件服务,用于底层存储的管理与连接。
- 管理员预先创建存储类的StorageClass,并指定对应的provisioner。
- 用户创建持久化存储卷声明PVC
- 系统根据PVC创建的信息,通知系统使用存储类storageclass创建相应的pv,并进行自动挂载
- 用户创建POD,并指定挂载的PVC name,完成整个过程操作
五、NFS的PV与PVC实战演示(StorageClass)
1、操作步骤说明
1、创建ServiceAccount帐号的RBAC权限控制,因nfs的provisioner是以pod的方式运行,同时该pod插件服务需要访问相应的k8s资源信息,因此该pod创建时需指定相应的ServiceAccount帐号。(如不额外创建对应的权限,可以使用当前系统较大权限的ServiceAccount帐号,如admin等,具体RBAC的创建方法描述,参考RBAC文章)
2、创建nfs的provisioner的deployment服务插件,用如连接管理底层存储磁盘的划分。
3、创建StorageClass,并与nfs的provisioner相匹配。
4、创建pvc,指定刚创建的storageclass做管理。
5、创建测试的pod,指定使用该pvc,查看该挂载情况。
2、相关服务的yaml文件
rbac.yaml文件
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: nfs-storage
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-client-provisioner-runner
rules:
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["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
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: nfs-storage
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
# replace with namespace where provisioner is deployed
namespace: nfs-storage
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
# replace with namespace where provisioner is deployed
namespace: nfs-storage
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: nfs-storage
roleRef:
kind: Role
name: leader-locking-nfs-client-provisioner
apiGroup: rbac.authorization.k8s.io
provisioner.yaml文件
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-client-provisioner
labels:
app: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: nfs-storage
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
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: fuseim.pri/ifs
- name: NFS_SERVER
value: 10.3.153.200
- name: NFS_PATH
value: /data/k8s
volumes:
- name: nfs-client-root
nfs:
server: 10.3.153.200
path: /data/k8s
storageclass.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"
nginx-pvc.yaml文件
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: nginx-disk
namespace: kang
spec:
accessModes:
- ReadWriteOnce
storageClassName: managed-nfs-storage
resources:
requests:
storage: 2Gi
nginx-pod.yaml文件
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-test
namespace: kang
spec:
replicas: 1
selector: #指定Pod的选择器
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:alpine
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
name: web
volumeMounts: #挂载容器中的目录到pvc nfs中的目录
- name: www
mountPath: /usr/share/nginx/html
volumes:
- name: www
persistentVolumeClaim: #指定pvc
claimName: nginx-disk