How to install a Wordpress site on Google Cloud Run

Salvo Pappalardo
Devmy
10 min readJun 16, 2019

--

Last updated on: 20/06/2019

At Google Cloud Next 2019 was released (in Beta) a brand new service called Cloud Run.

Cloud Run allows running HTTP stateless containers in a completely managed, way so without worrying about provisioning of VMs, clusters, TLS (https) or app auto-scaling.
Also it charges you only the actual request execution time, therefore it is a fully serverless solution but unlike other established serverless solutions like App Engine and Cloud Functions (see a comparative) it allows to deploy applications written in any language because the final deployed service is a baked container image (that follows a specific runtime contract ).

How is billed time calculated?

The official pricing page shows how billing is calculated:

Contrary to most serverless products Cloud Run is able to handle multiple requests simultaneously (currently 80 as default): the billable time begins with the start of the first request and ends at the end of the last request (see image above).
You are paying only for CPU, memory and the traffic sent to the client from your application (egress traffic) during the working time of the container.
Outside that time-frame, your application is free of charge.
There is also a monthly “Free Tier” so you are billed only for usage past the free quota.

In Acadevmy we are very focused on serverless products and starting from this article we will try to show several deployment examples of famous web applications.

Let’s begin from one “quite” famous app: the classic Wordpress blog!

Summary

To get the job done, we will do the following steps:

  1. GCP environment setup
  2. Create a MySQL instance
  3. Create one database and a user for Wordpress
  4. Create a container image for Wordpress
  5. Build & Deploy of the container image on Cloud Run
  6. Wordpress installation

The complete repo is available on GitHub:

If you are already familiar with container deployments just clone the repo and launch only the commands about database creation and the build & deploy of the service.

GCP environment setup

To setup the GCP environment let’s follow the official guide:

The steps addressed in the guide are basically:

  • Sign in on GCP and select/create a new project.
  • Enable the Cloud Run API billing.
  • Install the gcloud CLI (beta components included).
  • Set the default region.
  • Optional: install Docker to build the image in your local machine.

PLEASE NOTE: currently it seems only region us-central1 is supported on Cloud Run so we set our default region and zone like the following:

gcloud config set compute/region us-central1
gcloud config set compute/zone us-central1-a

If everything has been properly configured, launching the command:

gcloud config list

We should get a result similar to the following:

[compute]
region = us-central1
zone = us-central1-a
[core]
account = salvo@acadevmy.it
disable_usage_reporting = True
project = cr-test-wordpress

Create a MySQL instance

Wordpress need a MySQL database to save the site’s data.
To create a second generation MySQL instance on GCP you can refer to the following article:

However the required commands should be the following:

# instance creation
#gcloud sql instances create [INSTANCE_NAME] --tier=[MACHINE_TYPE] -#-region=[REGION]
# create a minimal (micro) instance
gcloud sql instances create mysql --tier=db-f1-micro --region=us-central1

You have to wait a few minutes before the instance becomes available and you could get this error:

ERROR: (gcloud.sql.instances.create) Operation https://www.googleapis.com/sql/v1beta4/projects/cr-test-wordpress/operations/a0a22c4e-2222-1111-yyyy-xxxx is taking longer than expected. You can continue waiting for the operation by running `gcloud beta sql operations wait --project cr-test-wordpress a0a22c4e-2222-1111-yyyy-xxxx`

As described by the message, you have to launch the suggested command to monitor the operations progress.
At the end you should get the following:

NAME                                  TYPE    START                          END                            ERROR  STATUS
a0a22c4e-2222-1111-yyyy-xxxx CREATE 2019-06-12T09:05:11.593+00:00 2019-06-12T09:12:04.976+00:00 - DONE

Now you can set the database root password, typing:

# set the root password
gcloud sql users set-password root --host % --instance mysql --password [PASSWORD]

Great! Now let’s add a user and a schema for our Wordpress site.

