volumes persistent storage of k8s

Keywords: MySQL Kubernetes Docker vim

Data persistence of k8s

kubernetes storage volume:
We know that by default, the container's data is non persistent, and the data is lost after the container is destroyed, so docker provides a volume mechanism to store the data persistently. Similarly, k8s provides a more powerful volume mechanism and rich plug-ins to solve the problem of container data persistence and data sharing between containers.

volume:
We often say: containers and pods are transient.
The implication is that they may have a short life cycle and are frequently destroyed and created. When the container is destroyed, the data stored in the internal file system of the container will be cleared. In order to persist the data of the container, k8s volume can be used.
The life cycle of a Volume is independent of the container. The container in the Pod may be destroyed and rebuilt, but the Volume will be retained.

The volume types supported by k8s include emptydir, hostpath, persistentVolumeClaim, gcepersistent disk, awsexelasticblockstore, nfs, iscsi, gitRepo, secret, etc. for a complete list and detailed documents, please refer to http://docs.kubernetes.org.cn/429.html.

In this paper, we mainly practice the following volume types:

1. EmptyDir (temporary storage):
emptyDir is the most basic Volume type. As its name suggests, an emptyDir Volume is an empty directory on the Host. In other words, there is no specified directory or file on the Host, which is directly mapped from the pod to the Host. (similar to the way docker manager volume is mounted in docker)

Let's practice emptydir with the following example:

[root@master yaml]# vim emptydir.yaml
apiVersion: v1
kind: Pod
metadata:
  name: read-write
spec:
  containers:
  - name: write
    image: busybox
    volumeMounts:     #Define data persistence
    - mountPath: /write     #Define the mount directory, which is the directory inside the pod
      name: share-volume
    args:
    - /bin/sh
    - -c
    - echo "hello volumes" > /write/hello; sleep 3000;    

  - name: read     #Define a second container within the pod
    image: busybox
    volumeMounts:
    - mountPath: /read
      name: share-volume
    args:
    - /bin/sh
    - -c
    - cat /read/hello; sleep 30000;
  volumes:
  - name: share-volume
    emptyDir: {}       #Define a data persistence type empytdir

We simulate a pod running two containers, two containers share a volume, one is responsible for writing data, one is responsible for reading data.

//Run the pod and view:
[root@master yaml]# kubectl  apply -f  emptydir.yaml 
pod/read-write created
[root@master yaml]# kubectl  get pod -o wide
NAME         READY   STATUS    RESTARTS   AGE   IP           NODE     NOMINATED NODE   READINESS GATES
read-write   2/2     Running   0          14s   10.244.2.2   node02   <none>           <none>
//Let's look at the mount contents in two containers:
[root@master yaml]# kubectl  exec  -it read-write -c read cat /read/hello
hello volumes
[root@master yaml]# kubectl  exec  -it read-write -c write cat /write/hello
hello volumes

Parameter interpretation:
-c: for specifying a container, it is the abbreviation of -- container =, which can be viewed through -- help.

Because emptyDir is the directory in the Docker Host file system, its effect is equivalent to executing docker run -v /write and docker run -v /read. We're at node02
Check the detailed configuration information of the container through docker inspect ion. We find that both containers have the same directory mount ed:

    "Mounts": [
        {
            "Type": "bind",
            "Source": "/var/lib/kubelet/pods/756b4f4a-917a-414d-a7ee-523eecf05465/volumes/kubernetes.io~empty-dir/share-volume",
            "Destination": "/read",
            "Mode": "",
            "RW": true,
            "Propagation": "rprivate"
        },

        {
            "Type": "bind",
            "Source": "/var/lib/kubelet/pods/756b4f4a-917a-414d-a7ee-523eecf05465/volumes/kubernetes.io~empty-dir/share-volume",
            "Destination": "/write",
            "Mode": "",
            "RW": true,
            "Propagation": "rprivate"
        },

Here, "/ var / lib / kubelet / pods / 756b4f4a-917a-414d-a7ee-523eecf05465 / volumes / kubernetes. IO ~ empty dir / share volume" is the real path for emptydir to mount to dockerhost.
So we can enter this path to view:

[root@node02 ~]# cd /var/lib/kubelet/pods/756b4f4a-917a-414d-a7ee-523eecf05465/volumes/kubernetes.io~empty-dir/share-volume/
[root@node02 share-volume]# cat hello 
hello volumes

Summary emptydir:
Different containers in the same pod share the same persistent directory. When the pod node is deleted, the contents of the volume will also be deleted, but if only the container is destroyed and the pod is still there, the volume will not be affected. That is to say, the data persistence life cycle of emptydir is consistent with the pod used. Generally used as temporary storage, as well as the temporary storage directory of checkpoint, which is the intermediate process of long-time tasks, and multi container shared directory.

2,hostPath:

  • 1) mount the existing directory or file on the host to the container.
  • 2) there are not many scenarios for this persistence method, because the core of virtualization technology is to isolate the host, but this method increases the coupling between pod and nodes.
  • 3) generally, this method is used for data persistence of k8s cluster and docker.

