Containerisieren einer Java-App

Abgeschlossen

In dieser Einheit containern Sie eine Java-Anwendung.

Wie bereits erwähnt, werden Container direkt über dem Hostbetriebssystem, Kernel und Hardware als gewöhnliche Systemprozesse ausgeführt. Container benötigen weniger Systemressourcen, was zu einem geringeren Speicherbedarf, weniger Aufwand und schnelleren Startzeiten der Anwendung führt. Diese Vorteile sind großartige Anwendungsfälle für die Skalierung bei Bedarf.

Es gibt Windows-Container und Linux-Container. In diesem Modul verwenden Sie die weit verbreitete Docker-Laufzeit, um ein Linux-Containerimage zu erstellen. Anschließend stellen Sie das Linux-Containerimage auf dem Hostbetriebssystem Ihres lokalen Computers bereit. Schließlich stellen Sie das Linux-Containerimage für Azure Kubernetes Service bereit.

Docker-Übersicht

Die Docker-Laufzeit wird zum Erstellen, Abrufen, Ausführen und Pushen von Container-Images verwendet, wie im folgenden Diagramm dargestellt.

Diagramm mit Docker-Befehlen.

In der folgenden Tabelle werden die einzelnen Docker-Befehle beschrieben:

Docker-Befehl BESCHREIBUNG
docker build Erstellt ein Containerimage, das aus den Anweisungen oder Ebenen besteht, die für Docker erforderlich sind, um einen ausgeführten Container aus einem Image zu erstellen. Das Ergebnis dieses Befehls ist ein Bild.
docker pull Container werden aus Images initialisiert, die aus Registrierungen wie azure Container Registry abgerufen werden. Diese Registrierung ist die Quelle, aus der Azure Kubernetes Service Inhalte abruft. Das Ergebnis dieses Befehls ist ein Netzwerk-Pull eines Bilds, das in Azure auftritt. Optional können Sie Bilder lokal abrufen. Diese Option ist üblich, wenn Sie Bilder erstellen, die Abhängigkeiten oder Ebenen erfordern, die ihre Anwendung möglicherweise benötigt, z. B. einen Anwendungsserver.
docker run Eine ausgeführte Instanz eines Images ist ein Container, und dieser Befehl führt alle Ebenen aus, die zum Ausführen und Interagieren mit der ausgeführten Containeranwendung erforderlich sind. Das Ergebnis dieses Befehls ist ein ausgeführter Anwendungsprozess auf dem Hostbetriebssystem.
docker push Die Azure Container Registry speichert die Images, sodass sie für Azure-Bereitstellungen leicht verfügbar und für Skalierungsvorgänge netzwerknah sind.

Klonen der Java-Anwendung

Klonen Sie zuerst das Repository des Flugbuchungssystems für Flugreservierungen und navigieren Sie zum Projektordner der Webanwendung Fluggesellschaften.

Hinweis

Wenn die Erstellung des Azure Kubernetes-Diensts auf ihrer CLI-Registerkarte abgeschlossen ist, verwenden Sie diese Registerkarte. Wenn sie noch ausgeführt wird, öffnen Sie eine neue Registerkarte, und navigieren Sie zu dem Ort, an dem Sie das Flugbuchungssystem für Flugreservierungen klonen möchten.

Führen Sie die folgenden Befehle aus:

git clone https://github.com/Azure-Samples/containerize-and-deploy-Java-app-to-Azure.git
cd containerize-and-deploy-Java-app-to-Azure/Project/Airlines

Wenn Sie Java und Maven installiert haben, können Sie optional den folgenden Befehl in Ihrer Terminalkonsole ausführen, um ein Gefühl der Erfahrung beim Erstellen der Anwendung ohne Docker zu erhalten. Wenn Sie Java und Maven nicht installiert haben, können Sie sicher mit dem nächsten Abschnitt fortfahren, erstellen Sie eine Docker-Datei. In diesem Abschnitt verwenden Sie Docker, um Java und Maven herunterzuziehen, um die Builds in Ihrem Auftrag auszuführen.

mvn clean package

Hinweis

Wir haben den mvn clean package-Befehl verwendet, um die betrieblichen Herausforderungen bei der Nichtverwendung von Docker-Multistage-Builds zu veranschaulichen, die wir als Nächstes behandeln. Auch hier ist dieser Schritt optional. Auf beide Weise können Sie sicher fortfahren, ohne den Maven-Befehl auszuführen.

