]
----
.Mögliche Verwendung von FROM
[source,dockerfile]
----
FROM debian:13-jdk
FROM --platform=linux/arm64 debian:13-slim
FROM debian:latest
FROM --platform=linux/arm64 eclipse-temurin:20-jammy
----
INFO: openjdk ist bereits deprecated und kann nicht mehr verwendet werden
=== Labs
==== Aufgabe: Java-Programm ausführen
. Erstelle folgendes File in einem neuen Folder:
+
.src/HelloWorld.java
[source,java]
----
void main() {
IO.println("Hello World!");
}
----
+
IMPORTANT: Wir benutzen Java 25+ und keine historischen Java-Versionen.
** Ausführen in der Shell:
+
[source,shell]
----
java HelloWorld.java
----
oder
+
[source,shell]
----
javac HelloWorld.java
java HelloWorld
----
. Erstellen eines Dockerfiles
+
[source,dockerfile]
----
FROM debian:latest # <.>
RUN apt update && apt install -y openjdk-25-jdk # <.>
COPY ./src . # <.>
RUN java -version # <.>
RUN javac HelloWorld.java # <.>
CMD ["java", "HelloWorld"] # <.>
----
<.> Nur beim Entwickeln `:latest` verwenden
<.> Installiert das JDK 25
<.> Kopiert alle Files from Ordner ./src in den aktuellen Ordner im Image (root)
<.> Gibt die Java-Version aus (zur Ausgabe auf der Console verwenden wir `--no-cache --progress=plain`)
<.> kompilieren des Java-Quellcodes
<.> CMD ist der eine Befehl, weswegen der Container gestartet wird.
. Bauen des Images
+
[source,shell]
----
docker build --no-cache --progress=plain -t hello-world .
----
. Starten des Containers
+
[source,shell]
----
docker run --name hello-world hello-world:latest
----
.Ansehen des gestoppten Containers
[source,shell]
----
docker container ls -a
----
.Löschen des gestoppten Containers
[source,shell]
----
docker container rm hello-world
----
==== Verwendung eines kleineren Images
* Es wird nur das image gewechselt -> eclipse-temurin:25-alpine, da dieses bereits das JDK 25 enthält und somit die Installation des JDKs im Dockerfile entfällt. Außerdem ist alpine ein sehr kleines Image, was die Größe des finalen Images reduziert.
[source,dockerfile]
----
FROM eclipse-temurin:25-jdk-alpine-3.23 # <.>
COPY ./src .
CMD ["java", "HelloWorld.java"] # <.>
----
<.> Nun ist genau festgelegt
** welches JDK verwendet wird -> JDK 25 von https://adoptium.net/[Eclipse Temurin^]
** welches OS verwendet wird -> https://www.alpinelinux.org/about/[Alpine Linux 3.23^], welches sehr klein ist und somit die Größe des finalen Images reduziert
<.> Man muss eine Einklassen-Java-Applikation nicht (mehr) kompilieren, um lauffähig zu sein.
IMPORTANT: Das Bauen des Images ist sehr schnell, da nur der letzte Layer geändert wurde, da die vorherigen Layer gecached werden. Das ist ein großer Vorteil von Docker, da man so schnell Änderungen am Image vornehmen und testen kann, ohne jedes Mal das gesamte Image neu bauen zu müssen.
.Bauen des Images
[source,shell]
----
docker build -t hello-world .
----
.Starten des Containers
[source,shell]
----
docker run --rm \ # <.>
--name hello-world \
hello-world:latest
----
<.> rm ... remove -> der gestoppte Container wird automatisch gelöscht
==== Webseiten am nginx publishen
* Aufgabe:
** Erstellen Sie ein docker image, das untenstehende Webseite mit nginx hostet.
+
.index.html
[source,html]
----
HTL
My Personal Site
Lorem ipsum dolor sit amet, consectetur adipiscing elit
Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua
----
** Nennen Sie das Image `my-webserver` mit der Version `v1.0`.
** Starten Sie den Webserver am Port 8080
.Lösung
[%collapsible]
====
.Dockerfile
[source,dockerfile]
----
FROM debian:trixie-slim
LABEL maintainer="Max Mustermann "
RUN apt update && RUN apt install -y nginx
COPY index.html /var/www/html/index.html
EXPOSE 80
ENTRYPOINT ["/usr/sbin/nginx", "-g", "daemon off;"]
----
----
# build the image
docker build -t my-webserver:v1.0 .
# start the container
docker run --rm \
-p 8080:80 \
--name my-web my-webserver:v1.0
----
* Man würde eher ein fertiges nginx-image verwenden, als es zu bauen.
.Dockerfile
[source,dockerfile]
----
FROM nginx:1.29-alpine3.23-slim
LABEL maintainer="Max Mustermann "
COPY index.html /var/www/html/index.html
EXPOSE 80
ENTRYPOINT ["/usr/sbin/nginx", "-g", "daemon off;"]
----
IMPORTANT: Beachte, dass geänderte html-Verzeichnis. siehe auch https://betterstack.com/community/questions/what-is-default-public-www-location-in-nginx/[Nginx Default Public Www Location?^]
====
=== CMD
* Jeder Docker Container führt nur EIN Kommando aus:
** CMD oder
** ENTRYPOINT
* Alle .sh files die in `/docker-entrypoint.d` enthalten sind, werden automatisch beim Starten des Containers ausgeführt. [https://www.camptocamp.com/en/news-events/flexible-docker-entrypoints-scripts[source^]]
.Dockerfile
[source,dockerfile]
----
FROM ...
CMD ["tail", "-f", "/dev/null"]
----
* Dieses Kommando verhindert, dass der Docker Container beendet wird.
----
# Beispiel für einen Shell-Befehl
echo "Hello, World!"
----
=== Labs
==== Aufgabe: CMD vs. ENTRYPOINT
* Experimentieren Sie mit `CMD` und `ENTRYPOINT` und deren Zusammenspiel. Erstellen Sie ein Dockerfile, das die Unterschiede demonstriert.
== Docker Build Context
* `WORKDIR`: Festlegen des Arbeitsverzeichnisses
* `COPY`: Kopieren von Dateien und Verzeichnissen
* `ADD`: Erweiterte Kopierfunktionen (URLs, automatische Entpackung)
* Der `.dockerignore`-File: Ausschließen von Dateien aus dem Build-Kontext
image::docker-build-context.png[]
=== Labs
==== Quarkus/Python/Node.js Anwendung
* Erstellen Sie ein Dockerfile, das eine kleine Quarkus-, Python- oder Node.js-Anwendung in das Image kopiert und ausführt.
==== COPY vs. ADD
* Demonstrieren Sie den Unterschied zwischen `COPY` und `ADD` (z.B. mit TAR-Archiven oder URLs bei `ADD`).
==== .dockerignore
* Fügen Sie eine `.dockerignore`-Datei hinzu, um bestimmte Dateien vom Build-Kontext auszuschließen (z.B. `.git`, `node_modules`, `target`). Zeigen Sie, dass diese Dateien nicht im Image landen.
== Umgebungsvariablen und Ports
* `ENV`: Definieren von Umgebungsvariablen
* `EXPOSE`: Dokumentation von exposed Ports
=== Labs
==== Konfigurierbare Anwendung
. Erstellen Sie ein java-Programm, das durch environment-Variablen konfiguriert wird:
** Je nachdem welchen Wert die ENV-Variable `APP_COLOR` hat, soll die Ausgabe in dieser Farbe erfolgen (zB rot, grün, gelb, blau). Wenn die Variable nicht gesetzt ist, soll die Ausgabe ohne Farbgebung erfolgen.
+
image::lab-1020-java-result.png[]
+
.Lösung
[%collapsible]
====
.src/Main.java
[source,java]
----
void main(String[] args) {
// Direkter Zugriff auf die Docker-Umgebungsvariable
String colorEnv = System.getenv("APP_COLOR");
String colorCode;
if (colorEnv == null) {
colorCode = "\u001B[0m"; // Reset
} else {
switch (colorEnv.toUpperCase()) {
case "RED":
colorCode = "\u001B[41m";
break;
case "GREEN":
colorCode = "\u001B[42m";
break;
case "YELLOW":
colorCode = "\u001B[43m";
break;
case "BLUE":
colorCode = "\u001B[44m";
break;
default:
colorCode = "\u001B[0m";
}
}
IO.println(colorCode + " " + "\u001B[0m");
IO.println(colorCode + " HALLO AUS DER JAVA-DOCKER-WELT! " + "\u001B[0m");
IO.println(colorCode + " Farbe aus ENV: " + (colorEnv != null ? colorEnv : "Standard") + " ".repeat(22-colorEnv.length()) + "\u001B[0m");
IO.println(colorCode + " " + "\u001B[0m");
}
----
oder
.src/Main.java
[source,java]
----
void main() {
// Holen der Variable (Optional zur Sicherheit, falls null)
String colorEnv = System.getenv("APP_COLOR");
// Der Switch als Expression: Kompakt, sicher und liest sich fast wie Prosa
String colorCode = switch (colorEnv) {
case null -> "\u001B[0m";
case String s when s.equalsIgnoreCase("RED") -> "\u001B[41m";
case String s when s.equalsIgnoreCase("GREEN") -> "\u001B[42m";
case String s when s.equalsIgnoreCase("YELLOW") -> "\u001B[43m";
case String s when s.equalsIgnoreCase("BLUE") -> "\u001B[44m";
default -> "\u001B[0m";
};
String displayEnv = (colorEnv == null) ? "Standard" : colorEnv;
IO.println("""
%s \u001B[0m
%s HALLO AUS DER JAVA-DOCKER-WELT! \u001B[0m
%s Farbe aus ENV: %-20s \u001B[0m
%s \u001B[0m
""".formatted(colorCode, colorCode, colorCode, displayEnv, colorCode));
}
----
====
. Erstellen sie ein Dockerfile, das dieses Java-Programm in ein Image packt und die Umgebungsvariable `APP_COLOR` auf "RED" setzt.
+
.Lösung
[%collapsible]
====
.Dockerfile
[source,dockerfile]
----
FROM eclipse-temurin:25-jdk-alpine-3.23
COPY src/Main.java /app/Main.java
WORKDIR /app
ENV APP_COLOR=BLUE
CMD ["java", "Main.java"]
----
NOTE: Auch hier ersparen wir uns das Kompilieren, da es sich um eine Einklassen-Java-Applikation handelt. Bei komplexeren Java-Anwendungen würde man natürlich den Quellcode kompilieren und die .class-Dateien oder das .jar-File in das Image kopieren.
====
. Bauen Sie das Image
+
[source,shell]
----
docker build -t my-java-env-test:1.0 .
----
. Starten Sie den Container
+
[source,shell]
----
docker run --rm --name java-env-test my-java-env-test:1.0
----
+
image::lab-1020-java-result-2.png[]
. Überschreiben Sie die Farbe beim Starten des Containers, indem Sie die Umgebungsvariable `APP_COLOR` auf "GREEN" setzen.
+
[source,shell]
----
docker run --rm \
--name java-env-test \
-e APP_COLOR=GREEN \
my-java-env-test:1.0
----
+
image::lab-1020-java-result-3.png[]