首页 > 解决方案 > 在 Dockerfile 的 ENTRYPOINT 上执行 java 命令无法识别给定的 ARG 值

问题描述

我创建了一个 dockerfile 来在 DockerHub 上构建和上传一个图像,该图像将连接到数据库并创建一个表。 Dockerfile

FROM openjdk:11 as builder
EXPOSE 8080
WORKDIR application
ARG JAR_FILE=toDoAppWithLogin.jar
COPY $JAR_FILE application.jar

ARG SQL_PASSWORD
ARG SQL_USERNAME
ARG SQL_PORT
ARG SQL_SERVER

RUN java -Djarmode=layertools -jar application.jar extract

FROM openjdk:11
WORKDIR application
COPY --from=builder application/dependencies/ ./
COPY --from=builder application/spring-boot-loader/ ./
COPY --from=builder application/application/ ./

ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher", "--my_sql.host=$SQL_SERVER", "--my_sql.port=$SQL_PORT", "--my_sql.username=$SQL_USERNAME", "--my_sql.password=$SQL_PASSWORD"]

我通过运行以下命令来构建映像(它已成功构建):

docker build -t nikspanos/cicd-pipeline:tag1 --build-arg SQL_USERNAME=user --build-arg SQL_PASSWORD=pass --build-arg SQL_PORT=0000 --build-arg SQL_SERVER=server .

然后在将其上传到 Docker 注册表之前运行 docker 映像对其进行测试

docker run nikspanos/cicd-pipeline:tag1

我得到的第一个错误:

java.sql.SQLNonTransientConnectionException:由于基础异常,无法加载连接类:com.mysql.cj.exceptions.WrongArgumentException:无法解析主机:端口对“$SQL_SERVER:$SQL_PORT”。

我想我没有正确传递参数,因此无法识别它们。我已经搜索过它,许多其他答案包括 ENV 选项,但我只想使用 ARG。

对此事提出任何建议。

标签: javaspringspring-bootdocker

解决方案


你在这里至少有 3 件事错了。

  1. ARG 值是有范围的,当您开始下一阶段时会超出范围。
  2. ARG 值用于构建时间(构建映像),用于运行时(从映像启动容器时),您需要设置 ENV。
  3. Docker 不会在 RUN、CMD 或 ENTRYPOINT 中扩展变量。相反,您将值作为环境变量注入。要将$var语法扩展为变量的值,您需要一个类似/bin/sh. json/exec 语法明确绕过了使用 shell 运行命令。

结果如下所示:

FROM openjdk:11 as builder
EXPOSE 8080
WORKDIR application
ARG JAR_FILE=toDoAppWithLogin.jar
COPY $JAR_FILE application.jar

RUN java -Djarmode=layertools -jar application.jar extract

FROM openjdk:11
WORKDIR application
COPY --from=builder application/dependencies/ ./
COPY --from=builder application/spring-boot-loader/ ./
COPY --from=builder application/application/ ./

ARG SQL_PASSWORD
ARG SQL_USERNAME
ARG SQL_PORT
ARG SQL_SERVER
ENV SQL_PASSWORD=$SQL_PASSWORD
ENV SQL_USERNAME=$SQL_USERNAME
ENV SQL_PORT=$SQL_PORT
ENV SQL_SERVER=$SQL_SERVER

ENTRYPOINT java org.springframework.boot.loader.JarLauncher "--my_sql.host=$SQL_SERVER" "--my_sql.port=$SQL_PORT" "--my_sql.username=$SQL_USERNAME" "--my_sql.password=$SQL_PASSWORD"

我个人会切换到将入口点作为 shell 脚本运行,这样您就可以回到 json 语法。这将允许在 CMD 值中传递其他 cli 选项。那个脚本可能是

#!/bin/sh
exec java org.springframework.boot.loader.JarLauncher "--my_sql.host=$SQL_SERVER" "--my_sql.port=$SQL_PORT" "--my_sql.username=$SQL_USERNAME" "--my_sql.password=$SQL_PASSWORD" "$@"

exec 避免留下/bin/sh可能会干扰信号的 as pid 1 的地方。

然后您会遇到下一个问题,您不应该将配置设置(如 db 主机名,尤其是密码)烘焙到映像中。而是在您运行容器时成为运行时配置:

FROM openjdk:11 as builder
EXPOSE 8080
WORKDIR application
ARG JAR_FILE=toDoAppWithLogin.jar
COPY $JAR_FILE application.jar

RUN java -Djarmode=layertools -jar application.jar extract

FROM openjdk:11
WORKDIR application
COPY --from=builder application/dependencies/ ./
COPY --from=builder application/spring-boot-loader/ ./
COPY --from=builder application/application/ ./
COPY entrypoint.sh /entrypoint.sh    

ENTRYPOINT [ "/entrypoint.sh" ]

然后你会在没有任何参数的情况下构建它并使用设置运行它:

docker build -t nikspanos/cicd-pipeline:tag1 .
docker run -e SQL_USERNAME=user -e SQL_PASSWORD=pass -e SQL_PORT=0000 -e SQL_SERVER=server nikspanos/cicd-pipeline:tag1

我还建议研究用于传递凭据的秘密解决方案,因为环境变量很容易暴露。这些会将凭据作为文件挂载或使它们可从外部秘密服务器获得。


推荐阅读