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
and443
. - GitHub - We'll assume you use GitHub as your version control, and that you have administrator privledges to the application repository.
Overview
- Configuring the Google Cloud SDK
- Configure Cloud Build Container Registry
- Create a Compute Engine Instance Template
- Create a Compute Engine Instance Group
- 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!