Using Persistent Disks with WordPress and MySQL

This tutorial shows you how to set up a single-replica WordPress deployment and a single-replica MySQL database on your cluster. Both of these applications use persistent disks for storage. Persistent disks preserve data when instances of your applications get restarted or when your application’s containers are rescheduled onto a different node in your container cluster.

Background

WordPress is a blogging tool which uses MySQL as its database to store the blog articles and the local filesystem to store assets, such as pictures in a blog post, or extensions. This tutorial uses the official MySQL and WordPress container images from Docker Hub.

In general, a container’s root filesystem is not suitable to store persistent data. The containers you run on Container Engine are typically disposable entities, and the cluster manager may delete, evict, or reschedule any containers that become unavailable due to node failure or other causes. In such an occurrence, all data saved to a container’s root filesystem is lost.

Using persistent disks lets you store data for WordPress and MySQL applications outside the containers themselves. This way, even if the containers are deleted, their data persists.

For this tutorial, you create two Deployments: one for MySQL and one for WordPress. Both Deployments run only a single replica of each Pod.

Next, you create two Services: one for the WordPress container to communicate with the MySQL container, another one to expose WordPress Deployment to the internet on an external IP address with a load balancer.

Before you begin

Take the following steps to enable the Google Container Engine API:
  1. Visit the Container Engine page in the Google Cloud Platform Console.
  2. Create or select a project.
  3. Wait for the API and related services to be enabled. This can take several minutes
  4. Enable billing for your project.

    Enable billing

Set defaults for the gcloud command-line tool

To save time typing your project ID and Compute Engine zone options in the gcloud command-line tool, you can set default configuration values by running the following commands:
gcloud config set project PROJECT_ID
gcloud config set compute/zone us-central1-b

Download application manifests

For this tutorial, you create the necessary Kubernetes objects using manifest files in YAML format.

This approach is called declarative object management. These files are provided for you; to download the manifest files from the GitHub repository, run the following commands in a terminal:

git clone https://github.com/GoogleCloudPlatform/container-engine-samples
cd container-engine-samples/wordpress-persistent-disks

(If you do not have git installed in your system, you can download them from the links below.)

Step 1: Create a Container Engine cluster

The first step is to create a Container Engine cluster to host your WordPress and MySQL application containers. The following command creates a cluster with 3 nodes:

gcloud container clusters create hello --num-nodes=3

Step 2: Create your persistent disks

This tutorial makes use of persistent disks, allowing the application to preserve its storage when Pods get restarted or rescheduled onto other nodes for reasons like application crashes or node restarts.

For this example, you need two disks: one for the MySQL database to store its data, and one for WordPress to store its assets on the filesystem. To create these persistent disks, run:

gcloud compute disks create --size 200GB mysql-disk
gcloud compute disks create --size 200GB wordpress-disk

Step 3: Set up MySQL

Deploy MySQL

First step to deploy MySQL is to create a Kubernetes Secret to store the password for the database. To create a Secret named mysql, run the following command (and replace YOUR_PASSWORD with a passphrase of your choice):

kubectl create secret generic mysql --from-literal=password=YOUR_PASSWORD

Then you will use the mysql.yaml manifest file to deploy the single instance MySQL application running on port 3306. The mysql.yaml file looks like:

apiVersion: extensions/v1beta1
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
          gcePersistentDisk:
            pdName: mysql-disk
            fsType: ext4

This manifest describes a Deployment with a single instance MySQL Pod which will have the MYSQL_ROOT_PASSWORD environment variable whose value is set from the secret created. The MySQL container will use the persistent disk mounted at /var/lib/mysql.

To deploy this manifest file, run:

kubectl create -f mysql.yaml

Check to see if the Pod is running. It might take a few minutes for the Pod to transition to Running status as attaching the persistent disk to the compute node takes a while:

$ kubectl get pod -l app=mysql
NAME                 READY     STATUS    RESTARTS   AGE
mysql-259040-flmqh   1/1       Running   0          3m

Create MySQL service

The next step is to create a Service to expose the MySQL container and make it accessible from the wordpress container you are going to create.

You will use the Service manifest defined in mysql-service.yaml, which looks like:

apiVersion: v1
kind: Service
metadata:
  name: mysql
  labels:
    app: mysql
spec:
  type: ClusterIP
  ports:
    - port: 3306
  selector:
    app: mysql

This manifest describes a Service that creates a Cluster IP on port 3306 for the Pods matching the label app: mysql. The mysql container deployed in the previous step has this label. In this case, you use type:ClusterIP for the Service, as this value makes the Service reachable only from within the cluster.

The Cluster IP created routes traffic to the MySQL container from all compute nodes in the cluster and is not accessible to clients outside the cluster. Once the Service is created, the wordpress container can use the DNS name of the mysql container to communicate, even though they are not in the same compute node.

