Gitlab CI/CD on Kubernetes
You can use GitLab CI/CD to build and deploy your applications on Kubernetes. It is not hard to integrate GitLab with Kubernetes. You can take an advantage of the GUI support to set up a connection with your Kubernetes cluster. Furthermore, GitLab CI provides a built-in container registry to store and share images.
Preface
In this article, I will describe all the steps required to build and deploy your Java application on Kubernetes with GitLab CI/CD. First, we are going to run an instance of the GitLab server on the local Kubernetes cluster. Then, we will use the special GitLab features in order to integrate it with Kubernetes. After that, we will create a pipeline for our Maven application. You will learn how to build it, run automated tests, build a Docker image, and finally run it on Kubernetes with GitLab CI.
In this article, I’m going to focus on simplicity. I will show you how to run GitLab CI on Kubernetes with minimal effort and resources. With this in mind, I hope it will help to form an opinion about GitLab CI/CD. The more advanced, production installation may be easily performed with Helm. I will also describe that approach in the next section.
Run GitLab on Kubernetes
In order to easily start with GitLab CI on Kubernetes, we will use its image from the Docker Hub. We may choose between community and enterprise editions. First, we need to change the default external URL. We will also enable the container registry feature. To override both these settings we need to add the environment variable GITLAB_OMNIBUS_CONFIG
. It can contain any of the GitLab configuration properties. Since I’m running the Kubernetes cluster locally, I also need to override the default URL of the Docker registry. You can easily get it by running the command docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' registry
. The local image registry is running outside the Kubernetes cluster as a simple Docker container with the name registry
.
apiVersion: apps/v1
kind: Deployment
metadata:
name: gitlab-deployment
spec:
selector:
matchLabels:
app: gitlab
template:
metadata:
labels:
app: gitlab
spec:
containers:
- name: gitlab
image: gitlab/gitlab-ee
env:
- name: GITLAB_OMNIBUS_CONFIG
value: "external_url 'http://gitlab-service.default/';gitlab_rails['registry_enabled'] = true;gitlab_rails['registry_api_url'] = \"http://172.17.0.2:5000\""
ports:
- containerPort: 80
name: HTTP
volumeMounts:
- mountPath: /var/opt/gitlab
name: data
volumes:
- name: data
emptyDir: {}
Then we will also create the Kubernetes service gitlab-service
. GitLab UI is available on port 80. We will use that service to access the GitLab UI outside the Kubernetes cluster.
apiVersion: v1
kind: Service
metadata:
name: gitlab-service
spec:
type: NodePort
selector:
app: gitlab
ports:
- port: 80
targetPort: 80
name: http
Finally, we can apply the GitLab manifest with Deployment
and Service
to Kubernetes. To do that you just need to execute the command kubectl apply -f k8s/gitlab.yaml
. Let’s check the address of gitlab-service
.
Of course, we may also install GitLab on Kubernetes with Helm. However, you should keep in mind that it will generate a full deployment with some core and optional components. Consequently, you will have to increase RAM assigned to your cluster to around 15MB to be able to run it. Here’s a list of required Helm commands. For more details, you may refer to the GitLab documentation.
$ helm repo add gitlab https://charts.gitlab.io/
$ helm repo update
$ helm upgrade --install gitlab gitlab/gitlab \
--timeout 600s \
--set global.hosts.domain=example.com \
--set global.hosts.externalIP=10.10.10.10 \
--set certmanager-issuer.email=me@example.com
Clone the source code
If you would like to try it by yourself, you may always take a look at my source code. In order to do that you need to clone my repository sample-spring-boot-on-kubernetes. Then you should just follow my instructions ๐
First, let’s create a new repository on GitLab. Of course, its name is sample-spring-boot-on-kubernetes
as shown below.
Then, you should clone my example repository, and move it to your GitLab instance running on Kubernetes. Assuming the address of my local instance of GitHub is http://localhost:30129
, I need to execute the following command.
$ git remote add gitlab http://localhost:30129/root/sample-spring-boot-on-kubernetes.git
To clarify, let’s display a list of Git remotes for the current repository.
$ git remote -v
gitlab http://localhost:30129/root/sample-spring-microservices-kubernetes.git (fetch)
gitlab http://localhost:30129/root/sample-spring-microservices-kubernetes.git (push)
origin https://github.com/piomin/sample-spring-microservices-kubernetes.git (fetch)
origin https://github.com/piomin/sample-spring-microservices-kubernetes.git (push)
Finally, we can push the source code to the GitLab repository using the gitlab
remote.
$ git push gitlab
Configure GitLab integration with Kubernetes
After login to the GitLab UI, you should enable local HTTP requests. Do that you need to go to the admin section. Then click “Settings” -> “Network” -> “Outbound requests”. Finally, you need to check the box “Allow requests to the local network from web hooks and services”. We will use internal communication between GitLab and Kubernetes API, and between the GitLab CI runner and the GitLab master.
Now, we may configure the connection to the Kubernetes API. To do that you should go to the section “Kubernetes”, then click “Add Kubernetes cluster”, and finally switch to the tab “Connect existing cluster”. We need to provide some basic information about our cluster in the form. The name is required, and therefore I’m setting the same name as my Kubernetes context. You may leave a default value in the “Environment scope” field. In the “API URL” field I’m providing the internal address of Kubernetes API. It is http://kubernetes.default:443
.
We also need to paste the cluster CA certificate. In order to obtain it, you should first find the secret with the prefix default-token-
, and copy it with the following command.
$ kubectl get secret default-token-ttswt -o jsonpath="{['data']['ca\.crt']}" | base64 --decode
Finally, we should create a special ServiceAccount
for GitLab with the cluster-admin
role.
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
Then you should find the secret with the prefix gitlab
in the kube-system
namespace, and display its details. After that, we need to copy value of field token
, and paste it to the form without decoding.
$ kubectl describe secret gitlab-token-5sk2v -n kube-system
Here’s the full information about our Kubernetes cluster required by GitLab. Let’s add it by clicking “Add Kubernetes cluster”.
Once you have successfully added the new Kubernetes cluster in the GitLab UI you need to display its details. In the tab “Applications” you should find the section “GitLab runner” and install it.
The GitLab runner is automatically deployed in the namespace gitlab-managed-apps
. We can verify if it started succesfully.
$ kubectl get pod -n gitlab-managed-apps
NAME READY STATUS RESTARTS AGE
runner-gitlab-runner-5649dbf49-5mnjv 1/1 Running 0 5m56s
The GitLab runner tries to communicate with the GitLab master. To verify that everything works fine, we need to go to the section “Overview” -> “Runners”. If you see the IP address and version number, it means that the runner is able to communicate with the master. In case of any problems, you should take a look at the pod logs.
Create application pipeline
The GitLab CI/CD configuration file is available in the project root directory. Its name is .gitlab-ci.yml
. It consists of 5 stages and uses the Maven Docker image for executing builds. It is automatically detected by GitLab CI. Let’s take a closer look at it.
First, we are running the build
stage responsible for building the application for the source code. It just runs the command mvn compile
. Then, we are running JUnit tests using mvn test
command. If all the tests are passed, we may build a Docker image with our application. We use the Jib Maven plugin for it. It is able to build an image in the docker-less mode. Therefore, we don’t have to run an image with a Docker client. Jib builds an image and pushes it to the Docker registry. Finally, we can deploy our container on Kubernetes. To do that we are using the bitnami/kubectl
image. It allows us to execute kubectl
commands. In the first step, we are deploying the application in the test
namespace. The last stage deploy-prod
requires a manual approval. Both deploy stages are allowed only for a master
branch.
image: maven:latest
stages:
- build
- test
- image-build
- deploy-tb
- deploy-prod
build:
stage: build
script:
- mvn compile
test:
stage: test
script:
- mvn test
image-build:
stage: image-build
script:
- mvn -s .m2/settings.xml -P jib compile jib:build
deploy-tb:
image: bitnami/kubectl:latest
stage: deploy-tb
only:
- master
script:
- kubectl apply -f k8s/deployment.yaml -n test
deploy-prod:
image: bitnami/kubectl:latest
stage: deploy-prod
only:
- master
when: manual
script:
- kubectl apply -f k8s/deployment.yaml -n prod
We may push our application image to a remote or a local Docker registry. If you do not pass any address, by default Jib tries to push the image to the docker.io
registry.
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>2.4.0</version>
<configuration>
<to>piomin/sample-spring-boot-on-kubernetes</to>
</configuration>
</plugin>
In order to push images to the docker.io
registry, we need to provide client’s authentication credentials.
<servers>
<server>
<id>registry-1.docker.io</id>
<username>${DOCKER_LOGIN}</username>
<password>${DOCKER_PASSWORD}</password>
</server>
</servers>
Hereโs a similar configuration, but for the local instance of the registry.
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>2.4.0</version>
<configuration>
<allowInsecureRegistries>true</allowInsecureRegistries>
<to>172.17.0.2:5000/root/sample-spring-boot-on-kubernetes</to>
</configuration>
</plugin>
Run GitLab CI pipeline on Kubernetes
Finally, we may run our GitLab CI/CD pipeline. We can push the change in the source code or just run the build manually. You can see the result for the master
branch in the picture below.
The last stage deploy-prod
requires manual approval. We may confirm it by clicking the “Play” button.
If you push changes to another branch than master
, the pipeline will run just three stages. It will build the application, run tests, and build a Docker image.
You can also take advantage of the integrated container registry. You just need to set the right name of a Docker image. It should contain the GitLab owner name and image name. In that case, it is root/sample-spring-boot-on-kubernetes
. Iโm using my local Docker registry available at 172.17.0.2:5000
.
Conclusion
GitLab seems to be a very interesting tool for building CI/CD processes on Kubernetes. It provides built-in integration with Kubernetes and a Docker container registry. Its documentation is at a very high level. In this article, I tried to show you that it is relatively easy to build CI/CD pipelines for Maven applications on Kubernetes. If you are interested in building a full CI/CD environment you may refer to the article How to setup continuous delivery environment. Enjoy ๐
2 COMMENTS