Wenn der Vorgang erfolgreich war, hat Maven erfolgreich das Flugbuchungssystem für das Webanwendungsarchiv für Fluglinienreservierungen AirlinesReservationSample-0.0.1-SNAPSHOT.war erstellt, wie in der folgenden Ausgabe gezeigt:

[INFO] Building war: $PROJECT_PATH/containerize-and-deploy-Java-app-to-Azure/Project/Airlines/target/AirlinesReservationSample-0.0.1-SNAPSHOT.war
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  11.776 s
[INFO] Finished at: 2024-11-15T09:33:26+09:00
[INFO] ------------------------------------------------------------------------

Stellen Sie sich vor, Sie sind Java-Entwickler und haben soeben AirlinesReservationSample-0.0.1-SNAPSHOT.war erstellt. Ihr nächster Schritt besteht wahrscheinlich darin, mit den Betriebstechnikern zusammenzuarbeiten, um dieses Artefakt entweder auf einem lokalen Server oder einem virtuellen Computer bereitgestellt zu bekommen. Damit die Anwendung erfolgreich gestartet und ausgeführt werden kann, müssen die Server und virtuellen Computer mit den erforderlichen Abhängigkeiten verfügbar und konfiguriert sein. Dieser Prozess ist schwierig und zeitaufwändig, insbesondere bei Bedarf, wenn eine erhöhte Last auf Ihre Anwendung trifft. Mit Containern werden diese Herausforderungen gemildert.

Erstellen einer Dockerfile-Datei

Sie können jetzt eine Dockerfile-Datei erstellen. Eine Dockerfile ist ein Textdokument, das alle Befehle enthält, die ein Benutzer in der Befehlszeile ausführen würde, um ein Containerimage zusammenzustellen. Jedes Bild ist eine Ebene, die zur Effizienz zwischengespeichert werden kann. Die Ebenen bauen aufeinander auf.

Beispielsweise muss das Flugbuchungssystem für Flugreservierungen innerhalb eines Anwendungsservers bereitgestellt und ausgeführt werden. Ein Anwendungsserver wird nicht innerhalb des AirlinesReservationSample-0.0.1-SNAPSHOT.war verpackt. Es handelt sich um eine externe Abhängigkeit, die für die Ausführung von AirlinesReservationSample-0.0.1-SNAPSHOT.war erforderlich ist, um HTTP-Anforderungen zu überwachen und zu verarbeiten, Benutzersitzungen zu verwalten und Flight-Buchungen zu erleichtern. Wenn Sie eine herkömmliche, nicht containerisierte Bereitstellung verwendet haben, würden Betriebstechniker einen Anwendungsserver auf einem physischen Server oder virtuellen Computer installieren und konfigurieren, bevor Sie die AirlinesReservationSample-0.0.1-SNAPSHOT.war für sie bereitstellen. Diese Betriebsingenieure müssten auch sicherstellen, dass das JDK, das auf Ihrem Computer verwendet wird - was mvn clean package zum Kompilieren der WAR-Datei verwendet wird - tatsächlich demselben JRE entspricht, der vom Anwendungsserver verwendet wird. Die Verwaltung dieser Abhängigkeiten ist schwierig und zeitaufwändig.

Mit einem Dockerfile können Sie die Anweisungen oder Schichten schreiben, die erforderlich sind, um dieses Ziel automatisch zu erreichen. Dabei schichten Sie die Schritte aufeinander, die erforderlich sind, um sicherzustellen, dass das Flugbuchungssystem für Flugreservierungen alle Abhängigkeiten aufweist, die für die Bereitstellung in der Docker-Container-Runtime benötigt werden. Diese Lösung ist überzeugend, wenn Sie mit bedarfsorientierter Skalierung in ungeplanten Intervallen umgehen. Jede Ebene verwendet Docker-Cache, der den Status des Containerimages an jedem Anweisungsmeilenstein enthält, wodurch die Berechnungszeit optimiert und wiederverwendet wird. Wenn sich eine Ebene nicht ändert, werden zwischengespeicherte Ebenen verwendet. Häufige Anwendungsfälle für zwischengespeicherte Ebenen sind die Java-Runtime, der Anwendungsserver und andere Abhängigkeiten für das Flight Booking System für airline Reservations-Webanwendung. Wenn und wenn sich eine Version auf einer zuvor zwischengespeicherten Ebene ändert, wird ein neuer zwischengespeicherter Eintrag erstellt.

