Persistent Volumes

A PersistentVolume (PV) is a piece of storage in the cluster that has been manually provisioned by an administrator, or dynamically provisioned by Kubernetes using a StorageClass.

A PersistentVolumeClaim (PVC) is a request for storage by a user that can be fulfilled by a PV.

PersistentVolumes and PersistentVolumeClaims are independent from Pod lifecycles and preserve data through restarting, rescheduling, and even deleting Pods.

Using Persistent Disks with WordPress and MySQL

Create your PersistentVolumes and PersistentVolumeClaims

cat << EOF > mysql-volumeclaim.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: mysql-volumeclaim
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 200Gi
EOF

cat << EOF > wordpress-volumeclaim.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: wordpress-volumeclaim
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 200Gi
EOF
{
  kubectl apply -f mysql-volumeclaim.yaml
  kubectl apply -f wordpress-volumeclaim.yaml
}
persistentvolumeclaim/mysql-volumeclaim created
persistentvolumeclaim/wordpress-volumeclaim created
kubectl get pvc
NAME                    STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mysql-volumeclaim       Bound    pvc-4323fc71-dad4-11e9-9b6e-42010a800fef   200Gi      RWO            standard       26s
wordpress-volumeclaim   Bound    pvc-434ee838-dad4-11e9-9b6e-42010a800fef   200Gi      RWO            standard       26s

Set up MySQL

Deploy MySQL

kubectl create secret generic mysql --from-literal=password=YOUR_PASSWORD
secret/mysql created
cat << EOF > mysql.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
  labels:
    app: mysql
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
        - image: mysql:5.6
          name: mysql
          env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql
                  key: password
          ports:
            - containerPort: 3306
              name: mysql
          volumeMounts:
            - name: mysql-persistent-storage
              mountPath: /var/lib/mysql
      volumes:
        - name: mysql-persistent-storage
          persistentVolumeClaim:
            claimName: mysql-volumeclaim
EOF
kubectl create -f mysql.yaml
deployment.apps/mysql created
kubectl get pod -l app=mysql -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP          NODE                                               NOMINATED NODE   READINESS GATES
mysql-674dcfbd85-9k4k9   1/1     Running   0          41s   10.24.0.3   gke-admatic-cluster-1-default-pool-902caf66-2j0p   <none>           <none>

Create MySQL service

cat << EOF > mysql-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: mysql
  labels:
    app: mysql
spec:
  type: ClusterIP
  ports:
    - port: 3306
  selector:
    app: mysql
EOF
{
  kubectl create -f mysql-service.yaml
  kubectl get service mysql
}
service/mysql created
NAME    TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE
mysql   ClusterIP   10.28.4.219   <none>        3306/TCP   0s

Set up WordPress

Deploy WordPress

cat << EOF > wordpress.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wordpress
  labels:
    app: wordpress
spec:
  replicas: 1
  selector:
    matchLabels:
      app: wordpress
  template:
    metadata:
      labels:
        app: wordpress
    spec:
      containers:
        - image: wordpress
          name: wordpress
          env:
          - name: WORDPRESS_DB_HOST
            value: mysql:3306
          - name: WORDPRESS_DB_PASSWORD
            valueFrom:
              secretKeyRef:
                name: mysql
                key: password
          ports:
            - containerPort: 80
              name: wordpress
          volumeMounts:
            - name: wordpress-persistent-storage
              mountPath: /var/www/html
      volumes:
        - name: wordpress-persistent-storage
          persistentVolumeClaim:
            claimName: wordpress-volumeclaim
EOF

kubectl create -f wordpress.yaml
deployment.apps/wordpress created
kubectl get pod -l app=wordpress -o wide
NAME                         READY   STATUS    RESTARTS   AGE   IP          NODE                                               NOMINATED NODE   READINESS GATES
wordpress-77bc8b5f57-khbbv   1/1     Running   0          52s   10.24.0.4   gke-admatic-cluster-1-default-pool-902caf66-2j0p   <none>           <none>

