1. Overview


-
First we create a simple REST-endpoint with quarkus.
-
We create an uber-jar.
-
We build a docker image with the jar-file.
-
We push the docker image to an image registry (ghcr.io).
-
We deploy the docker image to minikube.
-
we create a gh-actions pipeline to automate the deployment to minikube.
2. Create the quarkus Project
mvn io.quarkus.platform:quarkus-maven-plugin:3.8.2:create \
-DprojectGroupId=at.htl.minikube \
-DprojectArtifactId=minikube-pvc \
-Dextensions='resteasy-reactive-jackson, smallrye-health'
result
[INFO] Scanning for projects... [INFO] [INFO] ------------------< org.apache.maven:standalone-pom >------------------- [INFO] Building Maven Stub Project (No POM) 1 [INFO] --------------------------------[ pom ]--------------------------------- [INFO] [INFO] --- quarkus:3.8.2:create (default-cli) @ standalone-pom --- [INFO] ----------- [INFO] selected extensions: - io.quarkus:quarkus-smallrye-health - io.quarkus:quarkus-resteasy-reactive-jackson [INFO] applying codestarts... [INFO] π java π¨ maven π¦ quarkus π config-properties π§ tooling-dockerfiles π§ tooling-maven-wrapper π resteasy-reactive-codestart π smallrye-health-codestart [INFO] ----------- [SUCCESS] β quarkus project has been successfully generated in: --> /Users/stuetz/work/2024-ph-seminar/_delete/minikube-pvc ----------- [INFO] [INFO] ======================================================================================== [INFO] Your new application has been created in /Users/stuetz/work/2024-ph-seminar/_delete/minikube-pvc [INFO] Navigate into this directory and launch your application with mvn quarkus:dev [INFO] Your application will be accessible on http://localhost:8080 [INFO] ======================================================================================== [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 0.909 s [INFO] Finished at: 2024-03-10T11:11:44+01:00 [INFO] ------------------------------------------------------------------------
4. Request the REST-Endpoints
-
Create a REST-Client
-
New folder in project-Root:
http-requests
-
create a new file in this folder:
requests.http
-
### hello
GET http://localhost:8080/hello
Content-Type: */*
### Liveness
GET http://localhost:8080/q/health/live
###

-
you can also use cURL:
curl -i http://localhost:8080/hello (1)
1 | -i shows the header of the response. |
result
HTTP/1.1 200 OK content-length: 28 Content-Type: text/plain;charset=UTF-8 Hello from RESTEasy Reactive%
6. Add Database
6.1. Add Dependencies
./mvnw quarkus:add-extension -Dextensions='hibernate-orm-panache, jdbc-postgresql'
result
[INFO] Scanning for projects... [INFO] [INFO] --------------------< at.htl.minikube:minikube-pvc >-------------------- [INFO] Building minikube-pvc 1.0.0-SNAPSHOT [INFO] from pom.xml [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- quarkus:3.8.2:add-extension (default-cli) @ minikube-pvc --- [INFO] Looking for the newly published extensions in registry.quarkus.io [INFO] [SUCCESS] β Extension io.quarkus:quarkus-hibernate-orm-panache has been installed [INFO] [SUCCESS] β Extension io.quarkus:quarkus-jdbc-postgresql has been installed [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 2.071 s [INFO] Finished at: 2024-03-10T12:36:22+01:00 [INFO] ------------------------------------------------------------------------
Dependencies in pom.xml
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm-panache</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-health</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
6.2. Configure the Database
Download files.zip, unzip it and copy the files into the project.

# datasource configuration
quarkus.datasource.db-kind = postgresql
quarkus.datasource.username = demo
quarkus.datasource.password = demo
quarkus.datasource.jdbc.url = jdbc:postgresql://localhost:5432/demo
%prod.quarkus.datasource.jdbc.url = jdbc:postgresql://postgres:5432/demo
# drop and create the database at startup (use `update` to only update the schema)
quarkus.hibernate-orm.database.generation=drop-and-create
quarkus.package.type=uber-jar
quarkus.hibernate-orm.sql-load-script=import.sql
INSERT INTO vehicle (brand, model) VALUES ('Opel', 'Kadett');
INSERT INTO vehicle (brand, model) VALUES ('VW', 'KΓ€fer 1400');
INSERT INTO vehicle (brand, model) VALUES ('Opel', 'Blitz');
version: '3.1'
services:
db:
container_name: postgres
image: postgres:15.2-alpine
restart: unless-stopped
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: app
POSTGRES_DB: db
ports:
- 5432:5432
volumes:
- ./db-postgres/db:/var/lib/postgresql/data
- ./db-postgres/import:/import
networks:
- postgres
networks:
postgres:
driver: bridge
7. Code Vehicle

package at.htl.minikube.entity;
// imports
@Entity
public class Vehicle {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String brand;
private String model;
//region constructors
public Vehicle() {
}
public Vehicle(String brand, String model) {
this.brand = brand;
this.model = model;
}
//endregion
// getter and setter
@Override
public String toString() {
return String.format("%d: %s %s", id, brand, model);
}
}
package at.htl.minikube.control;
import jakarta.ws.rs.ApplicationPath;
import jakarta.ws.rs.core.Application;
@ApplicationPath("api")
public class RestConfig extends Application {
}
package at.htl.minikube.control;
// imports
@ApplicationScoped
public class VehicleRepository implements PanacheRepository<Vehicle> {
}
package at.htl.minikube.boundary;
import at.htl.minikube.control.VehicleRepository;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.core.Response;
@Path("vehicle")
public class VehicleResource {
@Inject
VehicleRepository vehicleRepository;
@GET
public Response getAll() {
return Response.ok(
vehicleRepository.listAll()
).build();
}
}
9. Start minikube
minikube start
result
π minikube v1.32.0 on Darwin 14.3.1 (arm64) β¨ Automatically selected the docker driver π Using Docker Desktop driver with root privileges π Starting control plane node minikube in cluster minikube π Pulling base image ... πΎ Downloading Kubernetes v1.28.3 preload ... > preloaded-images-k8s-v18-v1...: 341.16 MiB / 341.16 MiB 100.00% 1.81 Mi > gcr.io/k8s-minikube/kicbase...: 410.57 MiB / 410.58 MiB 100.00% 1.36 Mi π₯ Creating docker container (CPUs=2, Memory=7793MB) ... π³ Preparing Kubernetes v1.28.3 on Docker 24.0.7 ... βͺ Generating certificates and keys ... βͺ Booting up control plane ... βͺ Configuring RBAC rules ... π Configuring bridge CNI (Container Networking Interface) ... π Verifying Kubernetes components... βͺ Using image gcr.io/k8s-minikube/storage-provisioner:v5 π Enabled addons: storage-provisioner, default-storageclass π Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default
-
Falls die Meldung erscheint, dass der Cluster veraltet ist, dann
minikube stop
undminikube delete
. Beim anschlieΓendenminikube start
wird ein Cluster mit aktueller kubernetes-Software erstellt. -
Check, in the "π Enabled addons:"-section, that metrics-server and dashboard are installed.
-
When missing:
minikube addons enable metrics-server minikube addons enable dashboard
-
minikube addons list |grep enabled
| dashboard | minikube | enabled β | Kubernetes | | default-storageclass | minikube | enabled β | Kubernetes | | metrics-server | minikube | enabled β | Kubernetes | | storage-provisioner | minikube | enabled β | minikube |
10. .jar File erstellen (uber-jar)
quarkus.package.type=uber-jar
./mvnw clean package
-
check, if the runner-jar is created

11. Create docker Image
-
Therefore, we need a
Dockerfile
. -
There are already Dockerfiles in
src/main/docker
- these are not needed and can be deleted (when not already done). -
Create a new Dockerfile in
src/main/docker
result
... βββ src βΒ Β βββ main βΒ Β βΒ Β βββ docker βΒ Β βΒ Β βΒ Β βββ Dockerfile ...
FROM eclipse-temurin:21-jre
RUN mkdir -p /opt/application
COPY *-runner.jar /opt/application/backend.jar
WORKDIR /opt/application
CMD [ "java", "-jar", "backend.jar" ]

12. Create build.sh
-
Before we made it manually, but now we use a script in the project root.
#!/usr/bin/env bash
mvn -B package
cp src/main/docker/Dockerfile target/
docker login ghcr.io -u $GITHUB_ACTOR -p $GITHUB_TOKEN
docker build --tag ghcr.io/$GITHUB_REPOSITORY/backend:latest ./target
docker push ghcr.io/$GITHUB_REPOSITORY/backend:latest
13. Config gh-actions - Pipeline

name: Build and Deploy Dockerfiles
run-name: ${{ github.actor }} is building Docker images π
on: [ push ]
jobs:
build-images:
permissions: write-all
runs-on: ubuntu-22.04
steps:
- name: Check out repository code
uses: actions/checkout@v4
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- run: |
pwd
ls -lah
working-directory: ./k8s
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '21'
cache: 'maven'
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build with Maven
run: ./build.sh
-
Make a commit and push it to the repository

15. Configure kubernetes Deployment
# Quarkus Application Server
apiVersion: apps/v1
kind: Deployment
metadata:
name: appsrv
spec:
replicas: 1
selector:
matchLabels:
app: appsrv
template:
metadata:
labels:
app: appsrv
spec:
containers:
- name: appsrv
image: ghcr.io/quarkus-seminar/2024-lab-minikube-pvc/backend:latest (1)
# remove this when stable. Currently we do not take care of version numbers
imagePullPolicy: Always
ports:
- containerPort: 8080
startupProbe:
httpGet:
path: /q/health
port: 8080
timeoutSeconds: 5
initialDelaySeconds: 15
readinessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /q/health
port: 8080
timeoutSeconds: 5
initialDelaySeconds: 60
periodSeconds: 120
---
apiVersion: v1
kind: Service
metadata:
name: appsrv
spec:
ports:
- port: 8080
targetPort: 8080
protocol: TCP
selector:
app: appsrv
1 | Check, that your image name is correct |
You could also generate this file with kubectl
kubectl create deployment appsrv --image=ghcr.io/htl-leonding/backend:latest --port=8080
deployment.apps/appsrv created
kubectl get deployments/appsrv -o yaml > appsrv.yaml
kubectl expose deployments/appsrv --port=8080
kubectl expose deployments/appsrv-depl --port=8080
16. Deploy to minikube the first time
kubectl apply -f k8s/postgres.yaml
kubectl apply -f k8s/appsrv.yaml
result
deployment.apps/appsrv created service/appsrv created
16.1. Check the Success
minikube dashboard
result
π€ Verifying dashboard health ... π Launching proxy ... π€ Verifying proxy health ... π Opening http://127.0.0.1:53209/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/ in your default browser...
-
The following site should be opened in your browser
-
if not just use
minikube --url
and copy the given url into your browser
-

-
We notice there are problems
17. Port Forward from minikube
kubectl port-forward appsrv-xxxxxx-xxxxx 8080:8080
Use kubectl-autocomplete for the appsrv |
result
β― kubectl port-forward appsrv-65cd45564f-8vlrm 8080:8080 Forwarding from 127.0.0.1:8080 -> 8080 Forwarding from [::1]:8080 -> 8080
18. Access the App
curl -i http://localhost:8080/api/vehicle
GET http://localhost:8080/api/vehicle HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 content-length: 126 [ { "id": 1, "brand": "Opel", "model": "Kadett" }, { "id": 2, "brand": "VW", "model": "KΓ€fer 1400" }, { "id": 3, "brand": "Opel", "model": "Blitz" } ] Response file saved. > 2024-03-13T062310.200.json Response code: 200 (OK); Time: 872ms (872 ms); Content length: 125 bytes (125 B)