Gitlab CI/CD pipelines with MicroK8s

1. Overview

Setting up a CI/CD pipeline can be a difficult thing when you have multiple developer teams that want to maintain their focus on the product. Luckily there is a way to integrate Gitlab with Microk8s to automatically build, test and deploy your projects.
Microk8s is a lightweight, production-grade, conformant Kubernetes. Microk8s defaults to the most widely used Kubernetes options, so it ‘just works’ with no config necessary. With a quick Microk8s setup and Gitlab integration developers can focus on the project and have a fully working pipeline from build to production in a matter of minutes.

What you’ll learn

  • Deploy and configure instances with Juju on AWS
  • Install Gitlab
  • Install and configure HA Microk8s
  • Integrate Mircok8s with Gitlab

What you’ll need

  • Account credentials for AWS
  • A DNS entry of the Gitlab instance on your domain

2. Juju installation

You can deploy Juju with:

sudo snap install juju --classic

Next, you will need to add the credentials for your cloud. We will be using Juju operator lifecycle manager (OLM) with an Amazon provider, but you can choose between any other public or private cloud providers and a bare-metal option.
In AWS create an IAM user with Administrator access and import the credentials with:

juju add-credential aws

You can refer to Juju official documentation to get more information on how to do this.

After this you will need to bootstrap your controller:

juju bootstrap aws --credential aws

We will deploy 1 Gitlab instance and 3 Microk8s machines. We will set up a high availability Microk8s, but you can simply use 1 machine for testing purposes. Let’s check the bundle. This bundle will deploy ubuntu charm to 4 machines. All of the machines will have 16GB RAM and 40 GB storage:

series: bionic
services:
    gitlab:
        charm: cs:ubuntu
        to:
            - 0
        num_units: 1
    microk8s:
        charm: cs:ubuntu
        to:
            - 1
            - 2
            - 3  
        num_units: 3
machines:
    "0":
        constraints: "mem=16384 root-disk=40G"
    "1":
        constraints: "mem=16384 root-disk=40G"
    "2":
        constraints: "mem=16384 root-disk=40G"
    "3":
        constraints: "mem=16384 root-disk=40G"

Now, let’s deploy the bundle:

juju deploy ./bundle.yaml

You can monitor the deployment with:

watch -c juju status --color

After the deployment succeeds you will need to open some ports for GitLab dashboard, container registry, and Kubernetes cluster API and expose those applications:

juju run --application gitlab "open-port 80; open-port 443; open-port 5050"
juju run --application microk8s "open-port 16443"
juju expose gitlab
juju expose microk8s

By default, Juju configures closed security groups, so we will need to add a new security rule. In your AWS console go to EC2 instances, click on one of the Juju default machines, open the “Security” tab and choose the security group you want to edit. Then add an inbound rule for all traffic from anywhere:


3. Gitlab installation

Now we can prepare for the Gitlab installation. At this point, you should already have a DNS entry on your domain for the Gitlab instance.

First, ssh into Gitlab instance and run a simple docker container on port 80:

juju ssh gitlab/0
sudo snap install docker
sudo docker run -d -p 80:80 tutum/hello-world

Then visit https://letsdebug.net/ to check that we opened the ports we need and Let’s Encrypt will be able to issue the certificate for Gitlab. This is an important step for us to be able to use Gitlab Auto DevOps feature:


We can see that everything went fine, so now you can stop the container to release port 80:

sudo docker ps
sudo docker stop <container-id>

If you are getting errors at this stage - please double-check that you opened all the ports and security groups mentioned.

After this we can proceed with the recommended Gitlab installation:

sudo apt-get update
sudo apt-get install -y curl openssh-server ca-certificates tzdata perl
sudo apt-get install -y postfix
curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ee/script.deb.sh | sudo bash
sudo EXTERNAL_URL="https://gitlab.natalytvinova.com" apt-get install gitlab-ee

exit

On your first visit, you’ll be redirected to a password reset screen. Provide the password for the initial administrator account and you will be redirected back to the login screen. Use the default account’s username ‘root’ to login:




4. Microk8s installation

Let’s install Microk8s on 3 on the machines:

juju run --application microk8s "sudo snap install microk8s --classic --channel=1.19/stable; sudo usermod -a -G microk8s ubuntu; sudo chown -f -R ubuntu ~/.kube"

To use the external IP address to expose the Kubernetes cluster API we need to regenerate Microk8s certificates. Add Microk8s instance IPs to the certificate template file:

juju ssh microk8s/0
sudo vim /var/snap/microk8s/current/certs/csr.conf.template
...

