一、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

具体操作过程不作展示,只需根据以上yaml文件,逐个运行即完成整个pvc与pv自动创建使用。在创建provisioner的deployment时,要确保该pod的状态为正常即可。