K8S persistent storage: NFS+StorageClass enables dynamic provisioning

Posted by shiny_spoon on Sat, 04 Sep 2021 01:08:03 +0200

Quickly create StorageClass of NFS type to realize dynamic provisioning

Introduction: persistent storage in K8S

K8s also introduces the concept of Persistent Volumes, which can separate storage and computing, manage storage resources and computing resources through different components, and then decouple the life cycle association between pod and Volume. In this way, after the pod is deleted, the PV used by it still exists and can be reused by the new pod.

Persistent storage in K8S is divided into Static Volume Provisioning and Dynamic Volume Provisioning

What does dynamic supply mean? That is, the cluster administrator does not pre allocate PV. He writes a template file, which is used to represent some parameters required to create a certain type of storage (block storage, file storage, etc.). These parameters are not concerned by the user and implement relevant parameters for the storage itself. Users only need to submit their own storage requirements, that is, PVC files, and specify the storage template (StorageClass) to be used in PVC.
The management and control components in the K8s cluster will dynamically generate the storage (PV) required by users in combination with the information of PVC and StorageClass. After binding PVC and PV, pod can use PV. The storage template required for storage is generated through StorageClass configuration, and then PV objects are dynamically created according to the needs of users to achieve on-demand distribution, which not only does not increase the difficulty of users, but also liberates the operation and maintenance work of cluster administrators.
Alibaba cloud native open class: Application storage and persistent data volumes: core knowledge


(image source: Alibaba cloud native open class: Application storage and persistent data volumes: core knowledge)

Environmental Science:
Kubernetes 1.21.1

Deploying NFS provisioner

Note: k8s NFS provisioner will report an error when creating pvc in version 1.21

E0903 08:00:24.858523 1 controller.go:1004] provision "default/test-claim" class "managed-nfs-storage": unexpected error getting claim reference: selfLink was empty, can't make reference

resolvent:
Modify the / etc / kubernetes / manifest / Kube apiserver.yaml file
Add -- feature gates = removeselflink = false

spec:
  containers:
  - command:
    - kube-apiserver
    - --feature-gates=RemoveSelfLink=false   # Add this line
    - --advertise-address=172.24.0.5
    - --allow-privileged=true
    - --authorization-mode=Node,RBAC
    - --client-ca-file=/etc/kubernetes/pki/ca.crt

Because it is a cluster deployed by kubedm, it will be automatically overloaded after modifying the kube-apiserver.yaml file

Here's how to deploy NFS provisioner
(1) Create ServiceAccount, ClusterRole, ClusterRoleBinding, etc. to authorize NFS client provider

# rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: default
---
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: default
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: default
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
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    # replace with namespace where provisioner is deployed
    namespace: default
roleRef:
  kind: Role
  name: leader-locking-nfs-client-provisioner
  apiGroup: rbac.authorization.k8s.io

(2) Deploying NFS client provisioner
Note: the address and directory should be changed to the corresponding configuration of the actual NFS service

# nfs-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: default  #Consistent with the namespace in the RBAC file
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nfs-client-provisioner
  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: gxf-nfs-storage  #The name of the provisioner. Make sure that the name is consistent with the provisioner name in the nfs-StorageClass.yaml file
            - name: NFS_SERVER
              value: 10.24.X.X    #NFS Server IP address
            - name: NFS_PATH  
              value: /home/nfs/1    #NFS mount volume
      volumes:
        - name: nfs-client-root
          nfs:
            server: 10.24.X.X  #NFS Server IP address
            path: /home/nfs/1     #NFS mount volume
# deploy
sudo kubectl apply -f rbac.yaml 
sudo kubectl apply -f nfs-provisioner.yaml 


NFS provisioner startup

Create StorageClass

# nfs-StorageClass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: managed-nfs-storage
provisioner: gxf-nfs-storage #The name here should be the same as the environment variable provider in the provisioner configuration file_ Name consistent
reclaimPolicy: Retain # The default is delete
parameters:
  archiveOnDelete: "true" # false means that when pv is deleted, the corresponding folder under nfs will also be deleted. true is the opposite
  1. The reclaimPolicy in nfs-StorageClass.yaml is not written (or deleted by default), and archiveOnDelete: "false". When pvc is deleted, the corresponding pv will be automatically deleted, and the files in the NFS file directory will be deleted at the same time;
  2. When reclaimPolicy: retain, archiveOnDelete: "true" in nfs storageclass.yaml, pv needs to be deleted manually after pvc is deleted, and files on nfs will not be deleted.
# deploy
sudo kubectl nfs-StorageClass.yaml

experiment

Experiment 1: deployment

Deploy a deployment with two copies and mount the same directory

Create pvc

Specify storageClass as managed NFS storage created above

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: test-claim
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 100Mi
  storageClassName: managed-nfs-storage

pv and pvc are created

A folder is generated in the nfs directory (corresponding to pv)

deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: test-deploy
  labels:
    app: test-deploy
  namespace: default  #Consistent with the namespace in the RBAC file
spec:
  replicas: 2
  selector:
    matchLabels:
      app: test-deploy
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: test-deploy
  template:
    metadata:
      labels:
        app: test-deploy
    spec:
      containers:
      - name: test-pod
        image: busybox:1.24
        command:
          - "/bin/sh"
        args:
          - "-c"
          # - "touch /mnt/SUCCESS3 && exit 0 || exit 1"   #Exit after creating a SUCCESS file
          - touch /mnt/SUCCESS5; sleep 50000
        volumeMounts:
          - name: nfs-pvc
            mountPath: "/mnt"
            # subPath: test-pod-3 # Sub path (this represents the test pod subdirectory under the storage volume)  
      volumes:
        - name: nfs-pvc
          persistentVolumeClaim:
            claimName: test-claim  #Consistent with PVC name

This deployment will create a file called "SUCCESS5" in the persistent volume

Experiment 2: deploy statefulset

# test-sts-1.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: test-sts
  labels:
    k8s-app: test-sts
spec:
  serviceName: test-sts-svc
  replicas: 3
  selector:
    matchLabels:
      k8s-app: test-sts
  template:
    metadata:
      labels:
        k8s-app: test-sts
    spec:
      containers:
        - image: busybox:1.24
          name: test-pod
          command:
            - "/bin/sh"
          args:
            - "-c"
            # - "touch /mnt/SUCCESS3 && exit 0 || exit 1"   #Exit after creating a SUCCESS file
            - touch /mnt/SUCCESS5; sleep 50000
          imagePullPolicy: IfNotPresent
          volumeMounts:
            - name: nfs-pvc
              mountPath: "/mnt"
  volumeClaimTemplates:
  - metadata:
      name: nfs-pvc
    spec:
      accessModes: ["ReadWriteMany"]
      storageClassName: managed-nfs-storage
      resources:
        requests:
          storage: 20Mi
kubectl apply -f test-sts-1.yaml
kubectl get sts
NAME       READY   AGE
test-sts   3/3     4m46s

Here, directly use volumeClaimTemplates to specify storageClass and required storage capacity. Without creating pvc in advance, k8s directly create corresponding pvc and pv. Since it is a stateful application, three PVCs and corresponding PVS are created.

reference resources:
StorageClass+NFS in k8s learning notes
Build NFS service under Ubuntu

Topics: Kubernetes nfs