Das folgende Diagramm zeigt die Ebenen eines Containerimages. Wenn die Befehle in der Dockerfile-Datei ausgeführt werden, werden die Ebenen erstellt. Die oberste Ebene ist das Lese-/Schreib-Flight Booking System für die Webanwendungsebene "Airline Reservations". Diese Ebene wird zusätzlich zu den vorherigen schreibgeschützten Ebenen aufgebaut.

Diagramm, das die Docker-Ebenen zeigt.

Docker verfügt über das Konzept von Multistage-Builds, einem Feature, mit dem Sie ein kleineres Containerimage mit besserer Zwischenspeicherung und einem kleineren Sicherheitsbedarf erstellen können, was eine höhere Optimierung und Wartung der Dockerfile im Laufe der Zeit ermöglicht. Sie können z. B. die Containerbuildphase zum Kompilieren und Erstellen der Anwendung von der Phase zum Ausführen der Anwendung trennen. Mit diesem Feature können Sie nur die Während des Builds generierten Artefakte in das Produktionscontainerimage kopieren, wodurch der Speicherbedarf reduziert wird. Da Containerimages zwischengespeichert werden, wenn keine Änderungen vorhanden sind, können die zwischengespeicherten Images wiederverwendet werden, wodurch die Kosten und die Zeit für den Download aus dem Netzwerk reduziert werden.

Dienste, die in der Produktionsumgebung verfügbar gemacht werden, müssen für die Sicherheit sorgfältig verwaltet werden. Daher verwendet und betreibt die Produktionsumgebung ein sicheres Containerimage. Im Beispiel wird das CBL-Mariner von Microsoft bereitgestellte Bild verwendet.

CBL-Mariner Linux ist ein einfaches Betriebssystem, das nur die Pakete enthält, die für eine Cloudumgebung erforderlich sind. Sie können sie über benutzerdefinierte Pakete und Tools an die Anforderungen Ihrer Anwendung anpassen. CBL-Mariner wird Azure-Validierungstests unterzogen und ist mit Azure-Agents kompatibel. Microsoft erstellt und testet CBL-Mariner, um verschiedene Anwendungsfälle zu nutzen, von Azure-Diensten bis hin zur Energieversorgung der IoT-Infrastruktur. Es ist die intern empfohlene Linux-Verteilung für die Verwendung mit Microsoft-Clouddiensten und verwandten Produkten.

Hinweis

Microsoft stellt Containerimages bereit, die mit OpenJDK gebündelt sind, einschließlich Ubuntu, CBL-Marinerund distroless Images. Das distroless Bild hat die kleinste Bildgröße, aber das Ausführen von Tomcat ist eine Herausforderung. Um ein einfaches Design zu erzielen, entfernt das distroless Bild viele Befehle und Tools, einschließlich der Shell, was bedeutet, dass Sie catalina.sh nicht aufrufen können, um Tomcat zu starten. Das distroless Bild eignet sich für die Ausführung ausführbarer JARs, wie z. B. bei Spring Boot oder Quarkus.

Im folgenden Beispiel wird die gleiche Version von Microsoft Build of OpenJDK sowohl in der Buildphase als auch in der endgültigen Phase verwendet. Mit diesem Ansatz wird sichergestellt, dass Sie den Quellcode mit derselben Version des JDK erstellen, die von der Dienstbereitstellung tomcat verwendet wird, wodurch unerwartetes Verhalten aufgrund von Versionskonflikten vermieden wird.

Die folgende Abbildung zeigt den Multistage-Build und die Ereignisse in jeder Phase basierend auf den befehlen, die in der Dockerfile-Datei angegeben sind:

Diagramm mit dem Multistage-Build von Docker.

In Phase 0 wird Tomcat heruntergeladen und in ein Verzeichnis extrahiert, das durch eine Umgebungsvariable auf einem Ubuntu-Image angegeben wird. Die TOMCAT_VERSION Variable gibt die Version von Tomcat an, die heruntergeladen werden soll. Wenn eine neue Version von Tomcat veröffentlicht wird, sollten Sie die Versionsnummer aktualisieren, da ein neues Image nur abgerufen wird, wenn sich die Versionsnummer ändert. Andernfalls wird das zwischengespeicherte Bild verwendet. Der heruntergeladene Tomcat wird zur Verwendung in die Endumgebung kopiert.

