k8s persistent storage PV, PVC

Table of Contents

emptyDir storage volume

hostPath storage volume

nfs shared storage volume

PVC and PV

PV creation and destruction process

NFS uses PV and PVC

Build StorageClass + NFS to realize dynamic PV creation of NFS


The life cycle of files on the container disk is short-lived, which causes some problems when running important applications in the container. First, when a container crashes, the kubelet will restart it, but the files in the container will be lost – the container is restarted in a clean state (the original state of the image). Secondly, when multiple containers are running simultaneously in a Pod, files usually need to be shared between these containers. The Volume abstraction in Kubernetes solves these problems very well. Containers in the Pod share the Volume through the Pause container.

emptyDir storage volume

When a Pod is assigned to a node, the emptyDir volume is first created and exists as long as the Pod is running on that node. As the name of the volume states, it is initially empty. Containers in a Pod can read and write the same files in the emptyDir volume, although the volume can be mounted to the same or different paths in each container. When a Pod is removed from a node for any reason, the data in emptyDir is permanently deleted.

mkdir /opt/volumes
cd /opt/volumes

vim pod-emptydir.yaml
apiVersion: v1
Kind: Pod
metadata:
  name: pod-emptydir
  namespace:default
  labels:
    app: myapp
    tier: frontend
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
    imagePullPolicy: IfNotPresent
    ports:
    - name: http
      containerPort: 80
#Define container mounting content
    volumeMounts:
#The storage volume name used. If the value of the volume field name below is the same, it means that the storage volume of the volume is used.
    - name: html
#Which directory to mount to in the container
      mountPath: /usr/share/nginx/html/
  - name: busybox
    image: busybox:latest
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - name: html
#Define the mount storage name and mount path within the container
      mountPath: /data/
    command: ['/bin/sh','-c','while true;do echo $(date) >> /data/index.html;sleep 2;done']
  #Define storage volume
  volumes:
  #Define storage volume name
  - name: html
    #Define storage volume type
    emptyDir: {}
\t
\t
kubectl apply -f pod-emptydir.yaml
kubectl get pods -o wide

//Two containers are defined above, one of which is to input the date into index.html, and then verify whether the date can be obtained by accessing nginx’s html. To verify that the emptyDir mounted between the two containers is shared.

curl 10.244.2.3

hostPath storage volume

A hostPath volume mounts a file or directory in the node’s file system into the cluster.
hostPath can achieve persistent storage, but it will also cause data loss when the node node fails.

//Create a mounting directory on the node01 node

mkdir -p /data/pod/volume1
echo 'node01.my.com' > /data/pod/volume1/index.html

//Create a mounting directory on the node02 node

mkdir -p /data/pod/volume1
echo 'node02.my.com' > /data/pod/volume1/index.html

//Create Pod resources

vim pod-hostpath.yaml
apiVersion: v1
Kind: Pod
metadata:
  name: pod-hostpath
  namespace:default
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
#Define container mounting content
    volumeMounts:
#The storage volume name used. If the value of the volume field name below is the same, it means that the storage volume of the volume is used.
    - name: html
#Which directory to mount to in the container
      mountPath: /usr/share/nginx/html
#Read-write mounting mode, the default is read-write mode false
readOnly: false
  The #volumes field defines the host or distributed file system storage volume associated with the paues container
  volumes:
    #Storage volume name
    - name: html
#Path, storage path for the host machine
      hostPath:
#The path to the directory on the host machine
        path: /data/pod/volume1
#Define the type, which means that if the host does not have this directory, it will be automatically created.
        type: DirectoryOrCreate


kubectl apply -f pod-hostpath.yaml

//Access test

kubectl get pods -o wide
curl 10.244.1.4

//Delete the pod, rebuild it again, and verify whether the original content can still be accessed

kubectl delete -f pod-hostpath.yaml
kubectl apply -f pod-hostpath.yaml
kubectl get pods -o wide
curl 10.244.1.5

nfs shared storage volume

//Install nfs on the stor01 node and configure the nfs service

