50

在我的 Spring Boot 应用程序中,我想将属性外部化以在 Docker 容器中运行。my-server/src/main/resources/application.yml首次部署时,应用程序会按预期加载和使用当前所在的属性。一切正常。

但是,我的问题是我需要这些属性可以根据需要进行更新,因此我需要application.yml在 Docker 容器上访问该文件一次。但是此时,它build/docker/在运行buildDocker任务之前不包含在目录中,因此在首次部署后不会被复制或访问。

因此,我尝试将 Yaml 文件复制到docker/构建目录中,将其复制到可访问目录 ( /opt/meanwhileinhell/myapp/conf),然后使用该spring.config.location属性将配置的位置传递给我的 Dockerfile 中的 Jar:

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

查看在 Docker 容器上运行的命令,我可以看到这是预期的:

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

但是,当我更新此文件中的属性并重新启动 Docker 容器时,它并没有获取更改。文件权限为:

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

文档指出:

配置自定义配置位置时,除了默认位置外,还会使用它们。在默认位置之前搜索自定义位置。

我似乎无法弄清楚我做错了什么或误解了什么,但可能更重要的是,这是为这种 Docker 场景外部化配置的正确方法吗?

4

6 回答 6

62

Docker 映像配置

如果您查看Spring 建议启动 Spring Boot 驱动的 docker 容器的方式,您会发现:

FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG JAR_FILE
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

这意味着您的图像扩展了 openjdk,并且您的容器有自己的环境。如果您这样做,将您想要覆盖的内容声明为环境属性就足够了,Spring Boot 将获取它们,因为环境变量优先于 yml 文件。

环境变量也可以在您的 docker 命令中传递,以使用您所需的配置启动容器。如果您想为 JVM 内存设置一些限制,请参阅下面的链接。


码头工人组成样本

在这里,您有一个示例,说明了我如何使用 docker compose 启动一个简单的应用程序环境。如您所见,我在spring.datasource.url此处将属性声明为环境变量,因此它会覆盖您在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

也可以看看:

于 2017-09-05T14:57:38.567 回答
23

我个人会考虑两种选择:

  1. 每个配置使用一个环境变量

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

    app:
      image: my-app:latest
      ports:
        - "8080:8080"
      environment:
        SPRING_APPLICATION_JSON: '{
          "spring.datasource.url": "jdbc:mysql://db:3306/table",
        }'
    
于 2019-02-18T17:17:30.807 回答
14

就我个人而言,我会使用 Spring Cloud Config Server 而不是尝试在各处设置属性文件。

tl;dr 它允许您在集中位置的每个环境/配置文件级别保存 git 中的属性(允许版本控制、分支等),然后由 REST 提供服务。Spring Boot 完全支持它;实际上,它只是最终出现在您的环境中的另一个属性源。

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

于 2017-09-05T15:20:02.770 回答
7

所以我设法让它工作。而不是将类路径传递给我的 DockerFile 中的目录:

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

我改为尝试传递文件的完整位置:

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

现在,这会在 Docker 容器重新启动时更新。

于 2017-09-05T15:17:23.817 回答
7

Xtreme Biker 的答案的一个变体,这次是为了将 Spring 引导战部署到 dockerized TomCat中……</p>

我建议在您的应用程序中包含一个标称application.yml,但使用 Docker 环境变量来覆盖任何需要特定于环境的变化的单个键。

我推荐这种方法(使用 Docker 环境变量)的原因是:

  • 您的 docker 映像可以使用与您可能用于本地开发的完全相同的人工制品
  • 使用 volume-mounts 很痛苦;你需要在你的 docker 主机上找到让他们住的地方——这会将主机变成雪花
  • 使用 docker secrets 很痛苦;需要更改图像或应用程序层以从文件系统中显式查找机密

Spring Boot 的Externalized Configuration 文档解释了通过命令行提供环境的两种方法:

  • UN*X 环境变量(即SPRING_DATASOURCE_USERNAME=helloworld
  • Java 选项(即-Dspring.datasource.username=helloworld

我更喜欢 Java 选项,因为它们表达了明确的意图:“这适用于以下 Java 进程,并且适用于该 Java 进程”。

最后:我会使用 TomCatCATALINA_OPTS作为传递这些 Java 选项的机制。文档来自catalina.sh

(可选)执行“start”、“run”或“debug”命令时使用的 Java 运行时选项。包括这里而不是 JAVA_OPTS 中的所有选项,这些选项只能由 Tomcat 本身使用,而不是由停止进程、版本命令等使用。例如堆大小、GC 日志记录、JMX 端口等。

因为CATALINA_OPTS比让你的 Docker 镜像负责创建一个setenv.sh并将适当的 Docker 环境声明传递给它更容易。


像这样构建您的.war人工制品:

./gradlew war

我们希望.warGradle 将人工制品输出到build/libs/api-0.0.1-SNAPSHOT.war.

使用这样的 Dockerfile:

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

像这样构建你的 Docker 镜像:

docker build . --tag=my-api

像这样传递CATALINA_OPTS到您的容器:

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

docker-compose 变体如下所示:

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
于 2017-09-05T15:39:48.633 回答
6

您的方法绝对是一个可行的解决方案,但不建议这样做,因为它使您的图像无法在不同的生产和开发环境之间移植。容器应该是不可变的,所有环境配置都应该外部化。

对于 Spring Boot,有一个非常强大的项目可以让您外部化配置。它称为Spring Cloud Config。配置服务器允许您将特定于环境的配置保存在 git 存储库中,并将配置提供给需要它的应用程序。您基本上只需在 git 中保存相同的 application.yml 并将配置服务器指向存储库位置。

按照这种方法,您可以为不同的环境定义多个配置文件,并保持您的 docker 容器不可变。

于 2017-09-05T15:26:06.693 回答