To deploy this manifest file, run:

kubectl create -f mysql-service.yaml

Check to see if the Service is created:

$ kubectl get service mysql
NAME      CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
mysql     10.51.240.175           3306/TCP   4m

You have completed setting up the database for your new WordPress blog!

Step 4: Set up WordPress

Deploy WordPress

The next step is to deploy your WordPress container to the container cluster. You will use the wordpress.yaml manifest file to deploy a single instance WordPress container.

The wordpress.yaml looks like:

apiVersion: extensions/v1beta1
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
          gcePersistentDisk:
            pdName: wordpress-disk
            fsType: ext4

This manifest describes a Deployment with a single instance WordPress Pod. This container reads the WORDPRESS_DB_PASSWORD environment variable from the database password Secret you created earlier.

This manifest also configures the WordPress container to communicate MySQL with the host address mysql:3306. This value is set on the WORDPRESS_DB_HOST environment variable. We can refer to the database as mysql, because of Kubernetes DNS allows Pods to communicate a Service by its name.

To deploy this manifest file, run:

kubectl create -f wordpress.yaml

Check to see if the Pod is running. It might take a few minutes for the Pod to transition to Running status as attaching the persistent disk to the compute node takes a while:

$ kubectl get pod -l app=wordpress
NAME                     READY     STATUS    RESTARTS   AGE
wordpress-387015-02xxb   1/1       Running   0          9h

Expose WordPress Service

In the previous step, you have deployed a WordPress container which is not currently accessible from outside your cluster as it does not have an external IP address.

To expose your WordPress application to traffic from the internet using a load balancer (subject to billing), you need a Service with type:LoadBalancer.

The wordpress-service.yaml file contains the manifest for this Service looks like:

apiVersion: v1
kind: Service
metadata:
  labels:
    app: wordpress
  name: wordpress
spec:
  type: LoadBalancer
  ports:
    - port: 80
      targetPort: 80
      protocol: TCP
  selector:
    app: wordpress

To deploy this manifest file, run:

kubectl create -f wordpress-service.yaml

Deploying this manifest will create a load balancer, which may take a few minutes. Run the following command to find out the external IP address of your blog:

$ kubectl get svc -l app=wordpress
NAME        CLUSTER-IP      EXTERNAL-IP    PORT(S)        AGE
wordpress   10.51.243.233   203.0.113.3    80:32418/TCP   1m

In the output above, the EXTERNAL-IP column will show the public IP address created for your blog. Save this IP address for the next step.

You now have completed deploying and exposing your WordPress blog!

Step 5: 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 as follows:

Screenshot of 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. Everything is working as expected.

Step 6: (Optional) Test data persistence on failure

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

$ kubectl get pods -o=wide
NAME                   ...  IP          NODE
mysql-259040-flmqh     ...  10.48.2.8   gke-hello-default-pool-fe4fe9af-wcb4
wordpress-387483-02xxb ...  10.48.2.7   gke-hello-default-pool-fe4fe9af-wcb4

Now, delete the mysql pod by running:

kubectl delete pod -l app=mysql

Once the mysql Pod is deleted, the Deployment controller will notice that the Pod is missing and will recreate the Pod. It is likely that the new mysql Pod will start on a different node than it was running before.

Run the following command again to observe that the mysql Pod is scheduled onto a different compute instance than before (if not, you can delete the Pod again until it is running somewhere different).

$ kubectl get pods -o=wide
NAME                   ...  IP          NODE
mysql-259040-flmqh     ...  10.48.2.8   gke-hello-default-pool-fe4fe9af-vg56
wordpress-387483-02xxb ...  10.48.2.7   gke-hello-default-pool-fe4fe9af-wcb4

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.

Step 7: Updating application images

It’s important to keep deployed software up to date. For example, vulnerabilities may be reported in WordPress that require an update. To update the WordPress container image, find the newest image version on Docker Hub and update the image: value in the wordpress.yaml file. To apply the update, run:

kubectl apply -f wordpress.yaml

Step 8: Cleanup

After completing this tutorial, follow these steps to remove the following resources to prevent unwanted charges incurring on your account:

  1. Delete the Service: This step will deallocate the Cloud Load Balancer created for your wordpress Service:

    kubectl delete service wordpress
    
  2. Wait for the Load Balancer provisioned for the wordpress Service to be deleted: The load balancer is deleted asynchronously in the background when you run kubectl delete. Wait until the load balancer is deleted by watching the output of the following command:

    gcloud compute forwarding-rules list
    
  3. Delete the container cluster: This step will delete the resources that make up the container cluster, such as the compute instances, disks and network resources.

    gcloud container clusters delete hello-cluster
    
  4. Delete the persistent disks: This step deletes the disks created for the WordPress and MySQL applications.

    gcloud compute disks delete mysql-disk wordpress-disk
    

Send feedback about...

Container Engine Documentation