Expose WordPress Service

cat << EOF > wordpress-service.yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    app: wordpress
  name: wordpress
spec:
  type: LoadBalancer
  ports:
    - port: 80
      targetPort: 80
      protocol: TCP
  selector:
    app: wordpress
EOF

kubectl create -f wordpress-service.yaml
service "wordpress" created
kubectl get svc -l app=wordpress
NAME        TYPE           CLUSTER-IP    EXTERNAL-IP      PORT(S)        AGE
wordpress   LoadBalancer   10.28.4.221   35.208.130.178   80:30864/TCP   87s

Visit your new WordPress blog

After finding out the IP address of your blog, point your browser to this IP address and you will see the WordPress installation screen

Once you complete the WordPress setup, point your browser to the IP address of the WordPress app again to visit your blog.

Test data persistence on failure

With PersistentVolumes, your data lives outside the application container. When your container becomes unavailable and gets rescheduled onto another compute instance by Kubernetes, Kubernetes Engine will make the PersistentVolume available on the instance that started running the Pod.

kubectl get pods -o=wide
NAME                         READY   STATUS    RESTARTS   AGE     IP          NODE                                               NOMINATED NODE   READINESS GATES
mysql-674dcfbd85-9k4k9       1/1     Running   0          9m19s   10.24.0.3   gke-admatic-cluster-1-default-pool-902caf66-2j0p   <none>           <none>
wordpress-77bc8b5f57-khbbv   1/1     Running   0          7m9s    10.24.0.4   gke-admatic-cluster-1-default-pool-902caf66-2j0p   <none>           <none>
kubectl delete pod -l app=mysql
pod "mysql-674dcfbd85-9k4k9" deleted
kubectl get pods -o=wide
NAME                         READY   STATUS    RESTARTS   AGE   IP          NODE                                               NOMINATED NODE   READINESS GATES
mysql-674dcfbd85-t6qp8       1/1     Running   0          5s    10.24.0.5   gke-admatic-cluster-1-default-pool-902caf66-2j0p   <none>           <none>
wordpress-77bc8b5f57-khbbv   1/1     Running   0          11m   10.24.0.4   gke-admatic-cluster-1-default-pool-902caf66-2j0p   <none>           <none>

Visit your blog again to see that the website is functioning properly and the data is persisted even though you deleted your Pod and the Pod is scheduled to another instance in your cluster.

kubectl exec mysql-674dcfbd85-t6qp8 -it -- bash
root@mysql-674dcfbd85-t6qp8:/#
mysql -u root -p
Enter password: YOUR_PASSWORD
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 3
Server version: 5.6.45 MySQL Community Server (GPL)

Copyright (c) 2000, 2019, 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  |
| #mysql50#lost+found |
| mysql               |
| performance_schema  |
| wordpress           |
+---------------------+
5 rows in set (0.00 sec)
USE wordpress;
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
SHOW TABLES;
+-----------------------+
| Tables_in_wordpress   |
+-----------------------+
| wp_commentmeta        |
| wp_comments           |
| wp_links              |
| wp_options            |
| wp_postmeta           |
| wp_posts              |
| wp_term_relationships |
| wp_term_taxonomy      |
| wp_termmeta           |
| wp_terms              |
| wp_usermeta           |
| wp_users              |
+-----------------------+
12 rows in set (0.00 sec)
SELECT ID,post_author,post_date,post_name from wp_posts;
+----+-------------+---------------------+----------------+
| ID | post_author | post_date           | post_name      |
+----+-------------+---------------------+----------------+
|  1 |           1 | 2019-09-19 12:01:22 | hello-world    |
|  2 |           1 | 2019-09-19 12:01:22 | sample-page    |
|  3 |           1 | 2019-09-19 12:01:22 | privacy-policy |
+----+-------------+---------------------+----------------+
3 rows in set (0.00 sec)

results matching ""

    No results matching ""