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?
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:
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.
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.
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:
Spring Boot's Externalized Configuration docs erklären zwei Möglichkeiten, die Umgebung über die Befehlszeile bereitzustellen:
SPRING_DATASOURCE_USERNAME=helloworld
)-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
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.
Ich persönlich würde zwei Möglichkeiten in Betracht ziehen:
Eine Umgebungsvariable verwenden
app:
image: my-app:latest
ports:
- "8080:8080"
environment:
SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/table
SPRING_APPLICATION_JSON
verwenden
app:
image: my-app:latests
ports:
- "8080:8080"
environment:
SPRING_APPLICATION_JSON: '{
"spring.datasource.url": "jdbc:mysql://db:3306/table",
}'