Deploy to Google Kubernetes Engine using GitHub Actions

In this tutorial you’ll learn how to deploy containers to Google Kubernetes Engine (GKE) using GitHub Actions.

This is part three of the hands-on series: Deploy to any cloud:

Before directly jumping to the tutorial in GitHub read this post to get some background information.

Prerequisites

I assume that you already have a GitHub and Google Cloud account. If not, just register and create one.

Deployment of Google Cloud resources

The complete deployment happens in the script setup-gke.sh that you execute in the cloud shell. The script creates a GKR cluster with one node. For testing purposes this is enough:

$ gcloud container clusters create $GKE_CLUSTER --num-nodes=1

The script also creates an artifact repository for docker containers and a service account to perform the deployments.

In Kubernetes there is the concept of pods that contain the containers and are deployed using deployments in a YAML file – in this case Deployment.yaml. The deployment defines the container and binds it to an image:

spec:
  containers:
  - name: $GKE_APP_NAME
    image: $GKE_REGION-docker.pkg.dev/$GKE_PROJECT/$GKE_PROJECT/$GKE_APP_NAME:$GITHUB_SHA
    ports:
    - containerPort: 80
    env:
      - name: PORT
        value: "80"

I use environment variables in the file end replace them with envsubst before passing them to the kubectl apply command:

$ envsubst < Deployment.yml | kubectl apply -f -

A service exposes the pods – in this case to the internet. The service is deployed the same way using the file Service.yml:

spec:
  type: LoadBalancer
  selector:
    app: $GKE_APP_NAME
  ports:
  - port: 80
    targetPort: 80

The deployment of the service takes some time. You might have to execute the following command multiple times until you finally can see the external IP. If the IP is <pending> just wait some seconds and execute the command again:

$ kubectl get service
Getting the external IP of the Kubernetes service

If you have the IP you can test the deployment in a browser. You should see the Tailwind Traders website.

The setup should be straight forward and the only manual step is getting the external IP. At the and of the script you see the credentials for the service account. Copy them and create a new secret in GitHub named GKE_SA_KEY. The credentials are in a local file key.json and you can get them at any time using:

$ cat key.json | base64

If you’re done with testing you can tear doen all the resources executing the destroy-gke.sh script.

The deployment workflow

The deployment in the GitHub Actions workflow is straight forward. The authentication and setup of the gcloud CLI happens in the setup-gcloud action:

- uses: google-github-actions/setup-gcloud@v0.2.0
  with:
    service_account_key: ${{ secrets.GKE_SA_KEY }}
    project_id: ${{ secrets.GKE_PROJECT }}
    export_default_credentials: true

We can use gcloud to authenticate to the docker registry to build and push the image:

gcloud auth configure-docker $GKE_REGION-docker.pkg.dev --quiet

To deploy the new image to GKE we authenticate using the get-gke-credentials action:

- uses: google-github-actions/get-gke-credentials@v0.2.1
  with:
    cluster_name: ${{ env.GKE_CLUSTER }}
    location: ${{ env.GKE_ZONE }}
    credentials: ${{ secrets.GKE_SA_KEY }}

Then we just replace the variables in the deployment files and pass them to kubectl apply:

$ envsubst < Service.yml | kubectl apply -f -
$ envsubst < Deployment.yml | kubectl apply -f -

Summary

That’s it. Now you can head over to GitHub and follow the step-by-step guide. And you should have a working demo in less than 10 minutes.

Note that deployments to Kubernetes are very complex. You can do things like canary releases or blue/green deployments. A good starting point is the documentation.

One thought on “Deploy to Google Kubernetes Engine using GitHub Actions

  1. Awesome writeup, thanks for taking the time to put this together!

    PS. If you haven’t already, you should check out Helm, might make managing variable substitutions a bit easier compared to envsubst 🙂

Leave a comment