wake-up-neo.com

Externe Spring Boot-Eigenschaften bei der Bereitstellung in Docker

In meiner Spring Boot-App möchte ich die Eigenschaften für die Ausführung in einem Docker-Container externalisieren. Bei der ersten Bereitstellung werden die Eigenschaften, die sich aktuell in my-server/src/main/resources/application.yml befinden, wie erwartet von der Anwendung geladen und verwendet. Alles funktioniert gut.

Mein Problem ist jedoch, dass diese Eigenschaften nach Bedarf aktualisierbar sein müssen, sodass ich einmalig auf die application.yml-Datei im Docker-Container zugreifen muss. Zu diesem Zeitpunkt ist es jedoch nicht im build/docker/-Verzeichnis enthalten, bevor die buildDocker-Task ausgeführt wird. Nach der ersten Bereitstellung kann nicht darauf zugegriffen werden.

Also habe ich versucht, die Yaml-Datei in das Verzeichnis docker/ build zu kopieren, in ein barrierefreies Verzeichnis (/opt/meanwhileinhell/myapp/conf) zu kopieren und die spring.config.location-Eigenschaft zu verwenden, um einen Speicherort der Konfiguration an die Jar in meiner Docker-Datei zu übergeben:

ENTRYPOINT  ["Java",\
...
"-jar", "/app.jar",\
"--spring.config.location=classpath:${configDirectory}"]

Wenn ich den Befehl betrachte, der auf dem Docker-Container ausgeführt wird, sehe ich, dass dies wie erwartet ist:

/app.jar --spring.config.location=classpath:/opt/meanwhileinhell/myapp/conf]

Wenn ich jedoch eine Eigenschaft in dieser Datei aktualisiere und den Docker-Container neu starte, werden die Änderungen nicht übernommen. Dateiberechtigungen sind:

-rw-r--r-- 1 root root  618 Sep  5 13:59 application.yml

Die Dokumentation besagt: 

Wenn benutzerdefinierte Konfigurationsspeicherorte konfiguriert sind, werden sie zusätzlich verwendet zu den Standardstandorten. Benutzerdefinierte Standorte werden vor dem .__ gesucht. Standardspeicherorte.

Ich kann nicht herausfinden, was ich falsch mache oder falsch interpretiere, aber was noch wichtiger ist: Ist dies der richtige Weg, die Konfiguration für diese Art von Docker-Szenario zu externalisieren?

13
MeanwhileInHell

DOCKER BILDKONFIGURATION

Wenn Sie nach wie Spring es empfiehlt nach einem Spring Boot-angetriebenen Docker-Container suchen, finden Sie Folgendes:

FROM openjdk:8-jdk-Alpine
VOLUME /tmp
ADD target/gs-spring-boot-docker-0.1.0.jar app.jar
ENV Java_OPTS=""
ENTRYPOINT [ "sh", "-c", "Java $Java_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar" ]

Das bedeutet, dass Ihr Image openjdk erweitert und Ihr Container eine eigene Umgebung hat. Wenn Sie dies tun, reicht es aus, zu definieren, was Sie überschreiben wollen, als Umgebungseigenschaften, und Spring Boot ruft sie ab, da Umgebungsvariablen Vorrang haben über den Yml-Dateien.

Umgebungsvariablen können auch in Ihrem Docker-Befehl übergeben werden, um den Container mit der gewünschten Konfiguration zu starten. Wenn Sie einen Grenzwert für den JVM-Speicher festlegen möchten, finden Sie den Link unten.


DOCKER COMPOSE SAMPLE

Hier haben Sie ein Beispiel, wie ich eine einfache App-Umgebung mit Docker Compose starte. Wie Sie sehen, deklariere ich die spring.datasource.url-Eigenschaft hier als Umgebungsvariable. Sie überschreibt also alles, was Sie in Ihrer application.yml-Datei haben.

version: '2'
services:
    myapp:
        image: mycompany/myapp:1.0.0
        container_name: myapp
        depends_on:
        - mysql
        environment:
            - SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/myapp?useUnicode=true&characterEncoding=utf8&useSSL=false
        ports:
            - 8080:8080

    mysql:
        image: mysql:5.7.19
        container_name: mysql
        volumes:
            - /home/docker/volumes/myapp/mysql/:/var/lib/mysql/
        environment:
            - MYSQL_USER=root
            - MYSQL_ALLOW_EMPTY_PASSWORD=yes
            - MYSQL_DATABASE=myapp
        command: mysqld --lower_case_table_names=1 --skip-ssl --character_set_server=utf8

Siehe auch:

19
Xtreme Biker

Ich persönlich würde Spring Cloud Config Server verwenden, anstatt zu versuchen, überall Eigenschaftsdateien einzurichten.

damit können Sie Eigenschaften in git (die Versionskontrolle, Verzweigung usw. zulassen) auf einer Ebene pro Umgebung/Profil an einem zentralen Ort halten, die dann von REST bedient werden. Spring Boot bietet volle Unterstützung dafür. Tatsächlich ist es nur eine andere Eigenschaftsquelle, die in Ihrer Umgebung endet.

https://spring.io/guides/gs/centralized-configuration/

6
PaulNUK

Also habe ich es geschafft, es zum Laufen zu bringen. Anstatt den Klassenpfad an das Verzeichnis in meiner DockerFile zu übergeben:

"--spring.config.location=classpath:${configDirectory}"]

Ich habe stattdessen versucht, den vollständigen Speicherort der Datei zu übergeben:

 "--spring.config.location=file:${configDirectory}/application.yml"]

