How to create a review app using Gitlab CI

We need to set up our cluster by using the Gitlab UI interface. Gitlab allows us to install the helm tiller. We need Gitlab Runner to let Gitlab execute jobs and push back results to us.

On settings -> Variables, we need to set up some environment variables:

  • GCP_PROJECT
  • GCP_SERVICE_KEY
  • GKE_CLUSTER_NAME
  • GKE_CLUSTER_ZONE

GCP_SERVICE_KEY is the json from your service account. The other variables are pretty straightforward.

If you have any problem on installing tiller from Gitlab CI, for example a difference between the tiller server version and client version, you need to install the tiller from your command line. This happens sometimes.

Using Helm

The deployment will be made in two steps: build and deploy.

Build

At this phase, it gets the source code, build as a docker image and uploads it to the Gitlab Container Registry. The docker image is defined as a variable at the beginning of the yaml file:

variables:
  IMAGE: “${CI_REGISTRY_IMAGE}/web:${CI_COMMIT_REF_SLUG}-${CI_COMMIT_SHORT_SHA}”

CI_REGISTRY_IMAGE, CI_COMMIT_REF_SLUG are variables from the Gitlab CI. We also need to pass the commit short sha for the image, because we want to create a review app for each branch.

It runs the docker login into our Gitlab registry container and push our docker image to there.

build:
  stage: build
  script:
    - docker login -u gitlab-ci-token -p ${CI_BUILD_TOKEN} ${CI_REGISTRY}
    - docker pull ${IMAGE} || true
    - docker build —cache-from ${IMAGE} -t ${IMAGE} -f ./Dockerfile .
    - docker push ${IMAGE}

Deploy

The deploy will be made by helm. As we have the helm files inside of our repository, we can easily access them by using k8s_manager. Manager in this case is the name of our files. Some people use to have a repository only to store this information, and then clone them here. We are not doing this, we are pushing the helm files in the same repository. The stage name will be deploy, but its name will be `deployreview`.

To run the deploy, the script should be connected to the cluster. To do so, it needs the GCP credentials that we have stored as environment variables in Gitlab.

  - echo $GCP_SERVICE_KEY > /tmp/kubeadmin.json
  - gcloud config set project $GCP_PROJECT
  - gcloud auth activate-service-account —key-file /tmp/kubeadmin.json
  - gcloud container clusters get-credentials $GKE_CLUSTER_NAME —zone $GKE_CLUSTER_ZONE —project $GCP_PROJECT

After this, we install helm tiller, in case we don’t have it installed(you probably don’t need this step, I’m putting here because we some cases we have problems with helm versions, feel free to remove it if you think you don’t need it).

- helm init —service-account tiller —force-upgrade 

The image we are using should have access to the Gitlab container registry. We set the right variables in our cluster.

- ‘printf “apiVersion: v1\nkind: Secret\n$(kubectl create secret docker-registry gitlab-registry —docker-server=$CI_REGISTRY —docker-username=$CI_REGISTRY_USER —docker-password=$CI_REGISTRY_PASSWORD —docker-email=$GITLAB_USER_EMAIL -o yaml —dry-run)” | kubectl apply -f -‘

Once we have helm tiller installed, we can use the command helm upgrade and deploy our new app.
In this case, we set the name of the deployment appending the branch name: manager-${CI_COMMIT_REF_SLUG}.

With this, we have our script done for deploying a review app version.

deploy_review:
  stage: deploy
  image: franzejr/helm
  script:
    - echo $GCP_SERVICE_KEY > /tmp/kubeadmin.json
    - gcloud config set project $GCP_PROJECT
    - gcloud auth activate-service-account —key-file /tmp/kubeadmin.json
    - gcloud container clusters get-credentials $GKE_CLUSTER_NAME —zone $GKE_CLUSTER_ZONE —project $GCP_PROJECT
    - helm init —service-account tiller —force-upgrade
    - echo $CI_REGISTRY_IMAGE
    - echo $CI_COMMIT_REF_SLUG
    - echo ${CI_COMMIT_SHORT_SHA}
    - echo “${CI_REGISTRY_IMAGE}/web:${CI_COMMIT_REF_SLUG}-${CI_COMMIT_SHORT_SHA}”
    #  Creating a docker registry secret so that kubernetes can connect to gitlab registry and pull the image
    - kubectl config set-context —current —namespace=gitlab-managed-apps
    - ‘printf “apiVersion: v1\nkind: Secret\n$(kubectl create secret docker-registry gitlab-registry —docker-server=$CI_REGISTRY —docker-username=$CI_REGISTRY_USER —docker-password=$CI_REGISTRY_PASSWORD —docker-email=$GITLAB_USER_EMAIL -o yaml —dry-run)” | kubectl apply -f -‘
    - helm upgrade —install
        —set image.repository_url=“${IMAGE}”
        —force manager-${CI_COMMIT_REF_SLUG} ./k8s/manager
  except:
      refs:
          - master

The complete gitlab-ci file you can see here:

image: docker:stable

services:
  - docker:stable-dind
stages:
  - build
  - deploy
variables:
  IMAGE: “${CI_REGISTRY_IMAGE}/web:${CI_COMMIT_REF_SLUG}-${CI_COMMIT_SHORT_SHA}”
build:
  stage: build
  script:
    - docker login -u gitlab-ci-token -p ${CI_BUILD_TOKEN} ${CI_REGISTRY}
    - docker pull ${IMAGE} || true
    - docker build —cache-from ${IMAGE} -t ${IMAGE} -f ./Dockerfile .
    - docker push ${IMAGE}
deploy_review:
  stage: deploy
  image: franzejr/helm
  script:
    - echo $GCP_SERVICE_KEY > /tmp/kubeadmin.json
    - gcloud config set project $GCP_PROJECT
    - gcloud auth activate-service-account —key-file /tmp/kubeadmin.json
    - gcloud container clusters get-credentials $GKE_CLUSTER_NAME —zone $GKE_CLUSTER_ZONE —project $GCP_PROJECT
    - helm init —service-account tiller —force-upgrade
    #  Creating a docker registry secret so that kubernetes can connect to gitlab registry and pull the image
    - kubectl config set-context —current —namespace=gitlab-managed-apps
    - ‘printf “apiVersion: v1\nkind: Secret\n$(kubectl create secret docker-registry gitlab-registry —docker-server=$CI_REGISTRY —docker-username=$CI_REGISTRY_USER —docker-password=$CI_REGISTRY_PASSWORD —docker-email=$GITLAB_USER_EMAIL -o yaml —dry-run)” | kubectl apply -f -‘
    - helm upgrade —install
        —set image.repository_url=“${IMAGE}”
        —force manager-${CI_COMMIT_REF_SLUG} ./k8s/manager
  except:
      refs:
          - master