Introduction

My previous article described how to access a Google Container Registry from Kubernetes or Docker.

This short tutorial shows how you can configure Gitlab CI to authenticate and push docker images to a Google Container Registry.

Step 1 - Base64 encode your GCP Service Account key

When creating environment variables in Gitlab you can optionally mask them from the job logs (recommended for sensitive variables). However masked variables must meet a set of requirements in order to be considered valid. Base64 encode your Service Account key with the following command to meet those requirements.

base64 -w0 ~/key.json > ~/key-base64.json

(the -w0 option ensures the encoded output is formatted as a single line)

Step 2 - Create an environment variable in your Gitlab project

Navigate to Settings > CI/CD in your project (or Group) and expand the Variables section. Click on Add Variable.

Add Variable

Note: Variables created in a Group are inherited by and available to all projects in said group.

Name your Key (e.g. GCP_SA_KEY) and paste the contents of your base64 encoded Service Account key from the previous step into the Value field. Be sure to select ‘File’ as the variable Type. Check the Mask variable option (and the Protect variable option too if you require it). Click on Add variable to finish adding the variable to your project.

Variable Added

Step 3 - Configure .gitlab-ci.yml file to use the Service Account variable

To login to the GCR, first decode the Service Account variable (base64 -d) and pass it to --password-stdin in the docker login command

base64 -d $GCP_SA_KEY | docker login -u _json_key --password-stdin https://gcr.io

At the build stage, tag the image with the correct GCR name format (*.gcr.io/PROJECT_ID/IMAGE_PATH:TAG)

docker build --cache-from -t gcr.io/[PROJECT_ID]/[IMAGE_PATH]/[IMAGE_NAME]:[IMAGE_TAG] .

Example

docker build --cache-from -t gcr.io/arctic-goal-676703/colinwilson/test_image:1.0 .

Note: Don’t forget the period at the end, it’s easy to miss.

Push the image to the GCR

docker push gcr.io/[PROJECT_ID]/[IMAGE_PATH]/[IMAGE_NAME]:[IMAGE_TAG]

Example

docker push gcr.io/arctic-goal-676703/colinwilson/test_image:1.0

Here’s the final.gitlab-ci.yml configuration file. The example configuration below builds and pushes an image to both Google and Gitlab container registries.

image: docker:latest

services:
  - docker:dind

stages:
  - build

before_script:
  # Login to Google Container Registry
  - base64 -d $GCP_SA_KEY | docker login -u _json_key --password-stdin https://gcr.io
  # Login to Gitlab container Registry
  - echo $CI_JOB_TOKEN | docker login -u gitlab-ci-token $CI_REGISTRY --password-stdin

build:
  stage: build
  script:
    # Build and tag image for both GCR and Gitlab registries
    - docker build --cache-from -t gcr.io/$PROJECT_ID/$CI_PROJECT_NAMESPACE/$CI_PROJECT_NAME:latest -t $CI_REGISTRY_IMAGE:latest .
    # Push image to GCR
    - docker push gcr.io/$PROJECT_ID/$CI_PROJECT_NAMESPACE/$CI_PROJECT_NAME:latest
    # Push image to Gitlab registry
    - docker push $CI_REGISTRY_IMAGE:latest

Note: In the above example I’ve used additional variables (set in Gitlab) in place of hard coded values. e.g. $PROJECT_ID / $CI_PROJECT_NAMESPACE / $CI_PROJECT_NAME