[ alt_names ]
DNS.1 = kubernetes
IP.1 = 127.0.0.1
IP.2 = 10.152.183.1
IP.100 = <microk8s/0 IP>
IP.101 = <microk8s/1 IP>
IP.102 = <microk8s/2 IP>
#MOREIPS

...

And restart the services:

microk8s.stop
microk8s.start

In order to enable HA, generate a token on one of the Microk8s machines for the other machines:

microk8s add-node

exit

This will output a command with a generated token. Copy this command and run it from the next node. It may take a few minutes to successfully join:

juju ssh microk8s/1
microk8s join <IP>:25000/<TOKEN>

# exit after the command is completed

Repeat this process (generate a token, run it from the joining node) for the third node:

juju ssh microk8s/0
microk8s add-node

exit

juju ssh microk8s/2
microk8s join <IP>:25000/<TOKEN>

# exit after the command is completed

Now, let’s enable DNS and storage add-ons:

microk8s enable dns
microk8s enable storage

Microk8s uses a namespaced kubectl command to prevent conflicts with any existing installs of kubectl. If you don’t have an existing install, it is easier to add an alias (append to ~/.bash_aliases) like this:

echo "alias kubectl='microk8s kubectl'" >> ~/.bash_aliases
source .bash_aliases

Our Microk8s configuration is complete and we can go and integrate the cluster with Gitlab.


5. Gitlab integration

Create a new project:

Go to operations, kubernetes. Add a cluster:

Click add an existing cluster:

The cluster API will be on the Microk8s IP address: https://<microk8s unit external IP>:16443.

Extract the certificate with:

juju ssh microk8s/0
kubectl get secrets
kubectl get secret <TOKEN>  -o jsonpath="{['data']['ca\.crt']}" | base64 --decode

And paste it to the CA certificate field.
To extract the token create a Gitlab service account and a cluster role binding:

vim gitlab-admin-service-account.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: gitlab
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: gitlab-admin
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
  - kind: ServiceAccount
    name: gitlab
    namespace: kube-system

And apply it:

kubectl apply -f gitlab-admin-service-account.yaml

Now extract the token:

kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep gitlab | awk '{print $1}')

And copy the token value to the Service Token field.

Disable RBAC cluster and click “Create”.

To enable AutoDevops we need to install some apps. On the Kubernetes clusters page choose your new cluster and go to the Applications tab.

First install the Gitlab Runner, Prometheus, and a Cert Manager.

Before installing Ingress we will need also need to enable metallb add-on for the load balancing. To do it we need to choose the correct IP range for metallb to choose from:

ifconfig eth0

We will choose the range from 1 to 21:

microk8s enable metallb:172.31.16.1-172.31.31.21

And install the Ingress app:

To be able to run Auto Review and Auto Deploy stages we will need to set up a wildcard DNS pointing to the address provided by Ingress:

I will also disable the testing stage as I do not want to run the testing for this project. Go to Settings > CI/CD > Variables > Add Variable:

Then set TEST_DISABLED: true:

Now, let’s add some code into our repository and see the pipelines running. On your local machine login and clone an existing project:

$ git config --global user.name "Administrator"
$ git config --global user.email "[email protected]"
$ git clone https://github.com/natalytvinova/cdk-cats.git 
$ cd cdk-cats/
ls -a

Our example project is just a simple Nginx app that will greet the user with a cat picture. As you can see it already has a Dockerfile that will enable the Auto Build stage. We also have a Gitlab directory with the Auto Deploy values file that changes the default application port to 80:

service:
  internalPort:80
  externalPort:80

Let’s push the project to the Gitlab repo:

git remote rename origin old-origin
git remote add origin [email protected]:root/microk8s-cats.git
git push -u origin --all
git push -u origin --tags

Now we can check if the pipeline is running by going into CI/CD > Pipelines. After some time we can see every stage being completed:

After the review stage, we can click on the production job and check how the container was deployed on our cluster. Copy the container address:

To be able to access the review page we need to sshuttle from the Microk8s machine to the Ingress address:

sshuttle -r ubuntu@<Microk8s-IP> <Ingress-IP>

Great! We have a meow from Kubernetes:

Now let’s see how Gitlab would behave if we push something to the repository. We will switch to a new branch and replace an old picture with a new one:

git checkout master
git checkout -b serious-cat
cp ~/Downloads/serious_cat.jpg html/cat.jpg
git add html/cat.jpg
git commit -m "serious cat picture change"
git push -u origin serious-cat

As we can see the new pipeline has already been started:

And after some time we can visit the review page:

And check the changes we have made:


6. That’s all folks!