In Phase 1 wird Maven auf einem Ubuntu-Image installiert, und die erstellten Quellcode- und Konfigurationsdateien werden vor dem Erstellen des Maven-Projekts kopiert. Jede Ebene wird zwischengespeichert, sodass das Betriebssystem-Image und die Maven-Image-Ebenen den Cache nutzen. Wenn Konfigurationsdateien, Quellcodedateien oder das Webverzeichnis aktualisiert werden, werden die Ebenen aus den Änderungen neu erstellt. Wenn der Build während der Kompilierung erfolgreich abgeschlossen wird, wird ein Artefakt namens AirlinesReservationSample-0.0.1-SNAPSHOT.war unter dem target-Verzeichnis generiert. Dieses Artefakt wird zur Verwendung in die Endumgebungsphase kopiert.

In der letzten Phase wird das von Microsoft bereitgestellte sichere CBL-Mariner Bild verwendet, um die Tomcat- bzw. Java-Buildartefakte aus Phase 0 bzw. Phase 1 zu kopieren. Ein Benutzer mit dem Namen app besitzt alle Dateien, die im Projekt verwendet werden, und die Anwendung wird auch als app Benutzer ausgeführt, statt mit root-Berechtigungen. Mit diesem Setup wird sichergestellt, dass das Containerimage sicher betrieben werden kann, ohne unnötige Berechtigungen zu erteilen. Schließlich wird die Portnummer 8080 verfügbar gemacht, und das skript catalina.sh wird ausgeführt, um Tomcat zu starten. Wenn dies auf Ihrem lokalen Docker Desktop ausgeführt wird, können Sie über die URL http://localhost:8080/AirlinesReservationSampledarauf zugreifen.

Verwenden Sie im Stammordner Ihres Projekts containerize-and-deploy-Java-app-to-Azure/Project/Airlines den folgenden Befehl, um eine Datei namens Dockerfile zu erstellen:

vi Dockerfile

Fügen Sie dem Dockerfile die folgenden Inhalte hinzu, und speichern und schließen Sie die Datei dann. Zum Speichern und Beenden drücken Sie ESC, geben Sie :wq! ein, und drücken Sie dann die EINGABETASTE.

############################################
# Tomcat Intall stage
############################################
FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS tomcat

ENV CATALINA_HOME=/usr/local/tomcat

# Configure Tomcat Version (Be sure to use the latest version)
ENV TOMCAT_VERSION=10.1.33

# Install Tomcat and required packages
RUN apt-get update ; \
    apt-get install -y curl ; \
    curl -O https://downloads.apache.org/tomcat/tomcat-10/v${TOMCAT_VERSION}/bin/apache-tomcat-${TOMCAT_VERSION}.tar.gz ; \
    tar xzf apache-tomcat-${TOMCAT_VERSION}.tar.gz ; \
    mv apache-tomcat-${TOMCAT_VERSION} ${CATALINA_HOME} ; \
    rm apache-tomcat-${TOMCAT_VERSION}.tar.gz && \
    apt-get remove --purge -y curl && \
    apt-get autoremove -y && \
    apt-get clean

############################################
# Build stage (Compiles with Java 17)
############################################
FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS build

WORKDIR /build

# Install Maven
RUN apt-get update && apt-get install -y maven && mvn --version

# Copy source code
COPY pom.xml .
COPY src ./src
COPY web ./web

# Build the project
RUN mvn clean package

############################################
# Package final stage
############################################
FROM mcr.microsoft.com/openjdk/jdk:17-mariner

# Configure the location of the Tomcat installation
ENV CATALINA_HOME=/usr/local/tomcat
# Configure the path to the Tomcat binaries
ENV PATH=$CATALINA_HOME/bin:$PATH

# This is the user that runs the Tomcat process
USER app