For example, Kube API server and Kube controller manager are such applications.
We use the command "kubectl Edit - n Kube system pod Kube apserver master" to view the configuration of Kube apserver pod. The following is the relevant part of Volume:

volumeMounts:
- mountPath: /etc/ssl/certs
  name: ca-certs
  readOnly: true
- mountPath: /etc/pki
  name: etc-pki
  readOnly: true
- mountPath: /etc/kubernetes/pki
  name: k8s-certs
  readOnly: true
  volumes:
  - hostPath:
      path: /etc/ssl/certs
      type: DirectoryOrCreate
    name: ca-certs
  - hostPath:
      path: /etc/pki
      type: DirectoryOrCreate
    name: etc-pki
  - hostPath:
      path: /etc/kubernetes/pki
      type: DirectoryOrCreate
    name: k8s-certs

Here, three hostpath volumes are defined as k8s certs, CA certs and etc PKI, corresponding to the Host directory / etc/kubernetes/pki, / etc/ssl/certs and / etc/pki respectively.

If the Pod is destroyed, the directory corresponding to the hostPath will also be preserved. From this point of view, the persistence of hostPath is better than emptyDir. However, once the Host crashes, the hostPath cannot be accessed.

3,pv & pvc

  • Persistent volume (pv): a unified data persistence directory refers to a section of space on a storage system provided by the Cluster Administrator configuration. It is an abstraction of the underlying shared storage, which takes shared storage as a resource that can be applied for by users and realizes the "storage consumption" mechanism.
  • Persistent volume claim (PVC): a claim for pv persistent space, declaration. Specify the required minimum capacity requirements and access patterns, and then the user submits the list of persistent volume declarations to the kubernetes api server, which will find a matching persistent volume and bind it to the persistent volume declaration.

NFS PersistentVolume
Practice PV and PVC through NFS.

1) we deploy the nfs service on the master node:

[root@master ~]# yum -y install nfs-utils
[root@master ~]# mkdir /nfsdata
[root@master ~]# vim /etc/exports   #Writing an nfs configuration file
/nfsdata 172.16.1.0/24(rw,sync,no_root_squash)
[root@master ~]# systemctl enable rpcbind
[root@master ~]# systemctl start rpcbind
[root@master ~]# systemctl enable nfs-server
[root@master ~]# systemctl start nfs-server
[root@master ~]# showmount -e   #Check whether the mount is successful
Export list for master:
/nfsdata 172.16.1.0/24

2) create pv:

[root@master yaml]# vim nfs-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv
spec:
  capacity:  
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: nfs
  nfs:
    path: /nfsdata     #Specify nfs shared directory
    server: 172.16.1.30    #Specifies the ip address of the nfs server
//Run pv with the following command:
[root@master yaml]# kubectl apply -f  nfs-pv.yaml 
persistentvolume/nfs-pv created
Field explanation:
Capacity: Specifies the capacity of pv. At present, capacity only supports space setting. In the future, IOPS and throughput should also be specified.
Access modes: access modes, including the following modes:
                                              ReadWriteOnce: mount to a single node in a read-write manner, abbreviated as RWO in the command line.
                                              ReadOnlyMany: mount to multiple nodes in read-only mode, abbreviated as ROX in the command line.
                                              ReadWriteMany: mounts to multiple nodes in a read-write manner, abbreviated to RWX on the command line.
 Persistentvolumerreclaimpolicy: the recovery policy when pv space is released. There are several policies as follows:
                                          Recycle: clear the data in pv and recycle it automatically. (the automatic recycling policy is protected by the pvc protection mechanism. When the pvc is deleted, as long as the pvc is still in the data, it is still in the data)
                                              Retain: keep it still, and the administrator will recycle it manually.
                                              Delete: delete cloud storage resources, only supported by some cloud storage systems, if AWS, EBS, GCE PD, Azure Disk and Cinder.
Note: the recycling policy here refers to whether the stored source files are deleted after pv is deleted.
storageClassName: the basis of association between pv and pvc.
//Verify that pv is available:
[root@master yaml]# kubectl  get pv
NAME     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
nfs-pv   1Gi (Capacity is 1. GB)       RWO (Read and write)          Recycle   (Auto recycle)       Available(Available, make sure it is in this state.)           nfs(Be based on nfs To do it)                     Eighteen m(Time)

3) create a pvc:

[root@master yaml]# vim nfs-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-pvc
spec:
  accessModes:
    - ReadWriteOnce     #The access mode of pv and pvc must be consistent
  resources:             #Define the resources to request in the requests subfield under this field
    requests:
      storage: 1Gi
  storageClassName: nfs
Run this pvc: 
[root@master yaml]# kubectl apply -f  nfs-pvc.yaml 
persistentvolumeclaim/nfs-pvc created
//Verify pvc is available:
[root@master yaml]# kubectl  get pvc
NAME      STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
nfs-pvc   Bound   nfs-pv   1Gi        RWO            nfs            3m53s

[root@master yaml]# kubectl  get pv
NAME     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM             STORAGECLASS   REASON   AGE
nfs-pv   1Gi        RWO            Recycle          Bound    default/nfs-pvc   nfs                     29m