hostnamectl set-hostname stor01

mkdir /data/volumes -p
chmod 777 /data/volumes
rpm -q rpcbind nfs-utils
vim /etc/exports
/data/volumes 20.0.0.0/24(rw,no_root_squash)
systemctl start rpcbind
systemctl start nfs
systemctl enable rpcbind nfs

kubectl apply -f pod-nfs-vol.yaml
showmount -e

on node01 and node02

showmount -e 20.0.0.104

Operate on the master node

vim pod-nfs-vol.yaml

apiVersion: v1
Kind: Pod
metadata:
  name: pod-nfs
  namespace:default
  labels:
    app:myapp
spec:
  containers:
  - name: myapp
    image: soscscs/myapp:v1
    volumeMounts:
    - name: xiaoma
      mountPath: /usr/share/nginx/html
      readOnly: false
  nodeSelector:
    kubernetes.io/hostname: node02
  volumes:
    - name: xiaoma
      nfs:
        path: /data/volumes
        server: 20.0.0.104


kubectl apply -f pod-nfs-vol.yaml
kubectl get pods -o wide

//Create index.html on the nfs server

cd /data/volumes
vim index.html
<h1>nfs stor01</h1>

//master node operation

Test:
curl 10.244.2.4
kubectl delete -f pod-nfs-vol.yaml #Delete nfs-related pods and re-create them to obtain persistent storage of data
kubectl apply -f pod-nfs-vol.yaml

PVC and PV

The full name of PV is Persistent Volume, which is a persistent storage volume. It is used to describe or define a storage volume, which is usually defined by operation and maintenance engineers.

The full name of PVC is Persistent Volume Claim, which is a request for persistent storage. It is used to describe what kind of PV storage you want to use or what conditions you want to meet.

The usage logic of PVC: Define a storage volume in the Pod (the storage volume type is PVC), specify the size directly when defining, the PVC must establish a relationship with the corresponding PV, the PVC will apply to the PV according to the configuration definition, and the PV is Created from storage space. PV and PVC are storage resources abstracted by Kubernetes.

The PV and PVC modes introduced above require operation and maintenance personnel to create PVs first, and then developers define PVCs for one-to-one bonding. However, if there are thousands of PVC requests, thousands of PVs need to be created. Maintenance costs are very high for operation and maintenance personnel. Kubernetes provides a mechanism to automatically create PVs called StorageClass, which is used to create PV templates.

Creating a StorageClass requires defining the attributes of the PV, such as storage type, size, etc.; in addition, creating such a PV requires the use of storage plug-ins, such as Ceph, etc. With these two pieces of information, Kubernetes can find the corresponding StorageClass based on the PVC submitted by the user. Then Kubernetes will call the storage plug-in declared by the StorageClass to automatically create the required PV and bind it.

PV is a resource in the cluster. PVC is a request for these resources and an index check of the resources.

The interaction between PV and PVC follows this life cycle:

Provisioning ---> Binding ---> Using ---> Releasing ---> Recycling

●Provisioning, that is, the creation of PV, you can create PV directly (static method), or you can use StorageClass to create it dynamically
●Binding, assigning PV to PVC
●Using, Pod uses the Volume through PVC, and can prevent deletion of the PVC in use through admission control StorageProtection (1.9 and previous versions are PVCProtection)
●Releasing, Pod releases Volume and deletes PVC
●Reclaiming, recycling PV, you can keep the PV for next use, or you can delete it directly from the cloud storage

According to these 5 stages, there are 4 states of PV:
●Available: Indicates available status and has not been bound by any PVC.
●Bound: indicates that the PV has been bound to the PVC
●Released: Indicates that the PVC has been deleted, but the resources have not been reclaimed by the cluster.
●Failed: Indicates that the automatic recycling of the PV failed

PV creation and destruction process

