Greg Chan

Continuous Deploy with GCP

It's amazing how much you take production continuous deploy for granted. I've been working on a side project with a friend and the beginnings of this project were quite hacked together. Our first prototype was a server deployed on GCP using screen. Obviously, this wouldn't be sustainable as we want to move quickly deploying new features.

I decided it would be necessary to have continuous deploy built in, and what better of a time to learn how GCP works, when your friend works at Google. So, let's get to it!

Prerequisites

  • Docker - We'll be using Container-Optimized Compute Engines from GCP and as such, our application will need to run in a docker container on the box.
  • HTTP/HTTPS - We'll assume that your application server is running an HTTP/HTTPS server, and as such will not mention any firewall rules or VPC configurations needed for applications using ports other than 80 and 443.
  • GitHub - We'll assume you use GitHub as your version control, and that you have administrator privledges to the application repository.

Overview

  1. Configuring the Google Cloud SDK
  2. Configure Cloud Build Container Registry
  3. Create a Compute Engine Instance Template
  4. Create a Compute Engine Instance Group
  5. Configuring GitHub with Cloud Build

Configuring the Google Cloud SDK

Assuming you have the Google Cloud SDK installed, we'll go over how to configure it.

gcloud auth login

This will take you to a browser prompt where you'll authorize the gcloud cli to take action on your behalf.

Next, we need to tell glcoud which project to interact with:

gcloud config set project <my-project-name>

Configure Cloud Build Container Registry

The first step involves creating a container registry in GCP. For those not famililar with container registries, this is where your docker images will be pushed and pulled from.

I created a cloudbuild.yaml file in the root of my repository with the following:

steps:
 - name: gcr.io/cloud-builders/docker
   args: [ build, -t, gcr.io/$PROJECT_ID/<my-app-name>, . ]

images:
 - gcr.io/$PROJECT_ID/<my-app-name>

This specifies a series of steps that builds my docker image and pushes it to the specified container registry. Our continuous deploy system will use gcr.io/$PROJECT_ID/<my-app-name> to pull and run our docker image.

To create our build and the container registry, we'll submit our build to Cloud Build with our previously specified configuration.

gcloud builds submit --config cloudbuild.yaml

Create a Compute Engine Instance Template

Next, we need to create an Instance Template. An Instance Template is a blueprint that allow our Instance Groups to create identical instances running our application. This is useful for when we deploy a new version or need to scale the number of instances. We'll use the gcloud cli to create the template.

gcloud compute instance-templates create-with-container <my-instance-template-name> \
  --machine-type f1-micro \
  --boot-disk-size 10GB \
  --container-image <my-image> \
  --tags http-server,https-server

The --machine-type flag indicates the size of the instance, how much CPU and memory for the instnace. The --boot-disk-size flag indicates the size of the virtual machines disk. The --container-image flag describes the container image stored in ECR. This will be of the form gcr.io/$PROJECT_ID/<my-app-name>. Finally, the --tags flags that we've specified ensure that the firewall rules allow for traffic on port 80 and 443.

Create a Compute Engine Instance Group

After creating the Instance Template, we need to create a compute instance that will run our container. We can create an Instance Group to deploy our application which will be useful for scaling our application if needed.

gcloud compute instance-groups managed create <my-instance-group-name> \
  --template <my-instance-template-name>
  --size 3
  --zone us-west1-b

This will create an Instance Group that uses the <my-instance-template-name> (specified in the previous step) as the blueprint. The --size flag specifies the initial number of instances in the group. The --zone flag specifies which zone the virtual machine will be deployed in.

At this point, your application should be running on GCP! You can test it by navigating to the Console -> Compute Engine -> External IP of an Instance in the Instance Group. It may take a few mintues for the application to fully deploy, so don't give up if it's not working immediately. For me, it took about 2-5 minutes.

Configuring GitHub with Cloud Build

Great! Our application is deployed, but what if I make changes? This last step will ensure that any changes pushed to the master branch of your repo will trigger a build and deploy your application.

We'll use Cloud Build triggers to listen for push events to the master branch of our GitHub repo. To create a trigger, navigate to Console -> Cloud Build -> Triggers and click Add trigger.

In the Authenticate page, choose GitHub as the repository hosting option and consent to Google connecting to your repo. If you don't consent, you won't be able to move forward.

In the Select repository section, choose your repository. You'll need administrator privledges to select the repository.

In the Trigger settings section, name your trigger and choose a branch you'd like to run this trigger on. I set my branch regex to master, so builds will run on my master branch only. Finally, in the Cloud Build configuration file section I chose Cloud Build configration file. Click create trigger.

Add the following to your cloudbuild.yaml so the whole file looks something like this:

steps:
 - name: gcr.io/cloud-builders/docker
   args: [ build, -t, gcr.io/$PROJECT_ID/<my-app-name>, . ]
 - name: gcr.io/cloud-builders/gcloud
   args: [ compute, instance-groups, managed, rolling-action, restart, <my-instance-group-name>, --zone=<zone> ]

images:
 - gcr.io/$PROJECT_ID/<my-app-name>

Configuring IAM

Finally, we need to give CloudBuild permission to deploy your changes. Navigate to Console -> IAM. Select the member <my-project-number>@cloudbuild.gserviceaccount.com and add the roles Compute Instance Admin (beta), Cloud Build Service Account, and Service Account User roles to the member.

That's it! Pushing to the master branch of repository should now trigger redeploys of your application!

References

Posted on August 12, 2019