kubernetes persistent storage PV and PVC

Posted by DepretioN on Wed, 15 Dec 2021 03:28:25 +0100

kubernetes persistent storage PV and PVC

1. Introduction of PV and PVC

Volume provides a very good data persistence scheme, but there are still deficiencies in manageability.
Take the previous example of AWS EBS. To use Volume, Pod must know the following information in advance:
The current Volume is from AWS EBS.
The EBS Volume has been created in advance and the exact volume ID is known.
The Pod is usually maintained by the application developer, while the Volume is usually maintained by the storage system administrator. Developers need to get the above information:
Or ask the administrator.
Or you're the administrator.
This brings a management problem: the responsibilities of application developers and system administrators are coupled. If the system scale is small or for the development environment, it is acceptable. However, when the cluster size becomes larger, especially for the generation environment, considering efficiency and security, this has become a problem that must be solved.

The solutions given by Kubernetes are PersistentVolume and PersistentVolumeClaim.
A persistent Volume (PV) is a piece of storage space in an external storage system that is created and maintained by an administrator. Like Volume, PV is persistent and its life cycle is independent of Pod.
Persistent volume claim (PVC) is a claim for PV. PVC is usually created and maintained by ordinary users. When storage resources need to be allocated to the Pod, the user can create a PVC to indicate the capacity and access mode (such as read-only) of the storage resources. Kubernetes will find and provide qualified PV.
With PersistentVolumeClaim, users only need to tell Kubernetes what storage resources they need, without caring about the underlying details such as where the real space is allocated and how to access it. The underlying information of these storage providers is handled by the administrator. Only the administrator should care about the details of creating a PersistentVolume.

2. Persistent storage through NFS

2.1 configuring nfs

Install nfs on all nodes

[root@k8s-master]# yum install -y nfs-common nfs-utils

Create a shared directory on the master node

[root@k8s-master]# mkdir /nfsdata

Authorized shared directory

[root@k8s-master]# chmod 666 /nfsdata

Edit exports file

[root@k8s-master]# vim /etc/exports
/nfsdata *(rw,no_root_squash,no_all_squash,sync)

Configuration effective

Start rpc and nfs (note the order)

[root@k8s-master]# systemctl start rpcbind
[root@k8s-master]# systemctl start nfs
# Make configuration effective
[root@k8s-master]# exportfs -r
#Check whether the configuration is effective
[root@k8s-master]# exportfs
/nfsdata      	<world>

As a preparatory work, we have built an NFS server on the k8s master node. The directory is / nfsdata:

2.2 create PV

Next, create a PV mypv1 configuration file nfs-pv1 YML as follows:

[root@k8s-master ~]# vim nfs-pv1.yml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mypv1
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: nfs
  nfs:
    path: /nfsdata
    server: 192.168.119.163  #Specify the address of the machine where the nfs directory is located
  1. Capacity specifies that the capacity of PV is 1G.

  2. accessModes specifies that the access mode is ReadWriteOnce. The supported access modes are:
    ReadWriteOnce – PV can mount to a single node in read-write mode.
    ReadOnlyMany – PV can mount to multiple nodes in read only mode.
    ReadWriteMany – PV can mount to multiple nodes in read-write mode.

  3. persistentVolumeReclaimPolicy specifies that when the recycling policy of PV is Recycle, the supported policies are:
    Retain – the administrator needs to recycle manually.
    Recycle – clear the data in PV. The effect is equivalent to executing rm -rf /thevolume / *.
    Delete – delete the corresponding storage resources on the Storage Provider, such as AWS EBS, GCE PD, Azure Disk, OpenStack Cinder Volume, etc.

  4. storageClassName specifies that the class of PV is nfs. It is equivalent to setting a classification for PV. PVC can specify class to apply for PV of corresponding class.

  5. Specify the directory corresponding to PV on NFS server.

Create mypv1:

[root@k8s-master ~]# kubectl apply -f nfs-pv1.yml

STATUS is Available, indicating that mypv1 is ready and can be applied by PVC.

2.3 creating PVC

Next, create the PVC mypvc1 configuration file nfs-pvc1 YML as follows:

[root@k8s-master ~]# vim nfs-pvc1.yml 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mypvc1
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: nfs

PVC is very simple. You only need to specify the capacity, access mode and class of PV.

Execute the command to create mypvc1:

[root@k8s-master ~]# kubectl apply -f nfs-pvc1.yml

From the output of kubectl get pvc and kubectl get pv, you can see that mypvc1 has Bound to mypv1 and the application is successful.

2.4 creating a pod

pv and pvc have been created above. You can directly use this pvc in pod

[root@k8s-master ~]# vim pod1.yml 
apiVersion: v1
kind: Pod
metadata:
  name: mypod1
spec:
  containers:
    - name: mypod1
      image: busybox
      args:
      - /bin/sh
      - -c
      - sleep 30000
      volumeMounts:
      - mountPath: "/mydata"
        name: mydata
  volumes:
    - name: mydata
      persistentVolumeClaim:
        claimName: mypvc1

Similar to the format of using a normal Volume, specify the Volume requested using mypvc1 through persistentVolumeClaim in volumes.

Create mypod1 with the command:

[root@k8s-master ~]# kubectl apply -f pod1.yml

2.5 verification

[root@k8s-master ~]# docker exec -it mypod1 /bin/sh
/ # ls mydata/
/ # echo "do you think I look like someone who can" > mydata / Hello txt
/ # ls mydata/
hello.txt
/ # exit
[root@k8s-master ~]# ls /nfsdata/    #It can also be viewed in the shared directory of nfs, indicating that the volume sharing is successful
hello.txt
[root@k8s-master ~]# cat /nfsdata/hello.txt 
hello
 Visible, in Pod Files created in /mydata/hello.txt It has indeed been saved to NFS Server directory /nfsdata Yes.
If no longer needed PV,Available delete PVC recovery PV. 

Here, you can try to delete the file on either side, and the file will disappear at both ends;

3. Recovery of PV

When pv is no longer needed, it can be recycled by deleting pvc. Before deleting pvc, the status of pv is Bound

Delete pod

[root@k8s-master yaml]# kubectl delete pod mypod1

Delete pvc

[root@k8s-master yaml]# kubectl delete pvc mypvc1

Check the status of pv again

[root@k8s-master yaml]# kubectl get pv

After deleting PVC, the status of pv becomes Available. At this time, it can be applied for by new PVC after unbinding.

/The files in the nfsdata file were deleted

Because the recycling policy of PV is set to Recycle, the data will be cleared,

But this may not be the result we want. If we want to Retain data, we can set the policy to Retain

[root@k8s-master yaml]# vim nfs-pv1.yml

[root@k8s-master yaml]# kubectl apply -f nfs-pv1.yml

The recycling policy has been changed to Retain. Verify its effect through the following steps:

# Recreate mypvc1
[root@k8s-master yaml]# kubectl apply -f nfs-pvc1.yml
# Recreate the pod and reference mypvc1
[root@k8s-master yaml]# kubectl apply -f pod1.yml
# Enter the pod and create a file
[root@k8s-master yaml]# kubectl get pod -o wide
NAME                       READY     STATUS    RESTARTS   AGE       IP            NODE
mypod1                     1/1       Running   0          1m        172.17.44.2   192.168.119.164
# Come to 164
[root@k8s-node01]# docker exec -it mypod1 /bin/sh
/ # echo 'do you think I can' > mydata / Hello txt
/ # ls mydata/
hello.txt
/ # exit

# Verify in nfs directory
[root@k8s-master yaml]# ls /nfsdata/
hello.txt
[root@k8s-master yaml]# cat /nfsdata/hello.txt 
Do you think I will