The specific process of a PV from creation to destruction is as follows:
1. After a PV is created, its status will change to Available, waiting to be bound by the PVC.
2. Once bound by PVC, the status of PV will change to Bound, and it can be used by Pods with corresponding PVC defined.
3. After the Pod is used, the PV will be released, and the status of the PV will change to Released.
4. The PV that becomes Released will be recycled according to the defined recycling strategy. There are three recycling strategies, Retain, Delete and Recycle. Retain means to retain the scene. The K8S cluster does nothing and waits for the user to manually process the data in the PV. After the processing is completed, the PV is manually deleted. Delete policy, K8S will automatically delete the PV and the data in it. In Recycle mode, K8S will delete the data in the PV, and then change the status of the PV to Available, which can then be bound and used by new PVCs.

Set local mapping on node01 and node02

 cat>> /etc/hosts<<EOF
20.0.0.104 stor01
 EOF
kubectl explain pv #View how pv is defined

FIELDS:
apiVersion: v1
kind: PersistentVolume
metadata: #Since PV is a cluster-level resource, that is, PV can be used across namespaces, so there is no need to configure the namespace in the PV's metadata.
name:
spec
\t
kubectl explain pv.spec #View the specifications defined by pv
spec:
  nfs: (define storage type)
    path: (define the mount volume path)
    server: (define server name)
  accessModes: (define the access model, there are the following three access models, which exist in the form of a list, which means multiple access modes can be defined)
    - ReadWriteOnce #(RWO) storage is readable and writable, but only supports being mounted by a single Pod
- ReadOnlyMany #(ROX) storage can be mounted by multiple Pods in a read-only manner
- ReadWriteMany # (RWX) storage can be shared by multiple Pods in a read and write manner Note: Official website
#nfs supports all three; iSCSI does not support ReadWriteMany (iSCSI is a network storage technology that runs the SCSI protocol on an IP network); HostPath does not support ReadOnlyMany and ReadWriteMany.
  capacity: (defines storage capacity, generally used to set storage space)
    storage: 2Gi (specify size)
  storageClassName: (custom storage class name, this configuration is used to bind PVC and PV with the same category)
  persistentVolumeReclaimPolicy: Retain #Recycling policy (Retain/Delete/Recycle)
#Retain: When the PVC bound to it is deleted, the PV is marked as released (the PVC is unbound from the PV but the recycling strategy has not been implemented) and the previous data is still saved on the PV, but the PV cannot be You need to manually process the data and delete the PV.
#Delete: Delete the backend storage resources connected to the PV (only supported by AWS EBS, GCE PD, Azure Disk and Cinder)
#Recycle (recycling): delete data, the effect is equivalent to executing rm -rf /thevolume/* (only supported by NFS and HostPath)
kubectl explain pvc #View how PVC is defined

KIND: PersistentVolumeClaim
VERSION: v1
FIELDS:
   apiVersion <string>
   kind <string>
   metadata <Object>
   spec<Object>

#The spec key fields in PV and PVC must match, such as storage (storage) size, access mode (accessModes), and storage class name (storageClassName)
kubectl explain pvc.spec
spec:
  accessModes: (define the access mode, which must be a subset of the PV’s access mode)
  resources:
    requests:
      storage: (define the size of the requested resource)
  storageClassName: (defines the storage class name, this configuration is used to bind PVC and PV with the same category)

NFS uses PV and PVC

1. Configure nfs storage
Created on sto01

mkdir v{1,2,3,4,5}

vim /etc/exports
/data/volumes/v1 20.0.10.0/24(rw,no_root_squash)
/data/volumes/v2 20.0.0.0/24(rw,no_root_squash)
/data/volumes/v3 20.0.0.0/24(rw,no_root_squash)
/data/volumes/v4 20.0.0.0/24(rw,no_root_squash)
/data/volumes/v5 20.0.0.0/24(rw,no_root_squash)

exportfs -arv

showmount -e

Official documentation: https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/configure-persistent-volume-storage/#create-a-persistentvolume

2. Define PV (on master)
//Define 5 PVs here, and define the mounting path and access mode, as well as the size of the PV division.

vim pv-demo.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv001
  labels:
    name: pv001
