Learn how you can persist data independently from the pod lifecycle with a hands on example application
In my previous guide Kubernetes Patterns: Stateless Applications we managed to set up a basic guestbook application. But what about applications which need to persist data independently of the pod lifecycle.
Thats where persistent volume storage and secrets come in. Persistent volumes allow you to persist data independently of the pod lifecycle, while secrets allow you to store sensitive data into a separate space from the pod configuration.
We will demonstrate these features by setting up a simple WordPress site inside our Kubernetes cluster.
Prerequisites
- Running Kubernetes cluster
- Internet connection
- Kubectl connection
If you don’t have a running Kubernetes cluster yet, get started with my guide All in One Cluster with kubeadm as we will base this guide on that setup.
Step 1 – Prepare Deployment Resources
We will prepare all Kubernetes Resource files needed for later deployment.
- local-volumes.yaml
- mysql-deployment.yaml
- wordpress-deployment.yaml
First create a directory where we will store all Kubernetes resources.
user@computer$ mkdir wordpress-example
user@computer$ cd wordpress-example
Then download all the example resource files for the WordPress example.
user@computer$ wget https://raw.githubusercontent.com/kubernetes/examples/master/mysql-wordpress-pd/local-volumes.yaml
user@computer$ wget https://raw.githubusercontent.com/kubernetes/examples/master/mysql-wordpress-pd/mysql-deployment.yaml
user@computer$ wget https://raw.githubusercontent.com/kubernetes/examples/master/mysql-wordpress-pd/wordpress-deployment.yaml
And that’s it. We are now ready to start with the deployment of the different Kubernetes resources.
Step 2 – Create Persistent Volumes
To store our data persistently we will have to prepare persistent volumes.
Create two persistent volumes by using the downloaded Kubernetes volumes resource file.
user@computer$ kubectl create -f local-volumes.yaml
local-volumes.yaml |
|
Now check that the persistent volumes exist by using the persistentvolumes
or pv
key.
user@computer$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
local-pv-1 20Gi RWO Retain Available 1m
local-pv-2 20Gi RWO Retain Available 1m
And that’s it, we got our persistent volumes ready to serve claimable space for our pods.
Step 3 – Deploy WordPress Application
Let’s setup our WordPress application to claim storage from our prepared volumes. To set up the database we will need to securely store a password. That’s where secrets come in handy.
Define a secret object with the name mysql-pass and the key password to store our mysql database credentials.
user@computer$ kubectl create secret generic mysql-pass –from-literal=password=<YOUR_PASSWORD>
Check if the secret has been successfully created.
user@computer$ kubectl get secrets
NAME TYPE DATA AGE
…
mysql-pass Opaque 1 52s
Let’s look a bit deeper into the secret by using the
describe
key. We can see our password key with the hidden credentials.user@computer$ kubectl describe secrets mysql-pass
Name: mysql-pass
Namespace: default
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
password: 8 bytes
Now create the mysql deployment, by using the mysql-deployment.yaml kubernetes resource. This creates following resources:
- Service named WordPress, routing traffic to mysql pod
- Persistent volume claim of 20GB
- MySQL database deployment
- Loading credentials from secret object into
MYSQL_ROOT_PASSWORD
variable. - Persistent volume claim mounting into container
- Loading credentials from secret object into
user@computer$ kubectl create -f mysql-deployment.yaml
mysql-deployment.yaml |
|
Now let’s check that our service, deployment and persistent volume claim have been created correctly by using their shorthand notations:
pods
=po
service
=svc
deployment
=deploy
persistentvolumeclaims
=pvc
user@computer$ kubectl get po,svc,deploy,pvc
NAME READY STATUS RESTARTS AGE
po/wordpress-mysql-7b4ffb6fb4-65vd9 1/1 Running 0 10m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3d
svc/wordpress-mysql ClusterIP None <none> 3306/TCP 10m
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
deploy/wordpress-mysql 1 1 1 1 10m
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
pvc/mysql-pv-claim Bound local-pv-1 20Gi RWO 10m
We can also verify the claim by looking at the persistent volumes.
user@computer$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
local-pv-1 20Gi RWO Retain Bound default/mysql-pv-claim 54m
local-pv-2 20Gi RWO Retain Available 54m
Last but not least, let’s deploy our WordPress application with the downloaded Kubernetes resource file. This will create:
- Service named WordPress, routing traffic to pod
- Persistent Volume Claim of 20GB
- Deployment named WordPress
- Loading credentials from secret key into
WORDPRESS_DB_PASSWORD
variable and the database hostname defined in theWORDPRESS_DB_HOST
variable - Mounting volume claim
- Loading credentials from secret key into
user@computer$ kubectl create –f wordpress-deployment.yaml
Although we are defining the service port of type LoadBalancer, this will in our case jump back to exposing the port on NodePort.
wordpress-deployment.yaml |
|
Again we check that our service, deployment and volume claim have been deployed without problems.
user@computer$ kubectl get po,svc,deploy,pvc
NAME READY STATUS RESTARTS AGE
po/wordpress-db8f78568-cgpbw 1/1 Running 0 4m
po/wordpress-mysql-7b4ffb6fb4-65vd9 1/1 Running 0 23m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3d
svc/wordpress LoadBalancer 10.98.84.33 <pending> 80:31097/TCP 4m
svc/wordpress-mysql ClusterIP None <none> 3306/TCP 23m
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
deploy/wordpress 1 1 1 1 4m
deploy/wordpress-mysql 1 1 1 1 23m
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
pvc/mysql-pv-claim Bound local-pv-1 20Gi RWO 23m
pvc/wp-pv-claim Bound local-pv-2 20Gi RWO 4m
As we are basing this guide on my All in One Cluster with kubeadm we can get the exposing nodePort on the WordPress service and combine it with our node IP in a web browser. This will show us a running WordPress installation.
We have now successfully set up a WordPress application by using a WordPress deployment and a mysql datastore deployment. Both save their data to persistent volume claims.
Step 4 – Testing Persistence Features
Now that we have our application set up, with persistent volumes and their mounted volume claims where to save data, let’s see how it holds up.
First, complete your WordPress setup. Afterwards login in to your new WordPress site and generate some test data by installing FakerPress plugin.
On your sidebar go to the FakerPress section and start generating some content. After that you should have many blog posts including images.
Now let’s scale down our WordPress and mysql database deployments to 0 replicas, which will wipe out all running pods.
user@computer$ kubectl scale –replicas 0 deploy wordpress
user@computer$ kubectl scale –replicas 0 deploy wordpress-mysql
If we again scale up, our deployments content will reappear thanks to our persistent volume claims.
user@computer$ kubectl scale –replicas 1 deploy wordpress
user@computer$ kubectl scale –replicas 1 deploy wordpress-mysql
We just successfully tested our deployments persistent volume capability.
Conclusion
We have managed to set up a small WordPress application with a MySQL data store. Both applications are able to persist their data independently of the pod lifecycle. We tested this by shutting down all pods of the application and then again starting them to find our data in their previous saved state. We also learned how to save sensitive data to secrets independently from pod resource specifications.