Make sure that the status of both pv and pvc is Bound, which means the binding is successful.

Use of pv space.

Next, we practice the pv usage of mysql:
1) create a mysql pod:

[root@master yaml]# vim mysql-pod.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: mysql
spec:
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mysql:5.7
        env:             #Define a variable to map the mysqlroot password in the container to the local
        - name: MYSQL_ROOT_PASSWORD
          value: 123.com     #Password is 123.com
        ports:
        - containerPort: 3306
        volumeMounts:         #Define data persistence
        - name: mysql-pv-storage
          mountPath: /var/lib/mysql   #This directory is the default mysql data persistence directory
      volumes:                     #The volumes field is an explanation above
      - name: mysql-pv-storage        #Note that the name should be the same as above
        persistentVolumeClaim:      #Specify pvc, note that the pvc declared below should be the same as the pvc name created before
          claimName: nfs-pvc       
---
apiVersion: v1                  #Create a service resource object
kind: Service
metadata:
  name: mysql
spec:
  type: NodePort
  ports:
  - port: 3306
    targetPort: 3306
    nodePort: 30000
  selector:
    app: mysql
Run pod with the following command:
[root@master yaml]# kubectl apply -f  mysql-pod.yaml 
deployment.extensions/mysql created
service/mysql created
//To see if the pod is working properly:
[root@master yaml]# kubectl  get pod -o wide mysql-68d65b9dd9-hf2bf 
NAME                     READY   STATUS    RESTARTS   AGE     IP           NODE     NOMINATED NODE   READINESS GATES
mysql-68d65b9dd9-hf2bf   1/1     Running   0          9m34s   10.244.1.3   node01   <none>           <none>

2) log in to mysql database to write data:

[root@master yaml]# kubectl  exec  -it mysql-68d65b9dd9-hf2bf  -- mysql -u root -p123.com
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>
mysql> create database volumes_db;   #Create Library
Query OK, 1 row affected (0.01 sec)

mysql> use volumes_db;      #Entering the library
Database changed
mysql> create table my_id(     #Create table
    -> id int primary key,
    -> name varchar(25)
    -> );
Query OK, 0 rows affected (0.04 sec)

mysql> insert into my_id values(1,'zhangsan');   #Write data to table
Query OK, 1 row affected (0.01 sec)

mysql> select * from my_id;    #View data
+----+----------+
| id | name     |
+----+----------+
|  1 | zhangsan |
+----+----------+
1 row in set (0.00 sec)

3) verification:
(1) delete the pod manually to verify whether the data in the database still exists

[root@master ~]# kubectl  delete pod mysql-68d65b9dd9-hf2bf 
pod "mysql-68d65b9dd9-hf2bf" deleted
[root@master ~]# kubectl  get pod -o wide
NAME                     READY   STATUS    RESTARTS   AGE    IP           NODE     NOMINATED NODE   READINESS GATES
mysql-68d65b9dd9-bf9v8   1/1     Running   0          26s    10.244.1.4   node01   <none>           <none>

After deleting the pod, kubernetes will generate a new pod. We log in to mysql to check
Whether the data will still exist.

[root@master ~]# kubectl  exec  -it mysql-68d65b9dd9-bf9v8 -- mysql -u root -p123.com
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> select * from volumes_db.my_id;  
+----+----------+
| id | name     |
+----+----------+
|  1 | zhangsan |
+----+----------+
1 row in set (0.01 sec)

You can see that the data will still exist.

2) simulate whether the node where the pod is running goes down and whether the data returns to normal in the newly generated pod.
From the information above, we know that the pod is running on node01, so we shut down the node01 host in the cluster.
##[root@node01 ~]# systemctl poweroff

After a period of time, kubernetes will migrate the pod to the node02 host in the cluster:

[root@master ~]# kubectl  get nodes   #Know that node01 node is down
NAME     STATUS     ROLES    AGE   VERSION
master   Ready      master   39d   v1.15.0
node01   NotReady   <none>   39d   v1.15.0
node02   Ready      <none>   39d   v1.15.0

[root@master ~]# kubectl  get pod -o wide
NAME                     READY   STATUS        RESTARTS   AGE   IP           NODE     NOMINATED NODE   READINESS GATES
mysql-68d65b9dd9-bf9v8   1/1     Terminating   0          15m   10.244.1.4   node01   <none>           <none>
mysql-68d65b9dd9-mvxdg   1/1     Running       0          83s   10.244.2.3   node02   <none>           <none>

You can see that the pod has been migrated to node02.

Finally, we log in to mysql to verify whether the data is recovered:

[root@master ~]# kubectl exec  -it mysql-68d65b9dd9-mvxdg  -- mysql -u root -p123.com
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> select * from volumes_db.my_id;
+----+----------+
| id | name     |
+----+----------+
|  1 | zhangsan |
+----+----------+
1 row in set (0.09 sec)

It can be seen that after pod migration, mysql service is running normally, and data is not lost...

pv and pvc realize the persistence of mysql data, separate the responsibilities of administrators and ordinary users, and are more suitable for the production environment.

Posted by bobvaz on Sat, 14 Dec 2019 08:44:59 -0800