spec:
  nfs:
    path: /data/volumes/v1
    server: stor01
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    Storage: 1Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv002
  labels:
    name: pv002
spec:
  nfs:
    path: /data/volumes/v2
    server: stor01
  accessModes: ["ReadWriteOnce"]
  capacity:
    Storage: 2Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv003
  labels:
    name: pv003
spec:
  nfs:
    path: /data/volumes/v3
    server: stor01
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    Storage: 2Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv004
  labels:
    name: pv004
spec:
  nfs:
    path: /data/volumes/v4
    server: stor01
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 4Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv005
  labels:
    name: pv005
spec:
  nfs:
    path: /data/volumes/v5
    server: stor01
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    Storage: 5Gi


kubectl apply -f pv-demo.yaml
#View pv
kubectl get pv

3. Define PVC
//The access mode of pvc is defined here as multi-channel read and write. This access mode must be among the access modes defined by pv previously. Define the size of the PVC application to be 2Gi. At this time, the PVC will automatically match the multi-channel read and write PV with a size of 2Gi. The status of the PVC successfully obtained by matching is Bound.

vim pod-vol-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name:mypvc
  namespace:default
spec:
  accessModes: ["ReadWriteMany"]
  resources:
    requests:
      Storage: 2Gi
---
apiVersion: v1
Kind: Pod
metadata:
  name: pod-vol-pvc
  namespace:default
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html
  volumes:
    - name: html
      persistentVolumeClaim:
        claimName: mypvc

kubectl apply -f pod-vol-pvc.yaml
kubectl get pv
kubectl get pvc

4. Test access
//Create index.html on the storage server, write data, and view the corresponding page by accessing the Pod.

cd /data/volumes/v3/
echo "welcome to use pv3" > index.html
kubectl get pods -o wide
curl 10.244.1.6

Build StorageClass + NFS to realize dynamic PV creation of NFS

The dynamic PV creation supported by Kubernetes itself does not include NFS, so you need to use an external storage volume plug-in to allocate PVs. For details, see:

https://kubernetes.io/zh/docs/concepts/storage/storage-classes/

The volume plug-in is called Provisioner (storage allocator), and NFS uses nfs-client. This external volume plug-in will automatically create PVs using the configured NFS server.
Provisioner: used to specify the type of Volume plug-in, including built-in plug-ins (such as kubernetes.io/aws-ebs) and external plug-ins (such as ceph.com/cephfs provided by external-storage).

1. Install nfs on the stor01 node and configure the nfs service

mkdir /opt/k8s
chmod 777 /opt/k8s/
vim /etc/exports
/opt/k8s 20.0.0.0/24(rw,no_root_squash,sync)
systemctl restart nfs

2. Create a Service Account to manage the permissions of NFS Provisioner to run in the k8s cluster, and set nfs-client rules for PV, PVC, StorageClass, etc.

vim nfs-client-rbac.yaml
#Create a Service Account to manage the permissions of NFS Provisioner running in the k8s cluster
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-client-provisioner
---
#Create cluster role
apiVersion: rbac.authorization.k8s.io/v1
kind:ClusterRole
metadata:
  name: nfs-client-provisioner-clusterrole
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: ["list", "watch", "create", "update", "patch"]
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["create", "delete", "get", "list", "watch", "patch", "update"]
---
#Cluster role binding
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: nfs-client-provisioner-clusterrolebinding
subjects:
- kind: ServiceAccount
  name: nfs-client-provisioner
  namespace:default
roleRef:
  kind:ClusterRole
  name: nfs-client-provisioner-clusterrole
  apiGroup: rbac.authorization.k8s.io


kubectl apply -f nfs-client-rbac.yaml

3. Use Deployment to create NFS Provisioner
NFS Provisione (ie nfs-client) has two functions: one is to create a mount point (volume) under the NFS shared directory, and the other is to associate the PV with the NFS mount point.

#Since selfLink is enabled in version 1.20, an error will be reported when k8s 1.20+ version dynamically generates pv through nfs provisioner. The solution is as follows:

vim /etc/kubernetes/manifests/kube-apiserver.yaml
spec:
  containers:
  - command:
    - kube-apiserver
    - --feature-gates=RemoveSelfLink=false #Add this line
    - --advertise-address=192.168.80.20
...
kubectl apply -f /etc/kubernetes/manifests/kube-apiserver.yaml
kubectl delete pods kube-apiserver -n kube-system
kubectl get pods -n kube-system | grep apiserver

#Create NFS Provisioner
First, pass nfs-client-provisioner.tar to node01 and node02 to speed up image pulling.

docker load -i nfs-client-provisioner.tar
scp nfs-client-provisioner.tar @20.0.0.103:/opt/
docker images View images
vim nfs-client-provisioner.yaml
Kind: Deployment
apiVersion: apps/v1
metadata:
  name: nfs-client-provisioner
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nfs-client-provisioner
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: nfs-client-provisioner
    spec:
      serviceAccountName: nfs-client-provisioner #Specify Service Account account
      containers:
        - name: nfs-client-provisioner
          image: quay.io/external_storage/nfs-client-provisioner:latest
          imagePullPolicy: IfNotPresent
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME
              value: nfs-storage #Configure the provisioner Name and ensure that the name is consistent with the provisioner name in the StorageClass resource
            - name: NFS_SERVER
              value: stor01 #Configure the bound nfs server
            - name: NFS_PATH
              value: /opt/k8s #Configure the bound nfs server directory
      volumes: #Declare nfs data volume
        - name: nfs-client-root
          nfs:
            server: stor01
            path: /opt/k8s
\t
\t
kubectl apply -f nfs-client-provisioner.yaml
kubectl get pod

4. Create StorageClass, which is responsible for establishing PVC and calling NFS provisioner to perform scheduled work, and associate PV with PVC.

vim nfs-client-storageclass.yaml

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-client-storageclass
provisioner: nfs-storage #The name here should be consistent with the environment variable PROVISIONER_NAME in the provisioner configuration file
parameters:
  archiveOnDelete: "false" #false means that the data will not be archived when the PVC is deleted, that is, the data will be deleted
kubectl apply -f nfs-client-storageclass.yaml
kubectl get storageclass

5. Create PVC and Pod tests

vim test-pvc-pod.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: test-nfs-pvc
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: nfs-client-storageclass #Associate StorageClass object
  resources:
    requests:
      Storage: 1Gi
---
apiVersion: v1
Kind: Pod
metadata:
  name: test-storageclass-pod
spec:
  containers:
  - name: busybox
    image: busybox:latest
    imagePullPolicy: IfNotPresent
    command:
    - "/bin/sh"
    - "-c"
    args:
    - "sleep 3600"
    volumeMounts:
    - name: nfs-pvc
      mountPath: /mnt
  restartPolicy: Never
  volumes:
  - name: nfs-pvc
    persistentVolumeClaim:
      claimName: test-nfs-pvc #Consistent with the PVC name
\t  
\t  
kubectl apply -f test-pvc-pod.yaml

//PVC automatically applies for space through StorageClass

kubectl get pvc

//Check whether the corresponding directory is generated on the NFS server. The automatically created PV will be placed on the NFS server in the directory format of n a m e s p a c e ? {namespace}- namespace? {pvcName}-${pvName}

ls /opt/k8s/

//Enter Pod and write a file under the mounting directory /mnt, and then check whether the file exists on the NFS server

kubectl exec -it test-storageclass-pod sh
/ # cd /mnt/
/mnt # echo 'ceshiyixia' > test.txt

// Found to exist on the NFS server, indicating successful verification

cat /opt/k8s/default-test-nfs-pvc-pvc-8885e05c-bc65-4930-a2dc-dc3b42c38111/test.txt

The knowledge points of the article match the official knowledge archives, and you can further learn relevant knowledge. Cloud native entry-level skills treeContainer orchestration (production environment k8s)kubelet, kubectl, kubeadm three-piece set 17034 people Currently studying the system