GitLab CI/CD Pipeline – Push Image to GRC and Cannary Deploy to GKE | GCP Infrastructure overview

When the infrastructure is done and all resources are in place we need to create a CI/CD pipeline to allow our code to be deployed. We choose GitLab’s CI/CD pipeline to deploy new code to production.

We will test, build, push image to Google Container Registry and deploy a simple Go app to Google Kubernetes Engine. The app can be found in repo but feel free to use directly your docker image, it’ s straight forward 🙂

Some remarks on .gitlab-ci.yml file, skipping the basics:

Stages

We manage deploys in 4 stages, 4 + 1 because one is a bit fatty

stages:
  - test
  - build
  - cannary
  - deploy

Test

test:
  stage: test
  image: node:latest
  cache:
    paths:
      - node_modules/
  script:
    - npm install
    - npm test

The stage pulls the latest node image and perform npm install and npm test and it keeps node_modules/ cached for further runs

Build

build-push:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  before_script:
    # Login to Google Cloud Registry
    - base64 -d $GCP_SA_KEY | docker login -u _json_key --password-stdin https://gcr.io

  script:
    - docker build -t gcr.io/project-292212/api:$CI_COMMIT_TITLE -t gcr.io/project-292212/api:latest .
    - docker push gcr.io/project-292212/api:$CI_COMMIT_TITLE
    - docker push gcr.io/project-292212/api:latest
  # only:
  #   - tags

Before building docker image we need to authenticate in GCR to have where to push new images. We use a service-account.json key for authentication step, which need a cluster admin and storage write permissions

$GCP_SA_KEY is the serice-account.json encoded in base64 variable defined in GitLab -> Settings -> CI/CD -> Variables. To encode the json in base64

base64 project-292212-69fc45252989.json > project-292212-69fc45252989-base64.json

$CI_COMMIT_TITLE is a gitlab environment variable containing the commit title. A full list of predefined vars here

Optionally, we can define when this stage will be triggered.

Cannary

deploy:
  stage: cannary
  image: google/cloud-sdk
  services:
  - docker:dind
  before_script:
    # Login to Google Cloud 
    - gcloud auth activate-service-account project@project-292212.iam.gserviceaccount.com --key-file=$GOOGLE_CLOUD_ACCOUNT
    - gcloud config set project project-292212
    - gcloud config set compute/zone europe-west4
    - gcloud container clusters get-credentials project-292212-gke-cluster-2ab5 --zone europe-west4 --project project-292212
    - kubectl config current-context
  script:
  # Make gcloud available
  - source /root/.bashrc
  - sed -e "s|GIT_VERSION|$CI_COMMIT_TITLE|g" cannary.yml | kubectl apply -f -

Auto deploy to a cannary stage in Google Kubernetes Engine. In deployment.yaml is a simple Kubernetes Deploy which takes the latest app version from Google Cloud Registry and apply to GKE. For this, we need the google/cloud-sdk image, which includes kubectl (thanks to this StackOverflow answer), and we need to configure cluster access to kubectl in before_script:job. We added a simple variable to our cannary.yml Deploy and apply it through kubectl

Deploy

Last step is the same but we added when: manual to manually promote deploy to production, if the cannary looks good.

Some useful resources:

Connection between GitLab and Kubernetes Cluster can be made through GitLab’s Cluster Management

Configure Gitlab CI with Google Container Registry article is the inspiration for the build-push stage

GitLab repo: https://gitlab.com/aaadipop/gcr-gke-deploy-pipeline