Gitlab CI/CD Deploy to GKE

Gitlab CI/CD Deploy to GKE

This is a guide to create a Google Kubernetes cluster and configure Gitlab CI / CD to deploy your app to the cluster.

This assumes that you have a paid or trial Google Cloud account.

Creating a Kubernetes cluster

  • Go to the Google Console.
  • https://console.cloud.google.com/kubernetes
  • Click “Create Cluster”.
  • Fill in the name, description, location, zone. It’s a good idea to keep everything in the same location and zone so it can quickly communicate with other services from Google.
  • Under the “Node Pools” section there will be a “More Options” button. Click it to configure your default pool. There will be an option for giving access levels to other Google services. You can give it full access or on a service by service basis. If you need your services to talk to a Cloud SQL database then enable the Cloud SQL service for example.
  • Click the “Availability, networking, security, and additional features” link at the bottom of the page.
  • Check the “Enable VPC-native (using alias IP)” checkbox. This will enable pods to connect to services without the need for a Kubernetes side car.
  • Check the “Enable legacy authorization” checkbox. This will enable Gitlab to be able to deploy to Kubernetes.
  • Click the “Create” button.
  • Go into the cluster to view the basic information about Kubernetes.
  • Copy the “Endpoint” IP address and save it to use to configure Gitlab.
  • Click the “Show credentials” right next to the “Endpoint” IP address. This will give the CA certificate to use to configure Gitlab.

Creating a secret key in Kubernetes

  • Go to the Google Console.
  • https://console.cloud.google.com/kubernetes
  • Click “Connect” on the cluster.
  • Click “Run in Cloud Shell”. This will create an in browser shell for you to run commands for the cluster.
# Create the Gitlab Deploy Key
kubectl create serviceaccount gitlab-deploys

# Create the Gitlab Deploy Binding
kubectl create clusterrolebinding gitlab-deploy-binding --clusterrole=cluster-admin --user=gitlab-deploys

# Describe the Service Account. This will reveal the name of the secret
kubectl describe serviceaccount gitlab-deploys

# Copy the output of the token. We will enter this in Gitlab
kubectl describe secret {secret-name}

Configuring Gitlab

  • Open up your gitlab group
  • https://gitlab.com/{{group}}
  • Click the left side “Kubernetes” navigation link
  • Click the “Add Kubernetes Cluster” button
  • You can either connect an already existing cluster, or create a new one right from Gitlab. We will add an existing cluster.
  • Click “Add existing cluster”.
  • Give it your Kubernetes cluster name.
  • The API URL is the “Endpoint” IP address of the Kubernetes cluster
  • The CA Certificate is the one found under “Show Credentials” in the Kubernetes cluster information. Make sure it is formatted correctly. Sometimes copying it straight from the Kubernetes cluster will not correctly format it.
  • The Service Token is the secret key we created in the Kubernetes cluster. Copy it from the Cloud Shell into this field.
  • Leave the rest checked if you want Gitlab to manage your Kubernetes cluster for you, which we don’t. If you just want to deploy then uncheck the rest of the checkboxes.
  • Create a Gitlab deployment repo

We will now create a docker image that will run a script that will deploy the application to the Kubernetes cluster.

  • Create a repo in your group called “kubernetes-deploy”.
  • Create a Dockerfile. -Create a folder called scripts, and a file called deploy.sh under that folder.
  • Create a .gitlab-ci.yml file.

Dockerfile

FROM alpine

RUN apk --no-cache add bash curl && \
    curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl && \
    chmod +x ./kubectl && \
    mv ./kubectl /usr/local/bin/kubectl

COPY scripts /scripts
CMD ["bash"]

deploy.sh

#!/usr/bin/env bash
set -e

mkdir -p ~/.kube
cp $KUBECONFIG ~/.kube/config

sed -i -e "s/{{image}}/$CI_COMMIT_SHA/g" kube-deployment.yaml

kubectl apply -f kube-deployment.yaml

Don’t forget to give this file execution permissions.

sudo chmod a+x deploy.sh

.gitlab-ci-yml

image: docker:git

services:
  - docker:dind

stages:
  - build
  - release

variables:
GITLAB_GROUP: example
GITLAB_CONTAINER_TEST_IMAGE: registry.gitlab.com/$GITLAB_GROUP/kubernetes-deploy:$CI_COMMIT_SHA
  GITLAB_CONTAINER_RELEASE_IMAGE: registry.gitlab.com/$GITLAB_GROUP/kubernetes-deploy:latest

before_script:
  - docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN registry.gitlab.com

build:
  stage: build
  script:
    - docker build -t $GITLAB_CONTAINER_TEST_IMAGE .
    - docker push $GITLAB_CONTAINER_TEST_IMAGE

release:
  stage: release
  script:
    - docker pull $GITLAB_CONTAINER_TEST_IMAGE
    - docker tag $GITLAB_CONTAINER_TEST_IMAGE $GITLAB_CONTAINER_RELEASE_IMAGE
    - docker push $GITLAB_CONTAINER_RELEASE_IMAGE
  only:
    - master

Push this to your master branch and let the pipeline build the docker image for you. This is important as we will be using this docker image to build from any repo that we want to deploy to Kubernetes.

