1. Classroom-Demo

Falls du kein eigenes Quarkus-Projekt zur Verfügung hast oder dir die Einrichtung ersparen möchtest, kannst du auch das bereits vorbereitete Beispielprojekt verwenden.

Dieses Projekt ist vollständig konfiguriert:

  • Es basiert auf Quarkus und verwendet Maven.

  • Es ist vollständig mit Docker und Docker Compose ausgestattet – inklusive Datenbank und NGINX-Reverse-Proxy.

Das Projekt ist in meinem GitHub Repository unter dem Tutorial Demo-Branch verfügbar. Diesen kann man sich einfach als zip-Datei herunterladen, diese lokal entpacken und in ein eigenes Repo-hochladen.

1.1. Architekturdiagramm

NP5FJy904CNlyocUtBbuv63u0mA94bGq9iJ3sWxGK7TnPXhy4xwxNSicrSDStlkzvjTiNys9LMybNUIyAJpZVZ9Q3HRJcmcoqgbIoHk31KYntHdoGZYp8dWYbraGF2eBKGoTtvQCpk2h4iRJRGVV1BIAnNI UCPImiTdqm7ht o5W5DzV 5noFOi60LlhlOIJQCMSrSvsLUAmU6GMc1zJkDdWTF pUmIOsVkrIctDZ4TfaMy4r7yDNIxLsZCUtXWdojcgzKImkyLgn4rNQIIJ0 NGOvESasKKkfghZ4OTcfkxet5PQOu5hP3VjOYwhFFuvly0000

1.2. Datenmodell

PO z2eD048JxVOh1tXUm4jAdI9xWugrcuNxWTgy8cdUFIH05kvc5cM dLtF5QWmKU3HOHf7fQV1IU339YOYJYRtGN89JlUKS6pZ5i3dC18Zla6fyS5bDSf4xg1L94xttWhjLl fVNF w71wbg5sF3K1mfnD79 48sVzPbhRDyqOWwZdvxy0F

1.3. Endpoints

1.3.1. /api/classrooms

  1. GET

    • Returned eine Liste aller Klassen

Beispiel-Daten
[
  {
    "id": 1,
    "name": "5AHIF",
    "students": [
      {
        "id": 1,
        "firstName": "Alice",
        "lastName": "Müller",
        "email": "alice.ahif@example.com"
      },
      {
        "id": 2,
        "firstName": "Bob",
        "lastName": "Schmidt",
        "email": "bob.ahif@example.com"
      }
    ]
  }
]

1.3.2. /api/students

  1. GET

    • Returned eine Liste aller Schüler:innen

Beispiel-Daten
[
  {
    "id": 2,
    "firstName": "Bob",
    "lastName": "Schmidt",
    "email": "bob.ahif@example.com",
    "classRoom": {
      "id": 1,
      "name": "5AHIF"
    }
  },
  {
    "id": 3,
    "firstName": "Charlie",
    "lastName": "Weber",
    "email": "charlie.bhif@example.com",
    "classRoom": {
      "id": 2,
      "name": "5BHIF"
    }
  }
]

2. Docker & Docker-Compose

2.1. Classroom-Demo-Dockerfile

Dockerfile
(1)
FROM eclipse-temurin:24-jre

(2)
RUN mkdir -p /opt/application

(3)
COPY *-runner.jar /opt/application/backend.jar

COPY import.sql /opt/application/import.sql

(4)
WORKDIR /opt/application

(5)
CMD [ "java", "-jar", "backend.jar" ]
1 Dies gibt das Basis-Image für den Container an.In diesem Fall handelt es sich um ein JDK 24-Image von Eclipse Temurin.
2 Der RUN-Befehl erstellt ein Verzeichnis /opt/application im Container, das als Ziel für die Anwendung und deren Dateien dient.
3 Der COPY-Befehl kopiert Dateien aus dem Build-Context in das Container-Dateisystem.Hier wird die JAR-Datei als backend.jar und die SQL-Datei als import.sql ins Verzeichnis /opt/application kopiert.
4 Der WORKDIR-Befehl legt das Arbeitsverzeichnis für alle folgenden Befehle auf /opt/application fest.
5 Der CMD-Befehl definiert den Standardbefehl, der beim Starten des Containers ausgeführt wird.In diesem Fall wird die Java-Anwendung backend.jar ausgeführt.

2.2. H2-Dockerfile

Dockerfile
FROM eclipse-temurin:24-jre

(1)
ENV DEBIAN_FRONTEND=noninteractive