# Delete pod
[root@k8s-master yaml]# kubectl delete -f pod1.yml 
pod "mypod1" deleted
[root@k8s-master yaml]# ls /nfsdata/
hello.txt
# Delete pvc (mypvc1)
[root@k8s-master yaml]# kubectl delete pvc mypvc1
persistentvolumeclaim "mypvc1" deleted
[root@k8s-master yaml]# ls /nfsdata/
hello.txt
[root@k8s-master yaml]# cat /nfsdata/hello.txt 
Do you think I will

# Found that the data is still retained

Although the data in mypv1 is retained, its PV status will always be Released and cannot be applied by other PVC. In order to reuse storage resources, you can delete and recreate mypv1. The delete operation only deletes the PV object, and the data in the storage space will not be deleted.

[root@k8s-master yaml]# ls /nfsdata/
hello.txt
[root@k8s-master yaml]# kubectl delete pv mypv1
persistentvolume "mypv1" deleted
[root@k8s-master yaml]# ls /nfsdata/
hello.txt
[root@k8s-master yaml]# kubectl apply -f nfs-pv1.yml 
persistentvolume/mypv1 created
[root@k8s-master yaml]# kubectl get pod
No resources found in default namespace.
[root@k8s-master yaml]# kubectl get pv

The newly created mypv1 is Available and can be applied by PVC.

PV also supports the recycle policy of Delete, which will Delete the storage space corresponding to PV on the Storage Provider. NFS PV does not support Delete. Providers that support Delete include AWS EBS, GCE PD, Azure Disk, OpenStack Cinder Volume, etc.

4. Static supply of PV / PVC

All nodes Download nfs
yum install -y nfs-common nfs-utils 

master Node as nfs Server
[root@k8s-master]# cat /etc/exports
/data/opv *(rw,no_root_squash,no_all_squash,sync)
[root@k8s-master]# chmod 777 -R /data/opv
# Make configuration effective
[root@k8s-master]# exportfs -r
#Check whether the configuration is effective
[root@k8s-master]# exportfs

master Node operation
#1. Definition pv
[root@k8s-master yaml]# vim pv-pod.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: my-pv
spec:
  capacity:
    storage: 5Gi
  accessModes:
    - ReadWriteMany
  nfs:
    path: /data/opv  #Directory shared by nfs server
    server: 192.168.119.163   #Address of nfs server
[root@k8s-master yaml]# kubectl apply -f pv-pod.yaml

#2. Define pvc and deployment
[root@k8s-master yaml]# vim pvc-pod.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        #The name of the enabled data volume is wwwroot, and it is mounted in the html directory of nginx
        volumeMounts:
        - name: wwwroot
          mountPath: /usr/share/nginx/html
        ports:
        - containerPort: 80
    #Define the name of the data volume as wwwroot and the type as pvc
      volumes:
      - name: wwwroot
        persistentVolumeClaim:
          claimName: my-pvc


---
# Define the data source of pvc and match pv according to the capacity
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  #Corresponding to the above name
  name: my-pvc
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 5G
[root@k8s-master yaml]# kubectl apply -f pvc-pod.yaml

#3. Expose the port
[root@k8s-master yaml]# vim pv-service.yaml 
apiVersion: v1
kind: Service
metadata:
  name: pv-svc
spec:
  type: NodePort
  ports:
    - port: 8080
      nodePort: 30001
      targetPort: 80
  selector:   #selector
    app: nginx

# 4. Start
[root@k8s-master yaml]# kubectl apply -f pv-service.yaml
#5.nfs server operation
[root@k8s-master yaml]# echo hello >> /data/opv/index.html 
#6. Visit and see the effect

5. Dynamic supply of PV

In the previous example, we created PV in advance, then applied for PV through PVC and used it in Pod. This method is called Static Provision.

