Andocken einer Spring Boot-Anwendung

1. Übersicht

In diesem Artikel konzentrieren wir uns darauf, wie Sie eine Spring Boot-Anwendung andocken , um sie in einer isolierten Umgebung, auch bekannt als Container , auszuführen .

Darüber hinaus zeigen wir, wie Sie eine Zusammensetzung von Containern erstellen, die voneinander abhängig sind und in einem virtuellen privaten Netzwerk miteinander verknüpft sind. Wir werden auch sehen, wie sie zusammen mit einzelnen Befehlen verwaltet werden können.

Beginnen wir mit der Erstellung eines Java-fähigen, leichtgewichtigen Basisimages unter Alpine Linux .

2. Buildpacks-Unterstützung in Spring Boot 2.3

Spring Boot 2.3 hat Unterstützung für Buildpacks hinzugefügt . Einfach ausgedrückt, anstatt unser eigenes Dockerfile zu erstellen und es mit Docker Build zu erstellen , müssen wir nur den folgenden Befehl eingeben :

$ ./mvnw spring-boot:build-image

Oder in Gradle:

$ ./gradlew bootBuildImage

Die Hauptmotivation für Buildpacks besteht darin, dasselbe Bereitstellungserlebnis zu schaffen, das einige bekannte Cloud-Dienste wie Heroku oder Cloud Foundry für eine Weile bieten . Wir führen nur das Build-Image- Ziel aus und die Plattform selbst kümmert sich um das Erstellen und Bereitstellen des Artefakts.

Darüber hinaus kann es uns helfen, die Art und Weise, wie wir Docker-Images erstellen, effektiver zu ändern. Anstatt dieselbe Änderung auf viele Docker-Dateien in verschiedenen Projekten anzuwenden, müssen wir lediglich den Image Builder der Buildpacks ändern oder optimieren.

Neben der Benutzerfreundlichkeit und der insgesamt besseren Entwicklererfahrung kann es auch effizienter sein . Der Buildpacks-Ansatz erstellt beispielsweise ein Docker-Image mit mehreren Ebenen und verwendet die explodierte Version der Jar-Datei.

3. Allgemeines Basisbild

Wir werden unter Verwendung von Docker der eigenen Build-Datei - Format: eine Dockerfile .

Eine Docker-Datei ist im Prinzip eine zeilenweise Batch-Datei, die Befehle zum Erstellen eines Images enthält. Es ist nicht unbedingt erforderlich, diese Befehle in eine Datei einzufügen, da wir sie auch an die Befehlszeile übergeben können - eine Datei ist einfach bequemer.

Schreiben wir also unser erstes Dockerfile :

