web-dev-qa-db-fra.com

Externalisation des propriétés Spring Boot lors du déploiement sur Docker

Dans mon application Spring Boot, je souhaite externaliser les propriétés à exécuter dans un conteneur Docker. Lors du premier déploiement, les propriétés actuellement dans my-server/src/main/resources/application.yml sont chargées et utilisées par l'application comme prévu. Tout fonctionne bien.

Cependant, mon problème est que j'ai besoin que ces propriétés puissent être mises à jour si nécessaire. J'ai donc besoin d'accéder au fichier application.yml une fois sur le conteneur Docker. Mais à ce stade, il n'est pas inclus dans le répertoire build/docker/ avant l'exécution de la tâche buildDocker. Par conséquent, il ne sera ni copié ni accessible après le premier déploiement.

Donc, ce que j'ai essayé, c'est de copier le fichier Yaml dans le répertoire de construction docker/, de le copier dans un répertoire accessible (/opt/meanwhileinhell/myapp/conf) et d'utiliser la propriété spring.config.location pour transmettre un emplacement de la configuration au fichier Jar de mon fichier Docker:

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

En regardant la commande en cours d'exécution sur le conteneur Docker, je peux voir que c'est comme prévu:

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

Cependant, lorsque je mets à jour une propriété dans ce fichier et que je redémarre le conteneur Docker, les modifications ne sont pas prises en compte. Les autorisations de fichier sont:

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

La documentation indique: 

Lorsque les emplacements de configuration personnalisés sont configurés, ils sont également utilisés aux emplacements par défaut. Les emplacements personnalisés sont recherchés avant le emplacements par défaut.

Je n'arrive pas à comprendre ce que je fais de mal ou de mal interpréter, mais ce qui est probablement plus important, est-ce la bonne façon d'extérioriser la configuration pour ce type de scénario Docker?

13
MeanwhileInHell

CONFIGURATION D'IMAGE DU DOCKER

Si vous regardez comme le recommande Spring pour lancer un conteneur de dockers alimenté par Spring Boot, vous trouverez ceci:

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" ]

Cela signifie que votre image s'étend à openjdk et que votre conteneur possède son propre environnement. Si vous procédez ainsi, il vous suffira de déclarer ce que vous souhaitez écraser en tant que environment properties. Spring Boot les récupérera, car les variables d'environnement ont priorité sur les fichiers yml.

Des variables d'environnement peuvent également être passées dans votre commande docker pour lancer le conteneur avec la configuration souhaitée. Si vous souhaitez définir une limite pour la mémoire de la machine virtuelle Java, voir le lien ci-dessous.


DOCKER COMPOSE ECHANTILLON

Ici, vous avez un exemple de la façon dont je lance un environnement d’application simple avec docker compose. Comme vous le voyez, je déclare la propriété spring.datasource.url ici en tant que variable d'environnement. Elle remplace donc tout ce que vous avez dans votre fichier application.yml.

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

Voir également:

19
Xtreme Biker

Personnellement, j'utilisais Spring Cloud Config Server au lieu d'essayer de configurer des fichiers de propriétés partout.

cela vous permet de conserver les propriétés dans git (ce qui permet le contrôle de version, la création de branches, etc.) au niveau de l’environnement/du profil dans un emplacement centralisé, qui sont ensuite servies par REST. Spring Boot a un support complet pour cela; en réalité, il s’agit simplement d’une autre source de propriétés qui aboutit dans votre environnement.

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

6
PaulNUK

Alors j'ai réussi à le faire fonctionner. Plutôt que de passer le classpath au répertoire de mon fichier Docker:

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

J'ai plutôt essayé de passer l'emplacement complet du fichier:

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

Ceci est maintenant mis à jour lors du redémarrage du conteneur Docker.

3
MeanwhileInHell

Une variante de answer de Xtreme Biker, cette fois pour le déploiement d'une guerre des bottes de printemps dans un Tomcat dockerisé

Je recommande d'inclure un application.yml nominal dans votre application, mais d'utiliser des variables d'environnement Docker pour remplacer toutes les clés individuelles nécessitant une variation spécifique à l'environnement.

La raison pour laquelle je recommande cette approche (à l'aide de variables d'environnement Docker) est la suivante:

  • votre image de menu fixe peut utiliser exactement le même artefact que vous pourriez utiliser pour le développement local
  • utiliser des supports de volume est douloureux; vous devez trouver un endroit où vivre sur votre docker Host, qui transformera cet hôte en flocon de neige
  • utiliser des secrets de docker est douloureux; l'image ou la couche d'application doit être modifiée pour rechercher explicitement les secrets du système de fichiers

La documentation de Spring Boot Externalized Configuration explique deux manières de fournir un environnement via une ligne de commande:

  • UN * X env vars (c'est-à-dire SPRING_DATASOURCE_USERNAME=helloworld)
  • Options Java (i.e. -Dspring.datasource.username=helloworld)

Je préfère les options Java, car elles expriment une intention explicite: "ceci est destiné au processus Java suivant, et uniquement pour ce processus Java".

Enfin: J'utiliserais le CATALINA_OPTS de Tomcat comme mécanisme pour transmettre ces options Java. Documentation de catalina.sh:

(Facultatif) Options d'exécution Java utilisées lorsque "démarrer", La commande "run" ou "debug" est exécutée . Incluez ici et non dans Java_OPTS toutes les options, cela devrait être utilisé uniquement par Tomcat lui-même, et non par le processus d'arrêt, la commande de version etc . Les exemples sont la taille de tas, la journalisation GC, les ports JMX, etc.

Parce que CATALINA_OPTS est un itinéraire plus facile que de rendre votre image Docker responsable de la création d'un setenv.sh et de la transmission des déclarations env Docker appropriées.


Construisez votre artefact .war comme ceci:

./gradlew war

Nous nous attendons à ce que Gradle produise un artefact .war dans build/libs/api-0.0.1-SNAPSHOT.war.

Utilisez un tel fichier Docker:

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"]

Construisez votre image Docker comme ceci:

docker build . --tag=my-api

Passez CATALINA_OPTS à votre conteneur comme suit:

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

Et une variante docker-compose ressemble à ceci:

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

Votre approche est sans aucun doute une solution viable, mais elle n’est pas recommandée car elle rend votre image non portable entre différents environnements de production et de développement. Les conteneurs doivent être immuables et toute la configuration de l'environnement doit être externalisée.

Pour le démarrage du printemps, il existe un projet très puissant qui vous permet d’externaliser la configuration. Son appelé Spring Cloud Config . Le serveur de configuration vous permet de sauvegarder la configuration spécifique à votre environnement dans un référentiel git et de la transmettre aux applications qui en ont besoin. En gros, vous venez de sauvegarder le même fichier application.yml dans git et de diriger le serveur de configuration vers l’emplacement du référentiel.

En suivant cette approche, vous pouvez définir plusieurs fichiers de configuration pour différents environnements et conserver votre conteneur docker immuable.

2
yamenk

Personnellement, je considérerais deux options:

  1. Utiliser une variable d'environnement

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

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