Lab 1 Report
1. Successful run of the CI/CD job
https://gitlab.stud.atlantis.ugent.be/lvmolle/devops-project/-/jobs/248801
2. Structure & Encountered Problems
2.a. Structure
The .gitlab-ci.yml file automates the build, packaging, and execution of the project.
The pipeline uses the eclipse-temurin:25-jdk image so that Maven and Java tools are available in all stages.
There are global variables defined to configure Maven and Quarkus. MAVEN_CLI_OPTS and MAVEN_OPTS make Maven run in batch mode and use a local .m2/repository folder. The Quarkus variables define where the container image should be built and pushed. The LOGIC_SERVICE_IMAGE variable then combines those values into the full image URL used later in the pipeline.
Maven dependencies in .m2/repository/ are cached to make builds faster by reusing previously downloaded packages. The cache key includes both the pom.xml file and the branch name ($CI_COMMIT_REF_SLUG), so each branch has its own cache and it refreshes automatically if dependencies change.
The pipeline has three stages: build, package, and execute. This way, the project is first built, then turned into a container image, and finally executed in a test environment.
Build stage
In this stage, the maven-build job compiles the project using ./mvnw package. The output is stored in the target/ folder as an artifact (for one week), so other jobs can use it without rebuilding everything.
Package stage
In the package stage, the maven-container-image-generation job builds and pushes the container image using the Quarkus built-in image support (which uses Jib). I used the short-lived $CI_JOB_TOKEN for authentication instead of $CI_REGISTRY_PASSWORD since it’s more secure. This job uploads the image to the GitLab Container Registry.
Execute stage
Finally, the run-game job runs the pushed logic-service image as a service.
It uses the devops-runner image to start the game, which connects to http://logic-service:8080.
I also set some environment variables like PLAYER_NAME, LOGIC_URL, TURN_INTERVAL_MS, and TURN_LIMIT to control the game behavior. The job then launches the main class that runs the game logic.
2.b. Problems Encountered
-
Trouble setting up the maven-container-image-generation job
- Most of the issues I had were with the maven-container-image-generation job. I mixed up some predefined GitLab environment variables at first, which caused the image to be pushed to the wrong location. To fix it, I added some debug
echostatements and usedprintenvto see which variables produced the right image URL.
- Most of the issues I had were with the maven-container-image-generation job. I mixed up some predefined GitLab environment variables at first, which caused the image to be pushed to the wrong location. To fix it, I added some debug
-
Removed the QUARKUS_CONTAINER_IMAGE_PUSH flag by accident
- While cleaning up the
.gitlab-ci.yml, I accidentally removedQUARKUS_CONTAINER_IMAGE_PUSH: "true", which caused the build to fail because Jib wasn’t building the image anymore. I fixed it by checking back in my Git history and comparing with a previous working version.
- While cleaning up the
3. Questions
-
What is
./mvnwand what is the advantage of using it abovemvn?./mvnwis a Maven wrapper script that comes with the project. This wrapper downloads a project-compatible version of Maven that will only be used within the context of this project. Using Maven wrapper makes it easier for developers to collaborate and prevents you from having to install Maven on your system. -
Explain the key differences between GitLab's
cacheandartifactsmechanisms. For each of Maven dependencies and build files, explain which mechanism you chose and why. What are the trade-offs of your choices?A cache (used for
.m2/repository/) is one or more files that a job downloads and saves for later reuse. Subsequent jobs that use the same cache key can restore these files without re-downloading them, which speeds up builds significantly. However, caches are best-effort and depend on the cache key — if the key doesn’t change when dependencies do, you might end up with stale content.Artifacts (used for
target/) are files explicitly saved after a job completes and are made available to subsequent jobs within the same pipeline. They are reliable, versioned snapshots of your build outputs and can also be downloaded manually from the GitLab UI. Artifacts can be configured with an expire_in value to automatically clean up after some time.Choices
- For Maven dependencies, I used a cache so that jobs can reuse the local Maven repository (
.m2/repository) and skip re-downloading dependencies every time. - For build files (
target/), I used artifacts to share the compiled output between jobs (for example, from the build job to the container image job).
Trade-offs
- Cache: Faster builds and reduces time for downloading dependencies. But it relies on the user setting up a correct cache key to avoid outdated dependencies.
- Artifacts: More reliable and consistent since they are tied to a specific pipeline, but they consume more storage.
- For Maven dependencies, I used a cache so that jobs can reuse the local Maven repository (
-
In this lab, we use a
25-jreimage as the base for our runtime container and a25-jdkimage for the CI/CD build jobs. Explain the reasoning behind this choice. What would be the impact (positive or negative) of using25-jdkfor both? What about using25-jrefor both?The
25-jdkimage includes everything needed to build Java applications including the compiler, debugging tools, and runtime. The25-jreimage only contains what’s needed to run Java programs, which makes it smaller and more secure since it doesn’t include unnecessary build tools.In the pipeline, the build stage uses the
25-jdkimage because Maven needs the compiler to build the project. Once it’s built, the runtime container doesn’t need any of that, it just needs to run the compiled app. So I used the25-jreimage there. This keeps the final image smaller and faster to deploy.By using
25-jdkfor both, it would still work, but the runtime image would be larger than necessary and include tools that shouldn’t really be in production. If I used25-jrefor both, the build would fail completely because there’s no compiler included. This why we only use25-jdkfor the build process and25-jrefor running the container.