Create one database and a user for Wordpress

To create database and user you need the following:

# create wordpress database
gcloud sql databases create wordpress --instance=mysql --charset=utf8 --collation=utf8_general_ci
# create wordpress user
gcloud sql users create wordpress \
--host=% --instance=mysql --password=[PASSWORD]

Changing [PASSWORD] as necessary.
After the instance/database/user creation you can check that everything was successful opening a Google Cloud Shell and typing:

gcloud sql connect mysql --user=wordpress --quiet

PLEASE NOTE: Users created using Cloud SQL have all privileges except FILE and SUPER.
If you need to change the privileges for a user, use themysql client with classical GRANT andREVOKE commands.

Create a container image for Wordpress

Now, let’s create a custom container image to deploy Wordpress on Cloud Run.
We start from the official image present on Docker Hub https://hub.docker.com/_/wordpress/.

We need to change it just a little in order to fulfill the following points:

  1. Respect the runtime contract about the listening port.
  2. Connect with our MySQL instance through Cloud Sql Proxy.
  3. Set the database access credentials for MySQL inside the Wordpress wp-config.php configuration file
  4. Force the use of HTTPS still inside wp-config.php

Let’s take a look at our Dockerfile:

To fulfill point 1, our application must have a web server listening on 0.0.0.0 port 8080 (the port contained in the PORT environment variable).
Therefore, among the available tags we choose a-apache tag like the 5.2.1-php7.3-apache .

The default Apache image listens on the port 80, so we make a search&replace through sed(Dockerfile line 6) to change 80 with PORT on ports.conf and 000-default.conf .