# Copy the Tomcat installation from the Tomcat stage
COPY --chown=app:app --from=tomcat ${CATALINA_HOME} ${CATALINA_HOME}
# Copy the Tomcat configuration files
COPY --chown=app:app tomcat-users.xml ${CATALINA_HOME}/conf
# Copy the compiled WAR file from the build stage
COPY --chown=app:app --from=build /build/target/*.war ${CATALINA_HOME}/webapps/AirlinesReservationSample.war

# Expose the default Tomcat port
EXPOSE 8080
# Start Tomcat
CMD ["catalina.sh", "run"]

Hinweis

Optional können Sie die Dockerfile_Solution-Datei im Stammverzeichnis Ihres Projekts verwenden, die die benötigten Inhalte enthält.

Die Dockerfile-Datei ist in drei Phasen unterteilt, die in den folgenden Tabellen beschrieben werden:

  • Die Tomcat-Installationsstufe:

    Docker-Befehl BESCHREIBUNG
    FROM FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS tomcat legt das Basisimage auf Microsoft Build of OpenJDK 17 unter Ubuntu fest und benennt diese Phase tomcat. Hier wird Tomcat installiert.
    ENV ENV CATALINA_HOME=/usr/local/tomcat legt eine Umgebungsvariable für das Installationsverzeichnis von Tomcat fest.
    ENV ENV TOMCAT_VERSION=10.1.33 legt die Version von Tomcat fest, die installiert werden soll. Dies sollte bei Bedarf auf die neueste Version aktualisiert werden.
    RUN Der RUN Befehl aktualisiert die Paketliste, installiert curl, lädt die angegebene Version von Tomcat herunter, extrahiert sie, verschiebt sie in das angegebene Verzeichnis und bereinigt unnötige Dateien und Pakete. Dadurch wird sichergestellt, dass das Bild leicht bleibt.
  • Die Buildphase, die mit Java 17 kompiliert wird:

    Docker-Befehl BESCHREIBUNG
    FROM FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS build legt das Basisimage auf Microsoft Build of OpenJDK 17 unter Ubuntu fest und benennt diese Phase build. Diese Phase wird zum Kompilieren der Java-Anwendung verwendet.
    WORKDIR WORKDIR /build legt das Arbeitsverzeichnis im Container auf /build, auf das der Quellcode kopiert und kompiliert wird.
    RUN RUN apt-get update && apt-get install -y maven && mvn --version installiert Maven, ein Für Java-Projekte verwendete Buildautomatisierungstool, und überprüft seine Installation.
    COPY COPY pom.xml . kopiert die Maven-Konfigurationsdatei in das Arbeitsverzeichnis. Diese Datei ist für die Erstellung des Projekts unerlässlich.
    COPY COPY src ./src kopiert das Quellcodeverzeichnis in den Container. Hier befindet sich der Java-Anwendungscode.
    COPY COPY web ./web kopiert das Webressourcenverzeichnis in den Container. Dazu gehören Webanwendungsressourcen, die für den Build erforderlich sind.
    RUN RUN mvn clean package führt den Maven-Buildprozess aus, der die Java-Anwendung kompiliert und in eine WAR-Datei verpackt.
  • Die letzte Phase des Pakets:

    Docker-Befehl BESCHREIBUNG
    FROM FROM mcr.microsoft.com/openjdk/jdk:17-mariner legt das Basisimage auf Microsoft Build of OpenJDK 17 on CBL-Marinerfest, das für die endgültige Bereitstellung der Anwendung verwendet wird.
    ENV ENV CATALINA_HOME=/usr/local/tomcat legt die Umgebungsvariable für das Installationsverzeichnis von Tomcat fest, ähnlich der Installationsstufe.
    ENV ENV PATH=$CATALINA_HOME/bin:$PATH fügt das Verzeichnis "Tomcat bin" zum System PATHhinzu, sodass Tomcat-Befehle problemlos ausgeführt werden können.
    USER USER app Gibt den Benutzer an, unter dem der Tomcat-Prozess ausgeführt wird, wodurch die Sicherheit verbessert wird, indem er nicht als Stammbenutzer ausgeführt wird.
    COPY COPY --chown=app:app --from=tomcat ${CATALINA_HOME} ${CATALINA_HOME} kopiert die Tomcat-Installation aus der tomcat Phase und legt den Besitz auf den app Benutzer fest.
    COPY COPY --chown=app:app tomcat-users.xml ${CATALINA_HOME}/conf kopiert die Tomcat-Benutzerkonfigurationsdatei in den Container und legt den Besitz auf den app Benutzer fest.
    COPY COPY --chown=app:app --from=build /build/target/*.war ${CATALINA_HOME}/webapps/AirlinesReservationSample.war kopiert die kompilierte WAR-Datei aus der build Phase in das Tomcat-Webapps-Verzeichnis , wobei der Besitz auf den app Benutzer festgelegt wird.
    EXPOSE EXPOSE 8080 macht Port 8080 verfügbar, der Standardport für Tomcat, der externen Zugriff auf die Anwendung zulässt.
    CMD CMD ["catalina.sh", "run"] Gibt den Befehl an, um Tomcat zu starten, wenn der Container ausgeführt wird.

Weitere Informationen zur Dockerfile-Konstruktion finden Sie in der Dockerfile-Referenz.