Lab 1 Report
Successful run
I struggled for a while, but eventually I got a working build pipeline that takes under a minute to run (if a valid cache is available):
pipeline
Issues encountered during Lab 1
- After implementing the logic-service as a service in the run-game job, I ran into the following error in the job log:
ERROR: Job failed: failed to pull image "logic-service:latest" with specified policies [if-not-present]: Error response from daemon: pull access denied for logic-service, repository does not exist or may require 'docker login': denied: requested access to the resource is denied (manager.go:253:1s)
services:
- logic-service
I quickly realised that the path to the service wasn't correct. After adding some more logging to the build I found out the exact location of the logic-service.
echo $CI_REGISTRY/milandri/devops-project/logic-service
Using printenv I could see the values of all the environment variables and know which ones I could use for the service path and updated the code as follows:
services:
- "$CI_REGISTRY_IMAGE/logic-service:latest"
Now there was no more error, but the job would be stuck on this line until cancelled:
12:09:46.265 [main] INFO b.u.d.g.services.runner.Launcher - Waiting for player...
Turns out the runner expects the logic-service to be called "logic-service", so I added an alias to the service in the yml:
services:
- logic-service
alias
- Next issue was when I tried to implement caching in the first 2 jobs:
cache:
key:
files:
- pom.xml
paths:
- .m2/repository/
I noticed that the pipeline times weren't getting shorter and the logs still showed downloading of all the dependencies after loading the cache. There was also this warning message for the saving of the cache after the jobs finished:
Saving cache for successful job
00:00
Creating cache 0_pom-4e86d16a1a6ece4fbdde3c7afca3d5b41d98248f-protected...
WARNING: .m2/repository: no matching files. Ensure that the artifact path is relative to the working directory (/builds/milandri/devops-project)
Archive is up to date!
Created cache
The message gives a good indication of what might have gone wrong. After adding some more logging I find out that the .m2/repository/ folder is available in /root/, but not in the the project directory. I assumed that the variable $MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository" would change the working directory of the maven builds to the local file like with the environment variables that we defined. First I tried to change the artifact path to use the /root/.m2/repository/ path, but then I got this warning:
Creating cache run-game-3-protected...
WARNING: processPath: artifact path is not a subpath of project directory: /root/.m2/repository/
So I instead changed back the path and added $MAVEN_OPTS to the ./mvnw commands in both jobs. Now the artifacts are in the correct location and the caching works on both the build and package steps: pipeline
- I ended up with this .gitlab-ci.yml:
image: gitlab.stud.atlantis.ugent.be:5050/utils/docker/eclipse-temurin:25-jdk
variables:
MAVEN_CLI_OPTS: "--batch-mode"
QUARKUS_CONTAINER_IMAGE_PUSH: "true"
QUARKUS_CONTAINER_IMAGE_USERNAME: $CI_REGISTRY_USER
QUARKUS_CONTAINER_IMAGE_PASSWORD: $CI_REGISTRY_PASSWORD
QUARKUS_CONTAINER_IMAGE_REGISTRY: $CI_REGISTRY
QUARKUS_CONTAINER_IMAGE_GROUP: $CI_PROJECT_PATH
QUARKUS_CONTAINER_IMAGE_TAG: "latest"
FF_TIMESTAMPS: "true" # timestamps in the job logger for debugging
stages:
- build
- package
- execute
maven-build:
stage: build
tags: [cache] # gitlab caching best practices said to use tags for the gitlab runners
cache:
key:
files:
- pom.xml # invalidate cache when pom.xml updates, which keeps track of dependencies
paths:
- .m2/repository/
artifacts:
paths:
- target/
script:
- ./mvnw $MAVEN_OPTS $MAVEN_CLI_OPTS compile # added $MAVEN_OPTS to make sure the build output path is correct
maven-container-image-generation:
stage: package
tags: [cache] # gitlab caching best practices said to use tags for the gitlab runners
dependencies: [maven-build] # not necessary since there's only 1 job with an artifact so far
cache:
key:
files:
- pom.xml # invalidate cache when pom.xml updates, which keeps track of dependencies
paths:
- .m2/repository/
artifacts:
paths:
- target/
script:
- ./mvnw install $MAVEN_OPTS -D"quarkus.container-image.build=true" # added $MAVEN_OPTS to make sure the build output path is correct
run-game:
stage: execute
tags: [cache] # there are no dependencies to cache, but i kept the tag because i probably want to use the same instance runner anyway
dependencies: [maven-container-image-generation] # only getting the artifact from step 2, not 1
services:
- name: "$CI_REGISTRY_IMAGE/logic-service:latest"
alias: logic-service
image:
name: gitlab.stud.atlantis.ugent.be:5050/utils/docker/devops-runner:latest
entrypoint:
- ""
variables:
PLAYER_NAME: "CI/CD Player"
LOGIC_URL: "http://logic-service:8080"
TURN_INTERVAL_MS: 25
TURN_LIMIT: 100
script:
- java -cp /app/resources:/app/classes:/app/libs/* be.ugent.devops.gamehost.services.runner.LauncherKt
Questions about Lab 1
- What is ./mvnw and what is the advantage of using it above mvn?
./mvnw is the command used by the Maven Wrapper. The wrapper makes it easier to work together on a project without having to install Maven.
- 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?
For Maven dependencies I used caches. A cache will store dependencies between different pipelines, as long as they haven't changed. That means you don't have to download them all every time you run the same job with the same dependencies. This reduced my build times significantly. The downside is that they're stored on the runner and I can't easily view the caches themselves.
For the build files I used artifacts. Artifacts will store build outputs to later jobs in the same pipeline. I don't want build outputs to be cached because they change a lot between different pipelines. For each pipeline, I only want the build outputs of that pipeline with the correct version. The downside is that they're stored in Gitlab itself and not locally on the runner for faster retrieval.
- 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?
We need the JDK for Maven to be able to compile the code, but it is unnecessarily big for a runtime image. The JRE only contains what is needed to run the game, which means quicker download and installation, less storage usage, and less to go wrong.
Using the JDK for both would work, but the runtime image would be unnecessarily large. Using the JRE for both wouldn't work, the build would fail as it's lacking the compiler tools.