The corresponding is dynamic provision, that is, if there is no PV that meets the PVC conditions, it will dynamically create PV. Compared with static supply, dynamic supply has obvious advantages: there is no need to create PV in advance, which reduces the workload of administrators and has high efficiency.

Dynamic provisioning is implemented through StorageClass, which defines how to create PV. The following are two examples.

StorageClass standard

StorageClass slow:

Both storageclasses will dynamically create AWS EBS. The difference is that standard creates gp2 EBS, while slow creates io1 EBs. For parameters supported by different types of EBS, please refer to AWS official documents.

StorageClass supports two reclaimpolicies: Delete and Retain. The default is Delete.

As before, when applying for PV, PVC only needs to specify StorageClass, capacity and access mode, such as:

In addition to AWS EBS, Kubernetes supports a variety of other providers that dynamically supply PV. For a complete list, please refer to https://kubernetes.io/docs/concepts/storage/storage-classes/#provisioner

6. Persistent storage of PV and PVC in applications

[root@k8s-master yaml]# kubectl delete -f pod1.yml
[root@k8s-master yaml]# vim pod1.yml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mydep
spec:
  selector:
    matchLabels:
      app: busy
  replicas: 1
  template:
    metadata:
      labels:
        app: busy
    spec:
      containers:
        - name: mypod1
          image: busybox
          args:
          - /bin/sh
          - -c
          - sleep 30000
          volumeMounts:
          - mountPath: "/mydata"
            name: mydata
      volumes:
        - name: mydata
          persistentVolumeClaim:
            claimName: mypvc1
            
[root@k8s-master pv]# kubectl apply -f pod1.yml 
[root@k8s-master yaml]# kubectl get pod -o wide
[root@k8s-master yaml]# docker exec -it dc31ac288bfa /bin/sh
/ # echo "I won't" > mydata / Hello txt
/ # exit

Check which node the pod is running on and turn off the node. If another node is found, it will take over, and the data still exists

7. Practical project of persistent storage of PV and PVC in mysql

The following shows how to provide persistent storage for MySQL database. The steps are as follows:

  1. Create PV and PVC.
  2. Deploy MySQL.
  3. Add data to MySQL.
  4. Simulate node downtime, and Kubernetes will automatically migrate MySQL to other nodes.
  5. Verify data consistency.

First create PV and PVC with the following configuration:

mysql-pv.yml

# Create persistent directory
[root@k8s-master yaml]# mkdir /nfsdata/mysql-pv
[root@k8s-master yaml]# vim mysql-pv.yml 
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mysql-pv
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: nfs
  nfs:
    path: /nfsdata/mysql-pv
    server: 192.168.119.163
[root@k8s-master yaml]# kubectl apply -f mysql-pv.yml

mysql-pvc.yml

[root@k8s-master yaml]# vim mysql-pvc.yml 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: nfs
  
[root@k8s-master yaml]# kubectl apply -f mysql-pvc.yml

Next, deploy MySQL. The configuration file is as follows:

[root@k8s-master yaml]# vim mysqlpod.yml 
apiVersion: v1
kind: Service
metadata:
  name: mysql
spec:
  ports:
  - port: 3306
  selector:
    app: mysql
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - image: mysql:5.7.5 #The image here must be selected correctly to ensure that it can be pulled, and variables can be used
        name: mysql
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: password
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: mysql-persistent-storage
          mountPath: /var/lib/mysql
      volumes:
      - name: mysql-persistent-storage
        persistentVolumeClaim:
          claimName: mysql-pvc
          
[root@k8s-master yaml]# kubectl apply -f mysqlpod.yml

PVC MySQL PVC bound PV MySQL PV will be mount ed to the MySQL data directory var/lib/mysql.

MySQL is deployed to 163

Because it is assigned to the primary node, it cannot be shut down, so delete it and let it reassign the node

Wait for the image to be pulled and start the pod

1. Switch to mysql database.

2. Create the database company and table t1.