Now let’s connect with the MySQL, as indicated in the point 2:
To connect with a MySQL instance we need Cloud SQL Proxy (see also https://cloud.google.com/sql/docs/mysql/sql-proxy ).

Cloud SQL Proxy provides secure access to your Cloud SQL instances without having to whitelist IP addresses or configure SSL.

So, let’s install the Google binary (Dockerfile lines 10–13) and change the default image entrypoint to run cloud_sql_proxy(Dockerfile lines 16 and 18).

We have to do also:

At this stage cloud_sql_proxy should be authorized to connect with database, it just need the instance name as <project>:<region>:<instance-name>; We will use and environment variable called CLOUDSQL_INSTANCE
to accomplish this task.
Here our complete custom entrypoint:

We are almost there!
Now let’s see the points 3 e 4 regarding wp-config.

First, we set the credentials to access to the database through environment variables (we use PHP function getenv):

define( 'DB_NAME', getenv('DB_NAME')  );
/** MySQL database username */
define( 'DB_USER', getenv('DB_USER') );
/** MySQL database password */
define( 'DB_PASSWORD', getenv('DB_PASSWORD') );
/** MySQL hostname */
define( 'DB_HOST', getenv('DB_HOST') );

It would be correct to pass this sensitive data through secrets but they are not (currently) natively supported by Cloud Run: for the sake of simplicity we will just pass them as plain-text but for production deploy it is absolutely recommended to use one of the possible workarounds available here https://www.sethvargo.com/secrets-in-serverless/.

As last point, we need to force HTTPS for the WP admin area (and so for the WP installation) as explained at https://wordpress.org/support/article/administration-over-ssl/#using-a-reverse-proxy.

if (
isset($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
strpos($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') !== false
) {
define('FORCE_SSL_ADMIN', true);
$_SERVER['HTTPS'] = 'on';
}

Cloud Run give us HTTPS urls like *.run.app for our service (we can also use a custom domain following https://cloud.google.com/run/docs/mapping-custom-domains ).
Thanks to X-Forwarded-Proto header we can identify that our domain is under HTTPS and force the relative constant for Wordpress.

Hurrah! The Dockerfile is finally ready, so is time to build our final container image and deploy everything on Cloud Run.

Build & Deploy of the container image on Cloud Run

Thanks to Cloud Build we can have a fast build of our Dockerfile and simultaneously we can have it pushed on Cloud Registry.

From the directory containing the Dockerfile:

gcloud builds submit --tag gcr.io/[PROJECT-ID]/wp:v1

where [PROJECT-ID] is our GCP project id. We can get it using:

gcloud config get-value project

Immediately after starting the build command we will be asked if we wish to activate the API [cloudbuild.googleapis.com], we answer yes and then gcloud will start the build process:

at the end of the process it will push the image on the Container Registry with name:tag wp:v1 .

Now that our custom Wordpress container image is on Registry we can deploy it using the following command:

gcloud beta run deploy wp --image gcr.io/[PROJECT-ID]/wp:v1 \
--add-cloudsql-instances <instance-name> \
--update-env-vars DB_HOST='127.0.0.1',DB_NAME=<dbname>,DB_USER=<dbuser>,DB_PASSWORD=<dbpass>,CLOUDSQL_INSTANCE='<project.id>:<region>:<instance-name>'

Where:

  • --image shows the path of the Registry from which to retrieve the container image.
  • --add-cloudsql-instances <instance-name> as mentioned in this article, authorizes our service to access to the instance.
  • --update-env-vars set the environment vars (NAME=value) for our service.
    You can notice that the db host is 127.0.0.1 because we have installed and started cloud_sql_proxy inside our container image.

the final deploy command should be similar to:

gcloud beta run deploy wp --image gcr.io/cr-test-wordpress/wp:v1 \
--add-cloudsql-instances mysql \
--update-env-vars DB_HOST='127.0.0.1',DB_NAME=wordpress,DB_USER=wordpress,DB_PASSWORD=wordpress,CLOUDSQL_INSTANCE='cr-test-wordpress:us-central1:mysql'

We can run the deploy command now: we respond Y to both questions about activation of API [run.googleapis.com]and at unauthenticated invocations (we want our service publicly accessible).

If all the steps will be successful, you will get the https address of your Wordpress site!

Wordpress installation

Now we are ready to the Wordpress installation wizard:

at the end of installation we can start using our brand new blog.

Themes and plugins

To extend your blog with custom themes and plugins you should add their sources to Dockerfile mounting the paths recommended in the original docker image Wordpress:

Themes go in a subdirectory in /var/www/html/wp-content/themes/
Plugins go in a subdirectory in /var/www/html/wp-content/plugins/

Admin area would be available only in the warm state and in any case beyond the deployment of a new revision.
Conversely, once added to the container image they will be available even if the service should switch into the “cold” state, the state in which the service is “asleep” waiting to be “awakened” by a request that will download the image again and start a container that will serve the request.

Save your media files on the Cloud

Because we can’t rely on the local filesystem we must use one cloud storage for our media library files.

The most easy way to do that is use Google Cloud Storage with WP Offload Media Lite plugin.

Because we already are in GCP, we can use IAM Role option
Create and use a bucket to store media gallery files

From now on, our assets will be uploaded on Google Cloud Storage and we won’t use the local filesystem anymore.

Logs & Metrics

All service summary metrics will be available in the service control panel, in addition to the latency and CPU / Memory used.
There will be also a list of previous revisions (one for each new deployment) and a tab logs that show us the list of request received by the service, managed through the integrated product Stackdriver.

Conclusion

Despite being in beta, Google Cloud Run, is for sure a very interesting service: it overcomes brilliantly some of the limitations of actual serverless solutions on the market, making the hosting of stateless services absolutely cost-effective.

For any questions regarding this or anything I should add, correct or remove, feel free to comment, email or DM me on Twitter at @xantrix.
Thanks to Ahmet Alp Balkan for pointing out we must use a cloud storage to save assets files.

Follow Acadevmy — Software Factory & Learning on Medium, Twitter and Facebook to keep you updated about the world of Frontend and DevOps development.

--

--