(2)
RUN apt update && apt install -y \
    curl \
    bash \
    && rm -rf /var/lib/apt/lists/*

(3)
COPY ./download-h2.sh ./

(4)
RUN chmod +x ./*.sh

(5)
RUN bash ./download-h2.sh

(6)
CMD ["bash", "./h2-server-start.sh"]
1 Setzt eine Umgebungsvariable DEBIAN_FRONTEND=noninteractive, um die interaktive Installation von Paketen zu vermeiden.
2 Der RUN-Befehl aktualisiert die Paketquellen, installiert die Pakete curl und bash und entfernt anschließend unnötige Cache-Dateien, um das Image schlanker zu halten.
3 Der COPY-Befehl kopiert das Skript download-h2.sh in das Container-Dateisystem.
4 Der RUN-Befehl setzt Ausführungsrechte auf das kopierte Shell-Skript (.sh), sodass es später ausgeführt werden kann.
5 Der RUN-Befehl führt das Skript download-h2.sh aus, um die benötigten Dateien herunterzuladen oder die H2-Datenbank zu konfigurieren.
6 Der CMD-Befehl legt den Standardbefehl fest, der beim Starten des Containers ausgeführt wird.In diesem Fall startet er den H2-Server mit dem Skript h2-server-start.sh.
Bash muss für die Ausführung der Skripte verwendet werden, da es ansonsten zu Problemen im GitHub Runner kommen kann.

2.3. Docker-Compose

docker-compose.yaml
services:
  (1)
  backend:
    container_name: classroom-backend
    image: ${CLASSROOM_IMAGE}
    ports:
      - 8080:8080
    depends_on:
      - h2-db
    environment:
      - QUARKUS_DATASOURCE_JDBC_URL=jdbc:h2:tcp://h2-db:9092/db

  (2)
  h2-db:
    container_name: classroom-db
    image: ${H2_IMAGE}
    ports:
      - 9092:9092
      - 19999:19999
    volumes:
      - h2_data:/opt/h2-data
    healthcheck:
      test: ["CMD", "nc", "-z", "localhost", "9092"]
      interval: 10s
      retries: 5
      start_period: 30s
      timeout: 10s

(3)
volumes:
  h2_data:
    driver: local
1 Der backend-Dienst stellt die Anwendung dar.
  • Der Dienst nutzt ein Docker-Image, dessen Name über die Umgebungsvariable CLASSROOM_IMAGE gesetzt wird.

  • Der Dienst hört auf Port 8080 und hängt von der H2-Datenbank (h2-db) ab.

  • Die Umgebungsvariable QUARKUS_DATASOURCE_JDBC_URL wird gesetzt, um die JDBC-URL für die Verbindung zur Datenbank zu konfigurieren.

2 Der h2-db-Dienst stellt die H2-Datenbank dar.
  • Der Dienst verwendet ein Docker-Image, dessen Name über die Umgebungsvariable H2_IMAGE gesetzt wird.

  • Er legt die Ports 9092 (für die Verbindung zur Datenbank) und 19999 (für den H2-Console-Access) frei.

  • Die Datenbankdaten werden in einem Volume namens h2_data gespeichert, um die Daten persistent zu machen.

  • Ein Healthcheck überprüft alle 10 Sekunden, ob der H2-Server auf Port 9092 erreichbar ist.

3 Das volumes-Segment definiert ein Volumen namens h2_data, das den lokalen Treiber verwendet, um sicherzustellen, dass die H2-Datenbankdaten persistent sind, auch wenn der Container gestoppt oder gelöscht wird.

2.3.1. .env-Files

Die .env.dev und .env.prod Dateien werden verwendet, um Umgebungsvariablen für Docker-Compose festzulegen.

Durch die Verwendung von .env-Files können Entwickler schnell zwischen verschiedenen Umgebungen (z.B. Test- und Entwicklungsumgebung) wechseln.

env.dev
H2_IMAGE=classroom-db:latest
CLASSROOM_IMAGE=classroom-backend:latest

Das Docker-Compose-Deployment kann mit dem folgenden Befehl, der auf das jeweilige .env-File verweist, gestartet werden:

docker-compose --env-file .env.dev up --build

3. Nginx

nginx.conf
(1)
user www-data;
(2)
worker_processes auto;
(3)
pid /run/nginx.pid;

events {}

http {
    include /etc/nginx/mime.types;

    (4)
    server {
        listen 80;
        server_name $SSH_HOST;

        (5)
        location / {
            root /var/www/html;
            index index.html;
        }

        (6)
        location /api/ {
            proxy_pass http://127.0.0.1:8080;
            proxy_http_version 1.1;
            client_max_body_size 100M;
            proxy_read_timeout 600s;
        }
    }
}
1 Dieser Block definiert den Benutzer, unter dem NGINX ausgeführt wird. Der Benutzer www-data wird in der Regel für Webserver verwendet, um die Sicherheitsrichtlinien zu speichern.
2 Der worker_processes-Befehl lässt NGINX die Anzahl der Arbeitsprozesse basierend auf der Anzahl der CPU-Kerne automatisch festlegen.
3 Der pid-Befehl gibt den Pfad zur Prozess-ID-Datei an, die von NGINX verwendet wird.
4 Der server-Block definiert den Webserver, der auf Port 80 hört und den Hostnamen von der Umgebungsvariable $SSH_HOST verwendet.
  • Dies wird nicht von nginx.conf unterstützt, aber mithilfe des folgenden Commands kann man Environment variablen von Linux in die Konfiguration einfügen:

export SSH_HOST=<SSH_HOST>;

envsubst </etc/nginx/nginx.conf.template;

sudo tee /etc/nginx/nginx.conf
5 Der location /-Block definiert die Standardseite, die als index.html von NGINX bereitgestellt wird.
6 Der location /api/-Block leitet alle API-Anfragen an einen lokal laufenden Quarkus-Server weiter, der auf http://127.0.0.1:8080 hört.
  • Es wird sichergestellt, dass die Pfade bei der Weiterleitung nicht verändert werden.

  • Weiterhin wird die maximale Körpergröße auf 100 MB gesetzt.

  • Die Lesezeitüberschreitung auf 600 Sekunden erhöht.