Continuous deployment to Google Kubernetes Engine (GKE) with Cloud Build
Overview
In this lab, you'll learn to set up a continuous delivery pipeline for GKE with Cloud Build. This lab highlights how to trigger Cloud Build jobs for different git events as well as a simple pattern for automated canary releases in GKE.
You'll complete the following steps:
- Create the GKE Application
- Automate deployments for git branches
- Automate deployments for git main branch
- Automating deployments for git tags
Preparing your environment
-
Create environment variables to use throughout this tutorial:
export PROJECT_ID=$(gcloud config get-value project) export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)') export ZONE=us-central1-b export CLUSTER=gke-progression-cluster export APP_NAME=myapp
-
Enable the following APIs:
- Resource Manager
- GKE
- Cloud Source Repositories
- Cloud Build
- Container Registry
gcloud services enable \ cloudresourcemanager.googleapis.com \ container.googleapis.com \ sourcerepo.googleapis.com \ cloudbuild.googleapis.com \ containerregistry.googleapis.com \ --async
-
Clone the sample source and switch to the lab directory:
git clone https://github.com/GoogleCloudPlatform/software-delivery-workshop.git gke-progression cd gke-progression/labs/gke-progression rm -rf ../../.git
-
Replace placeholder values in the sample repository with your
PROJECT_ID
:This command creates instances of the various config files unique to your current environment.
for template in $(find . -name '*.tmpl'); do envsubst '${PROJECT_ID} ${ZONE} ${CLUSTER} ${APP_NAME}' < ${template} > ${template%.*}; done
-
Store the code from the sample repository in CSR:
gcloud source repos create gke-progression git init git config credential.helper gcloud.sh git remote add gcp https://source.developers.google.com/p/$PROJECT_ID/r/gke-progression git branch -m main git add . && git commit -m "initial commit" git push gcp main
-
Create your GKE cluster.
gcloud container clusters create ${CLUSTER} \ --project=${PROJECT_ID} \ --zone=${ZONE}
-
Give Cloud Build rights to your cluster.
Cloud Build will be deploying the application to your GKE Cluster and will need rights to do so.
gcloud projects add-iam-policy-binding ${PROJECT_ID} \ --member=serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com \ --role=roles/container.developer
Your environment is ready!
Creating your GKE Application
In this section, you build and deploy the initial production application that you use throughout this tutorial.
-
Build the application with Cloud Build:
gcloud builds submit --tag gcr.io/$PROJECT_ID/$APP_NAME:1.0.0 src/
-
Manually deploy to Canary and Production environments:
Create the production and canary deployments and services using the
kubectl apply
commands.kubectl create ns production kubectl apply -f k8s/deployments/prod -n production kubectl apply -f k8s/deployments/canary -n production kubectl apply -f k8s/services -n production
The service deployed here will route traffic to both the canary and prod deployments.
-
Review number of running pods
Confirm that you have four Pods running for the frontend, including three for production traffic and one for canary releases. That means that changes to your canary release will only affect 1 out of 4 (25%) of users.
kubectl get pods -n production -l app=$APP_NAME -l role=frontend
-
Retrieve the external IP address for the production services.
Note: It can take several minutes before you see the load balancer external IP address.
kubectl get service $APP_NAME -n production
Once the load balancer returns the IP address continue to the next step
-
Store the external IP for later use.
export PRODUCTION_IP=$(kubectl get -o jsonpath="{.status.loadBalancer.ingress[0].ip}" --namespace=production services $APP_NAME)
-
Review the application
Check the version output of the service. It should read Hello World v1.0
curl http://$PRODUCTION_IP
Congratulations! You deployed the sample app! Next, you'll set up a pipeline for continuously and deploying your changes.
Automating deployments for git branches
In this section you will set up a trigger that will execute a Cloudbuild job on commit of any branch other than main
. The Cloud Build file used here will automatically create a namespace and deployment for any existing or new branches, allowing developers to preview their code before integration with the main branch.
-
Set up the trigger:
The key component of this trigger is the use of the
branchName
parameter to matchmain
and theinvertRegex
parameter which is set to true and alters thebranchName
pattern to match anything that is notmain
. For your reference you can find the following lines inbuild/branch-trigger.json
."branchName": "main", "invertRegex": true
Additionally the last few lines of the Cloud Build file used with this trigger create a namespace named after the branch that triggered the job, then deploys the application and service within the new namespace. For your reference you can find the following lines in
build/branch-cloudbuild.yaml
kubectl get ns ${BRANCH_NAME} || kubectl create ns ${BRANCH_NAME} kubectl apply --namespace ${BRANCH_NAME} --recursive -f k8s/deployments/dev kubectl apply --namespace ${BRANCH_NAME} --recursive -f k8s/services
Now that you understand the mechanisms in use, create the trigger with the gcloud command below.
gcloud beta builds triggers create cloud-source-repositories \ --trigger-config build/branch-trigger.json
-
To review the trigger, go to the Cloud Build Triggers page in the Console.
-
Create a new branch:
git checkout -b new-feature-1
-
Modify the code to indicate v1.1
Edit
src/app.py
and change the response from 1.0 to 1.1@app.route('/') def hello_world(): return 'Hello World v1.1'
-
Commit the change and push to the remote repository:
git add . && git commit -m "updated" && git push gcp new-feature-1
-
To review the build in progress, go to the Cloud Build Builds page in the Console.
Once the build completes continue to the next step
-
Retrieve the external IP address for the newly deployed branch service.
Note: It can take several minutes before you see the load balancer external IP address.
kubectl get service $APP_NAME -n new-feature-1
Once the load balancer returns the IP address continue to the next step
-
Store the external IP for later use.
export BRANCH_IP=$(kubectl get -o jsonpath="{.status.loadBalancer.ingress[0].ip}" --namespace=new-feature-1 services $APP_NAME)
-
Review the application
Check the version output of the service. It should read Hello World v1.0
curl http://$BRANCH_IP
Automate deployments for git main branch
Before code is released to production, it's common to release code to a small subset of live traffic before migrating all traffic to the new code base.
In this section, you implement a trigger that is activated when code is committed to the main branch. The trigger deploys the canary deployment which receives 25% of all live traffic to the new revision.
-
Set up the trigger for the main branch:
gcloud beta builds triggers create cloud-source-repositories \ --trigger-config build/main-trigger.json
-
To review the new trigger, go to the Cloud Build Triggers page in the Console.
-
Merge the branch to the main line and push to the remote repository:
git checkout main git merge new-feature-1 git push gcp main
-
To review the build in progress, go to the Cloud Build Builds page in the Console.
Once the build has completed continue to the next step
-
Review multiple responses from the server
Run the following command and note that approximately 25% of the responses are showing the new response of Hello World v1.1
while true; do curl -w "\n" http://$PRODUCTION_IP; sleep 1; done
When you're ready to continue press
Ctrl+c
to exit out of the loop.
Automating deployments for git tags
After the canary deployment is validated with a small subset of traffic, you release the deployment to the remainder of the live traffic.
In this section, you set up a trigger that is activated when you create a tag in the repository. The trigger labels the image with the appropriate tag then deploys the updates to prod ensuring 100% of traffic is accessing the tagged image.
-
Set up the tag trigger:
gcloud beta builds triggers create cloud-source-repositories \ --trigger-config build/tag-trigger.json
-
To review the new trigger, go to the Cloud Build Triggers page in the Console.
-
Create a new tag and push to the remote repository:
git tag 1.1 git push gcp 1.1
-
To review the build in progress, go to the Cloud Build Builds page in the Console.
-
Review multiple responses from the server
Run the following command and note that 100% of the responses are showing the new response of Hello World v1.1
This may take a moment as the new pods are deployed and health checked within GKE
while true; do curl -w "\n" http://$PRODUCTION_IP; sleep 1; done
When you're ready to continue press
Ctrl+c
to exit out of the loop.Congratulations! You created CI/CD triggers in Cloud Build for branches and tags to deploy your apps to GKE.