FROM alpine:edge MAINTAINER baeldung.com RUN apk add --no-cache openjdk8 COPY files/UnlimitedJCEPolicyJDK8/* \ /usr/lib/jvm/java-1.8-openjdk/jre/lib/security/
  • FROM : Das Schlüsselwort FROM weist Docker an, ein bestimmtes Bild mit seinem Tag als Build-Basis zu verwenden. Befindet sich dieses Image nicht in der lokalen Bibliothek, wird eine Online-Suche in DockerHub oder einer anderen konfigurierten Remote-Registrierung durchgeführt
  • MAINTAINER : Ein MAINTAINER ist normalerweise eine E-Mail-Adresse, die den Autor eines Bildes identifiziert
  • RUN : Mit dem Befehl RUN führen wir eine Shell-Befehlszeile im Zielsystem aus. Hier verwenden wir den Paketmanager apk von Alpine Linux , um das Java 8 OpenJDK zu installieren
  • COPY : Der letzte Befehl sagt Docker zu COPY ein paar Dateien aus dem lokalen Dateisystem, speziell einen Unterordner mit dem Build - Verzeichnis, in das Bild in einem bestimmten Pfad

ANFORDERUNGEN: Um das Lernprogramm erfolgreich ausführen zu können, müssen Sie die JCE-Richtliniendateien (Java Cryptography Extension) von Oracle herunterladen . Extrahieren Sie einfach das heruntergeladene Archiv in einen lokalen Ordner mit dem Namen "Dateien".

Um das Image endgültig zu erstellen und in der lokalen Bibliothek zu speichern, müssen wir Folgendes ausführen:

docker build --tag=alpine-java:base --rm=true .

HINWEIS: Die Option –tag gibt dem Bild seinen Namen und –rm = true entfernt Zwischenbilder, nachdem es erfolgreich erstellt wurde. Das letzte Zeichen in diesem Shell-Befehl ist ein Punkt, der als Build-Verzeichnis-Argument fungiert.

4. Dockerisieren Sie eine eigenständige Spring Boot-Anwendung

Als Beispiel für eine Anwendung, die wir andocken können, nehmen wir die Spring-Cloud-Konfiguration / den Spring-Server aus dem Spring Cloud-Konfigurations-Tutorial. Als Vorbereitungsschritt müssen wir eine ausführbare JAR-Datei zusammenstellen und in unser Docker- Build-Verzeichnis kopieren :

tutorials $> cd spring-cloud-config/server server $> mvn package spring-boot:repackage server $> cp target/server-0.0.1-SNAPSHOT.jar \ ../../spring-boot-docker/files/config-server.jar server $> cd ../../spring-boot-docker

Jetzt erstellen wir eine Docker- Datei mit dem Namen Dockerfile.server mit folgendem Inhalt:

FROM alpine-java:base MAINTAINER baeldung.com COPY files/spring-cloud-config-server.jar /opt/spring-cloud/lib/ COPY files/spring-cloud-config-server-entrypoint.sh /opt/spring-cloud/bin/ ENV SPRING_APPLICATION_JSON= \ '{"spring": {"cloud": {"config": {"server": \ {"git": {"uri": "/var/lib/spring-cloud/config-repo", \ "clone-on-start": true}}}}}}' ENTRYPOINT ["/usr/bin/java"] CMD ["-jar", "/opt/spring-cloud/lib/spring-cloud-config-server.jar"] VOLUME /var/lib/spring-cloud/config-repo EXPOSE 8888
  • FROM : Als Basis für unser Image verwenden wir das Java- fähige Alpine Linux , das im vorherigen Abschnitt erstellt wurde
  • KOPIEREN : Wir lassen Docker unsere JAR-Datei in das Bild kopieren
  • ENV : Mit diesem Befehl können wir einige Umgebungsvariablen definieren, die von der im Container ausgeführten Anwendung berücksichtigt werden. Hier definieren wir eine angepasste Spring Boot Application- Konfiguration, die später an die ausführbare JAR-Datei übergeben wird
  • ENTRYPOINT / CMD : Dies ist die ausführbare Datei, die beim Booten des Containers gestartet wird. Wir müssen sie als JSON-Array definieren , da wir für einige Anwendungsargumente einen ENTRYPOINT in Kombination mit einem CMD verwenden
  • VOLUME : Da unser Container in einer isolierten Umgebung ohne direkten Netzwerkzugriff ausgeführt wird, müssen wir einen Mountpoint-Platzhalter für unser Konfigurations-Repository definieren
  • EXPOSE : Hier teilen wir Docker mit , an welchem ​​Port unsere Anwendung aufgelistet ist. Dieser Port wird beim Booten des Containers auf dem Host veröffentlicht

Um ein Image aus unserer Docker-Datei zu erstellen , müssen wir wie zuvor 'Docker Build' ausführen :

$> docker build --file=Dockerfile.server \ --tag=config-server:latest --rm=true .

Bevor wir jedoch einen Container aus unserem Image ausführen, müssen wir ein Volume zum Mounten erstellen:

$> docker volume create --name=spring-cloud-config-repo

HINWEIS: Während ein Container unveränderlich ist und nach dem Beenden der Anwendung nicht für ein Image festgeschrieben wird, bleiben die auf einem Volume gespeicherten Daten über mehrere Container hinweg bestehen.

Endlich können wir den Container von unserem Image aus ausführen:

$> docker run --name=config-server --publish=8888:8888 \ --volume=spring-cloud-config-repo:/var/lib/spring-cloud/config-repo \ config-server:latest
  • Zuerst müssen wir unseren Container benennen . Wenn nicht, wird automatisch eine ausgewählt
  • Dann müssen wir _ - PUBLISH unsere ausgesetzt Port (siehe Dockerfile ) an einen Port auf unserem Gastgeber. Der Wert wird in der Form 'Host-Port: Container-Port' angegeben . Wenn nur ein Container-Port angegeben ist, wird ein zufällig ausgewählter Host-Port verwendet. Wenn wir diese Option weglassen, wird der Container vollständig isoliert
  • Mit der Option –volume können Sie entweder auf ein Verzeichnis auf dem Host (bei Verwendung mit einem absoluten Pfad) oder auf ein zuvor erstelltes Docker- Volume (bei Verwendung mit einem Volume-Namen ) zugreifen . Der Pfad nach dem Doppelpunkt gibt den Einhängepunkt im Container an
  • Als Argument müssen wir Docker mitteilen , welches Bild verwendet werden soll. Hier müssen wir den Bildnamen aus dem vorherigen Schritt " Docker Build " angeben
  • Einige weitere nützliche Optionen:
    • -it - Aktiviert den interaktiven Modus und weist eine Pseudo-Tty zu
    • -d - Nach dem Booten vom Container abnehmen

Wenn wir den Container im getrennten Modus ausführen, können wir seine Details überprüfen, stoppen und mit den folgenden Befehlen entfernen:

$> docker inspect config-server $> docker stop config-server $> docker rm config-server

5. Dockerisieren Sie abhängige Anwendungen in einem Composite

Docker- Befehle und Docker- Dateien eignen sich besonders zum Erstellen einzelner Container. Wenn Sie jedoch in einem Netzwerk isolierter Anwendungen arbeiten möchten , ist die Containerverwaltung schnell überladen.

Um dies zu lösen, bietet Docker ein Tool namens Docker Compose an . Dies wird mit einer eigenen Build-Datei im YAML- Format geliefert und eignet sich besser für die Verwaltung mehrerer Container. Beispielsweise kann es eine Zusammenstellung von Diensten in einem Befehl starten oder stoppen oder die Protokollierungsausgabe mehrerer Dienste zu einer Pseudoty zusammenführen .

Lassen Sie uns ein Beispiel für zwei Anwendungen erstellen, die in verschiedenen Docker-Containern ausgeführt werden. Sie kommunizieren miteinander und werden dem Host-System als „einzelne Einheit“ präsentiert. Wir bauen und kopieren Sie die Feder-Cloud-config / Client Beispiel im Frühjahr Wolke beschriebenen Konfiguration Tutorial in unseren Dateien Ordner, wie wir vorher mit dem getan haben config-Server .

Dies wird unsere docker-compose.yml sein :

version: '2' services: config-server: container_name: config-server build: context: . dockerfile: Dockerfile.server image: config-server:latest expose: - 8888 networks: - spring-cloud-network volumes: - spring-cloud-config-repo:/var/lib/spring-cloud/config-repo logging: driver: json-file config-client: container_name: config-client build: context: . dockerfile: Dockerfile.client image: config-client:latest entrypoint: /opt/spring-cloud/bin/config-client-entrypoint.sh environment: SPRING_APPLICATION_JSON: \ '{"spring": {"cloud": \ {"config": {"uri": "//config-server:8888"}}}}' expose: - 8080 ports: - 8080:8080 networks: - spring-cloud-network links: - config-server:config-server depends_on: - config-server logging: driver: json-file networks: spring-cloud-network: driver: bridge volumes: spring-cloud-config-repo: external: true
  • version : Gibt an, welches Format verwendet werden soll. Dies ist ein Pflichtfeld. Hier verwenden wir die neuere Version, während das Legacy-Format '1' ist.
  • Dienste : Jedes Objekt in diesem Schlüssel definiert einen Dienst , auch Container genannt. Dieser Abschnitt ist obligatorisch
    • build: If given, docker-compose is able to build an image from a Dockerfile
      • context: If given, it specifies the build-directory, where the Dockerfile is looked-up
      • dockerfile: If given, it sets an alternate name for a Dockerfile
    • image: Tells Docker which name it should give to the image when build-features are used. Otherwise, it is searching for this image in the library or remote-registry
    • networks: This is the identifier of the named networks to use. A given name-value must be listed in the networks section
    • volumes: This identifies the named volumes to use and the mountpoints to mount the volumes to, separated by a colon. Likewise in networks section, a volume-name must be defined in separate volumes section
    • links: This will create an internal network link between this service and the listed service. This service will be able to connect to the listed service, whereby the part before the colon specifies a service-name from the services section and the part after the colon specifies the hostname at which the service is listening on an exposed port
    • depends_on: This tells Docker to start a service only, if the listed services have started successfully. NOTICE: This works only at container level! For a workaround to start the dependent application first, see config-client-entrypoint.sh
    • logging: Here we are using the ‘json-file' driver, which is the default one. Alternatively ‘syslog' with a given address option or ‘none' can be used
  • networks: In this section we're specifying the networks available to our services. In this example, we let docker-compose create a named network of type ‘bridge' for us. If the option external is set to true, it will use an existing one with the given name
  • volumes: This is very similar to the networks section

Before we continue, we will check our build-file for syntax-errors:

$> docker-compose config

This will be our Dockerfile.client to build the config-client image from. It differs from the Dockerfile.server in that we additionally install OpenBSD netcat (which is needed in the next step) and make the entrypoint executable:

FROM alpine-java:base MAINTAINER baeldung.com RUN apk --no-cache add netcat-openbsd COPY files/config-client.jar /opt/spring-cloud/lib/ COPY files/config-client-entrypoint.sh /opt/spring-cloud/bin/ RUN chmod 755 /opt/spring-cloud/bin/config-client-entrypoint.sh

And this will be the customized entrypoint for our config-client service. Here we use netcat in a loop to check whether our config-server is ready. You have to notice, that we can reach our config-server by its link-name, instead of an IP address:

#!/bin/sh while ! nc -z config-server 8888 ; do echo "Waiting for upcoming Config Server" sleep 2 done java -jar /opt/spring-cloud/lib/config-client.jar

Finally, we can build our images, create the defined containers, and start it in one command:

$> docker-compose up --build

To stop the containers, remove it from Docker and remove the connected networks and volumes from it, we can use the opposite command:

$> docker-compose down

A nice feature of docker-compose is the ability to scale services. For example, we can tell Docker to run one container for the config-server and three containers for the config-client.

But for this to work properly, we have to remove the container_name from our docker-compose.yml, for letting Docker choose one, and we have to change the exposed port configuration, to avoid clashes.

Danach können wir unsere Dienstleistungen folgendermaßen skalieren:

$> docker-compose build $> docker-compose up -d $> docker-compose scale config-server=1 config-client=3

6. Fazit

Wie wir gesehen haben, können wir jetzt benutzerdefinierte Docker- Images erstellen, eine Spring Boot-Anwendung als Docker- Container ausführen und abhängige Container mit Docker-Compose erstellen .

Weitere Informationen zu den Build-Dateien finden Sie in der offiziellen Dockerfile-Referenz und in der docker-compose.yml-Referenz .

Die Quellcodes für dieses Tutorial finden Sie wie gewohnt auf Github .