Dies wird jetzt beim Neustart des Docker-Containers aktualisiert.

3
MeanwhileInHell

Eine Variation der answer von Xtreme Biker, diesmal für die Einführung eines Spring-Boot-Krieges in einen angedockten Tomcat

Ich empfehle Ihnen, einen nominalen application.yml in Ihre App aufzunehmen, aber verwenden Sie Docker-Umgebungsvariablen, um einzelne Schlüssel zu überschreiben, die umgebungsspezifische Variationen erfordern.

Der Grund, warum ich diesen Ansatz (mit Docker-Umgebungsvariablen) empfehle, ist:

  • ihr Docker-Image kann genau dasselbe Artefakt verwenden, das Sie für die lokale Entwicklung verwenden könnten
  • die Verwendung von Volume-Mounts ist schmerzhaft. Sie müssen einen Ort finden, an dem sie auf Ihrem Docker-Host leben können - was diesen Host zu einer Schneeflocke macht
  • die Verwendung von Docker-Geheimnissen ist schmerzhaft; Bild oder Anwendungsebene müssen geändert werden, um Geheimnisse explizit aus dem Dateisystem herauszufinden

Spring Boot's Externalized Configuration docs erklären zwei Möglichkeiten, die Umgebung über die Befehlszeile bereitzustellen:

  • UN * X-Umgebungsvariablen (d. H. SPRING_DATASOURCE_USERNAME=helloworld)
  • Java-Optionen (d. H. -Dspring.datasource.username=helloworld)

Ich bevorzuge Java-Optionen, weil sie eine explizite Absicht ausdrücken: "Dies ist für den folgenden Java-Prozess und only für diesen Java-Prozess gedacht".

Zum Schluss: Ich würde Tomcat's CATALINA_OPTS als Mechanismus für die Übergabe dieser Java-Optionen verwenden. Dokumentation von catalina.sh:

(Optional) Java-Laufzeitoptionen, die beim "Start" verwendet werden, "run" oder "debug" -Befehl wird ausgeführt . Fügen Sie hier und nicht in Java_OPTS alle Optionen ein, die darf nur von Tomcat selbst verwendet werden, nicht vom Stopp-Prozess der Versionsbefehl etc . Beispiele sind Heapgröße, GC-Protokollierung, JMX-Ports usw.

Weil CATALINA_OPTS eine einfachere Route ist, als dafür zu sorgen, dass Ihr Docker-Image dafür verantwortlich ist, einen setenv.sh zu erstellen und die entsprechenden Docker-Env-Deklarationen zu übergeben.


Erstellen Sie Ihr .war-Artefakt wie folgt:

./gradlew war

Wir erwarten, dass ein .war-Artefakt von Gradle an build/libs/api-0.0.1-SNAPSHOT.war ausgegeben wird.

Verwenden Sie eine solche Docker-Datei:

FROM Tomcat:8.5.16-jre8-Alpine

EXPOSE 8080

COPY build/libs/api-0.0.1-SNAPSHOT.war /usr/local/Tomcat/webapps/v1.war

CMD ["catalina.sh", "run"]

Bauen Sie Ihr Docker-Image so auf:

docker build . --tag=my-api

Übergeben Sie CATALINA_OPTS wie folgt an Ihren Container:

docker run -it \
-p 8080:8080 \
-e CATALINA_OPTS="\
-Dspring.datasource.url='jdbc:mysql://mydatabase.stackoverflow.com:3306' \
-Dspring.datasource.username=myuser \
" \
my-api

Und eine Docker-Compose-Variante sieht folgendermaßen aus:

version: '3.2'
services:
  web:
    image: my-api
    ports:
      - "8080:8080"
    environment:
      - >
        CATALINA_OPTS=
        -Dspring.datasource.url='jdbc:mysql://mydatabase.stackoverflow.com:3306'
        -Dspring.datasource.username=myuser
2
Birchlabs

Ihr Ansatz ist definitiv eine praktikable Lösung, wird jedoch nicht empfohlen, da Ihr Image dadurch nicht zwischen verschiedenen Produktions- und Entwicklungsumgebungen portierbar ist. Container sollten unveränderlich sein und alle Umgebungskonfigurationen sollten externalisiert werden.

Für Spring Boot gibt es ein sehr leistungsfähiges Projekt, mit dem Sie die Konfiguration externisieren können. Es heißt Spring Cloud Config . Mit dem Konfigurationsserver können Sie Ihre umgebungsspezifische Konfiguration in einem git-Repository speichern und die Konfiguration für Anwendungen bereitstellen, die diese benötigen. Sie speichern im Grunde nur die gleiche application.yml in git und verweisen den Konfigurationsserver auf den Repository-Speicherort.

Mit diesem Ansatz können Sie mehrere Konfigurationsdateien für verschiedene Umgebungen definieren und Ihren Docker-Container unveränderlich halten.

2
yamenk

Ich persönlich würde zwei Möglichkeiten in Betracht ziehen:

  1. Eine Umgebungsvariable verwenden

    app:
      image: my-app:latest
      ports:
        - "8080:8080"
      environment:
         SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/table
    
  2. SPRING_APPLICATION_JSON verwenden

    app:
      image: my-app:latests
      ports:
        - "8080:8080"
      environment:
        SPRING_APPLICATION_JSON: '{
          "spring.datasource.url": "jdbc:mysql://db:3306/table",
        }'
    
0
Rafal Enden