[root@k8s-node01 ~]# docker exec -it e0effdbcddb4 bash
root@mysql-7f65bf577-b2ssv:/# mysql -uroot -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 5.7.5-m15 MySQL Community Server (GPL)

Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> create database company;
create database  company;
Query OK, 1 row affected (0.03 sec)

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| company            |
| mysql              |
| performance_schema |
+--------------------+
4 rows in set (0.02 sec)
mysql> use company;
Database changed
mysql> create table t1(Sno char(9) primary key,Sname char(20) not null,Ssex char(2),Sage smallint,Sdept char(20));
Query OK, 0 rows affected (0.07 sec)

3. Insert data.

Add table data
mysql> insert into t1(Sno,Sname,Ssex,Sage,Sdept)values('202112081','Li Si','male','20','CS');
Query OK, 1 row affected (0.02 sec)

mysql> insert into t1(Sno,Sname,Ssex,Sage,Sdept)values('202112082','Zhang San','female','19','CS');
Query OK, 1 row affected (0.01 sec)

mysql> insert into t1(Sno,Sname,Ssex,Sage,Sdept)values('202112083','Wang Wu','female','18','MA');
Query OK, 1 row affected (0.00 sec)

mysql> insert into t1(Sno,Sname,Ssex,Sage,Sdept)values('202112085','Pockmarks','male','19','IS');
Query OK, 1 row affected (0.01 sec)

4. Confirm that the data has been written.

mysql> show tables;
+-------------------+
| Tables_in_company |
+-------------------+
| t1                |
+-------------------+
1 row in set (0.01 sec)

mysql> select * from t1;
+-----------+-------+------+------+-------+
| Sno       | Sname | Ssex | Sage | Sdept |
+-----------+-------+------+------+-------+
| 202112081 |       |      |   20 | CS    |
| 202112082 |       |      |   19 | CS    |
| 202112083 |       |      |   18 | MA    |
| 202112085 |       |      |   19 | IS    |
+-----------+-------+------+------+-------+
4 rows in set (0.00 sec)

You can see that the data has been written. Due to coding problems, Chinese characters are not recognized. Is there a problem? no problem!

If you have problems, modify the code yourself.

Close k8s-node01 to simulate node downtime.

# Shutdown node01
[root@k8s-node01 ~]# poweroff
# The master needs to wait for some time to view mysql
[root@k8s-master yaml]# kubectl get pod -o wide
NAME                       READY     STATUS    RESTARTS   AGE       IP            NODE
mysql-7f65bf577-b2ssv      1/1       Unknown   0          2h        172.17.44.2   192.168.119.164
mysql-7f65bf577-hkvld      1/1       Running   0          9s        172.17.78.2   192.168.119.163

# Verify data consistency:
# Because node1 node has been down, the master node takes over the task, and the pod transfer needs to wait for a period of time, about five minutes

[root@k8s-master yaml]# docker exec -it 7f001e01d786 bash
root@mysql-7f65bf577-hkvld:/# mysql -uroot -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 5.7.5-m15 MySQL Community Server (GPL)

Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| company            |
| mysql              |
| performance_schema |
+--------------------+
4 rows in set (0.01 sec)

mysql> use company;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> show tables;
+-------------------+
| Tables_in_company |
+-------------------+
| t1                |
+-------------------+
1 row in set (0.00 sec)

mysql> select * from t1;
+-----------+-------+------+------+-------+
| Sno       | Sname | Ssex | Sage | Sdept |
+-----------+-------+------+------+-------+
| 202112081 |       |      |   20 | CS    |
| 202112082 |       |      |   19 | CS    |
| 202112083 |       |      |   18 | MA    |
| 202112085 |       |      |   19 | IS    |
+-----------+-------+------+------+-------+
4 rows in set (0.00 sec)

After entering the new pod, the data still exists and the persistence is successful. It's safe.

The MySQL service is restored and the data is intact.

Topics: Linux Docker Kubernetes Container Cloud Native