Now in the Gitlab repo you want to deploy to Kubernetes, create a .gitlab-ci.yml. This will use the kubernetes-deploy docker image to deploy to Kubernetes. Don’t forget to change your group and repo name.

image: docker:git

stages:
  - build
  - test
  - release
  - deploy

services:
  - docker:dind

variables:
  GITLAB_GROUP: example
  GITLAB_REPO: api
  GITLAB_KUBERNETES_DEPLOY_IMAGE: registry.gitlab.com/$GITLAB_GROUP/kubernetes-deploy:latest
  GITLAB_CONTAINER_TEST_IMAGE: registry.gitlab.com/$GITLAB_GROUP/$GITLAB_REPO:$CI_COMMIT_SHA
  GITLAB_CONTAINER_RELEASE_IMAGE: registry.gitlab.com/$GITLAB_GROUP/$GITLAB_REPO:latest

build:
  stage: build
  before_script:
    - docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN registry.gitlab.com
  script:
    - docker build -t $GITLAB_CONTAINER_TEST_IMAGE .
    - docker push $GITLAB_CONTAINER_TEST_IMAGE

lint:
  image: $GITLAB_CONTAINER_TEST_IMAGE
  stage: test
  script:
    - echo "Run your lint or tests here"

test:
  image: $GITLAB_CONTAINER_TEST_IMAGE
  stage: test
  script:
    - echo "run your tests here"

release:
  stage: release
  before_script:
    - docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN registry.gitlab.com
  script:
    - docker pull $GITLAB_CONTAINER_TEST_IMAGE
    - docker tag $GITLAB_CONTAINER_TEST_IMAGE $GITLAB_CONTAINER_RELEASE_IMAGE
    - docker push $GITLAB_CONTAINER_RELEASE_IMAGE
  only:
    - master

.deploy: &deploy
  image: $GITLAB_KUBERNETES_DEPLOY_IMAGE
  script:
    - /scripts/deploy.sh
  only:
    - master
  allow_failure: false

deploy:
  <<: *deploy
  stage: deploy
  when: manual

The deploy script expects a file named “kube-deployment.yaml” is present in the root of your repo. This is the Kubernetes definition file that will create a deployment for you in Kubernetes. Again don’t forget to change the name of the docker image to your group and repo name.

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  labels:
    app: api
  name: api
spec:
  replicas: 1
  selector:
    matchLabels:
      app: api
  strategy:
    rollingUpdate:
      maxSurge: 0
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: api
    spec:
      containers:
        - image: registry.gitlab.com/example/api:{{image}}
          imagePullPolicy: Always
          name: api
          ports:
            - containerPort: 3000
              protocol: TCP
          resources:
            requests:
              cpu: 200m
          env:
            - name: NODE_ENV
              valueFrom:
                secretKeyRef:
                  name: api-env-secrets
                  key: NODE_ENV
      restartPolicy: Always
      terminationGracePeriodSeconds: 30

apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: api
spec:
  scaleTargetRef:
    apiVersion: apps/v1beta1
    kind: Deployment
    name: api
  minReplicas: 2
  maxReplicas: 4
  targetCPUUtilizationPercentage: 50

This deployment file assumes you have created a secrets file named “api-env-secrets”.

To create this, first start out by creating an .env file

NODE_ENV=production

# Anything else you need. Database credentials, etc.

Then run the following command on the Cloud Shell command line.

kubectl create secret generic api-env-secrets --from-env-file=.env

Now you can push your code in your repo to the master branch in Gitlab and press the manual play button in the Gitlab CI / CD pipelines section to see it deploy to Kubernetes.

If everything was a success you should see your Kubernetes pods created.

kubectl get pods

Now we need to create a Kubernetes deployment with an exposed port that our application will be running on.

kubectl expose deployment api --target-port=3000 --type=NodePort

You can confirm it was created successfully by running.

kubectl get services

You will need to create an ingress load balancer to direct traffic to your application.

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: api-ingress
  namespace: development
spec:
  backend:
    serviceName: api
    servicePort: 3000

Then run this command to create the ingress.

kubectl apply -f ingress.yaml

You can confirm that it was created by running.

kubectl get ingress

This command will show you the IP address of the ingress. You will need to go into Google Cloud in order to setup SSL certificates and configure your ingress to use a static IP, but for now you should have everything setup in order to have the Gitlab CI / CD deploy your app to Kubernetes.

You can also modify the deployments to have Kubernetes namespaces. This will allow you to create a development and production environment if needed.


More Articles

Creating a Confirm Dialog in React and Tailwind CSS

Creating a Confirm Dialog in React and Tailwind CSS

In this article we will go over how to create a confirm dialog with React and Tailwind CSS.

Being a Developer and Dealing with Imposter Syndrome

Being a Developer and Dealing with Imposter Syndrome

The training you receive at your company wont prepare you for all that is necessary in completing certain tasks. However the greatest difficulty is the way we think about ourselves.

Next.js Google Search SEO

Next.js Google Search SEO

How to get your website indexed and ranked on Google using Next.js.


Sign up to our newsletter

Stay up to date with the latest articles, tutorials, and news.