Lab 1 Report
Description
Lab 1 was about setting up a CI/CD pipeline using GitLab for a Quarkus-based logic service.
The goal was to automatically build, package and execute the game through multiple pipeline stages.
I configured three jobs: maven-build, maven-container-image-generation and run-game with proper use of caches and artifacts to improve efficiency.
Here is a link to a successful run of the CI/CD job that executed the game.
Structure of .gitlab-ci.yml
Jobs
maven-build
This job compiles the project using Maven and is part of the build stage. It runs the command ./mvnw $MAVEN_CLI_OPTS compile and saves the output in the target/ directory as an artifact, so the next job can reuse it.
maven-container-image-generation
In this stage the container image for the logic service is built and pushed to the GitLab container registry.
I used environment variables such as $QUARKUS_CONTAINER_IMAGE_REGISTRY, $QUARKUS_CONTAINER_IMAGE_GROUP, and $QUARKUS_CONTAINER_IMAGE_TAG to dynamically generate the correct image name and tag. This job is part of the package stage.
run-game
This final job, part of the execute stage, runs the actual game inside a container using the devops-runner image. I defined a service that starts my previously built logic-service image so the runner can connect to it.
Cache and Artifacts
To improve performance, I added a cache for the local Maven repository so that dependencies do not have to be downloaded every time the pipeline runs.
Additionally, I configured the target/ directory as an artifact, ensuring that the compiled files are available in later stages.
Problems
My first encounter with problems was with this block of code:
image:
name: gitlab.stud.atlantis.ugent.be:5050/utils/docker/devops-runner:latest
entrypoint: ""
GitLab stated the following: Unable to create pipeline. jobs:run-game:image entrypoint should be an array of strings.
Thus, the solution was to simply wrap the entrypoint between brackets.
After pushing this solution, my next problem came in: ERROR: Job failed: invalid service image name: gitlab.stud.atlantis.ugent.be:5050/matthiascansse/devops-project/logic-service:.
This was due to an invalid image name:
services:
- name: $CI_REGISTRY_IMAGE/logic-service:${QUARKUS_CONTAINER_IMAGE_TAG:-latest}
GitLab could not interpret the latest tag correctly, so I removed it. Since I already defined QUARKUS_CONTAINER_IMAGE_TAG = latest in the GitLab GUI, the image tag was still applied automatically.
The last issue was caused by an incomplete Jib configuration. After adding the required dependency and properties and enabling the Jib builder in the CI job, the image build and push worked successfully.
Questions
What is ./mvnw and what is the advantage of using it above mvn?
./mvnw is a Maven Wrapper. It is a script included in the project that automatically downloads and uses the correct version of Maven that the project was built with. The advantage of using ./mvnw instead of mvn is that it guarantees consistency between environments. This makes the build process more portable and reproducible, since the same Maven version and configuration are always used.
Explain the key differences between GitLab's cache and artifacts mechanisms. For each of Maven dependencies and build files, explain which mechanism you chose and why. What are the trade-offs of your choices?
Cache is used to reuse files between different jobs or pipeline runs to speed up the process. It’s mostly used for dependencies that do not change often like the local Maven repository. Artifacts are used to pass build outputs from one job to another within the same pipeline. Artifacts are stored temporarily on the GitLab server.
In my pipeline, I used cache for the Maven dependencies so that they don’t need to be downloaded again in each build which saves time.
I used artifacts for the target/ folder because it contains the compiled classes and JAR files that need to be reused by the packaging and execution stages.
The trade-off is that caches can become outdated if dependencies change, so sometimes they need to be cleared manually. Artifacts are slower to store and retrieve, but ensure that the exact build output is passed between jobs.
In this lab, we use a 25-jre image as the base for our runtime container and a 25-jdk image for the CI/CD build jobs. Explain the reasoning behind this choice. What would be the impact (positive or negative) of using 25-jdk for both? What about using 25-jre for both?
The 25-jdk image includes the Java Development Kit (JDK), which is required to compile and build Java applications. That’s why it is used in the CI/CD build stages where Maven compiles the code and builds the container image. The 25-jre image only includes the Java Runtime Environment (JRE), which is lighter and meant only for running already compiled applications. Using it for the runtime container reduces image size and improves startup time.
If we used 25-jdk for both building and running, the runtime image would be unnecessarily large and contain tools that are not needed in production. If we used 25-jre for both, the build jobs would fail because the JRE does not include a compiler or build tools. It can only run Java applications, not compile them.