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.
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 Kubernetes 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 beginTake the following steps to enable the Google Kubernetes Engine API:
- Visit the Kubernetes Engine page in the Google Cloud Platform Console.
- Create or select a project.
- Wait for the API and related services to be enabled. This can take several minutes.
- Enable billing for your project.
Install the following command-line tools used in this tutorial:
gcloudis used to create and delete Kubernetes Engine clusters.
gcloudis included in the Google Cloud SDK.
kubectlis used to manage Kubernetes, the cluster orchestration system used by Kubernetes Engine. You can install
gcloud components install kubectl
Set defaults for the
To save time typing your project ID and Compute Engine zone
options in the
gcloud command-line tool
gcloudcommand-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.
git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples cd kubernetes-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 Kubernetes Engine cluster
The first step is to create a Kubernetes Engine cluster to host your
WordPress and MySQL application containers. The following command creates a
persistent-disk-tutorial with 3 nodes:
gcloud container clusters create persistent-disk-tutorial --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
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
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
MYSQL_ROOT_PASSWORD environment variable whose value is set from the
secret created. The MySQL container will use the persistent disk mounted at
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
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
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
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 <none> 3306/TCP 4m
You have completed setting up the database for your new WordPress blog!
Step 4: Set up 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.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
environment variable. We can refer to the database as
mysql, because of
Kubernetes DNS allows Pods to communicate a Service by
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.
wordpress-service.yaml file contains the manifest for this Service looks
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:
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, Kubernetes 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-persistent-disk-tutorial-default-pool-fe4fe9af-wcb4 wordpress-387483-02xxb ... 10.48.2.7 gke-persistent-disk-tutorial-default-pool-fe4fe9af-wcb4
Now, delete the
mysql pod by running:
kubectl delete pod -l app=mysql
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
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-persistent-disk-tutorial-default-pool-fe4fe9af-vg56 wordpress-387483-02xxb ... 10.48.2.7 gke-persistent-disk-tutorial-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
To avoid incurring charges to your Google Cloud Platform account for the resources used in this tutorial:
After completing this tutorial, follow these steps to remove the following resources to prevent unwanted charges incurring on your account:
Delete the Service: This step will deallocate the Cloud Load Balancer created for your
kubectl delete service wordpress
Wait for the Load Balancer provisioned for the
wordpressService 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
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 persistent-disk-tutorial
Delete the persistent disks: This step deletes the disks created for the WordPress and MySQL applications.
gcloud compute disks delete mysql-disk wordpress-disk