【问题标题】:How to build a Docker image for Spring Boot application without the JAR at hand如何在没有 JAR 的情况下为 Spring Boot 应用程序构建 Docker 映像
【发布时间】:2019-02-09 07:00:47
【问题描述】:

我已按照这些教程为我的 Spring Boot 应用程序构建 Docker 映像,该应用程序使用 Maven 作为构建工具。 我在 Windows 10 机器上使用 boot2docker VM,将我的项目从我的 Bitbucker 存储库克隆到 VM。

https://spring.io/guides/gs/spring-boot-docker/

https://www.callicoder.com/spring-boot-docker-example/

我了解所提供的说明,但是我未能构建正确的 Docker 映像。这是我尝试过的东西。

  1. 为 Dockerfile 使用 Spotify maven 插件。尝试运行 ./mvnw 来构建 JAR 以及 Docker 映像。但是,我没有在 boot2docker 中安装 Java。所以 Maven 包装器 ./mvnw 无法运行。

  2. 我尝试通过基于 openjdk:8-jdk-alpine 镜像的 Dockerfile 构建 JAR。我在 Dockerfile 中添加了RUN ./mvnw package 指令。然后运行docker build -t <my_project> . 来构建Docker 镜像。 它在 RUN 指令处失败,声称 /bin/sh: mvnw: not found The command '/bin/sh -c mvnw package' returned a non-zero code: 127

我的Dockerfile,位于mvnw所在目录:

MAINTAINER myname

VOLUME /tmp

RUN ./mvnw package

ARG JAR_FILE=target/myproject-0.0.1-SNAPSHOT.jar

COPY ${JAR_FILE} app.jar

ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

对于 1,我需要在 Docker 引擎所在的操作系统中安装 Java。但我认为这不是一个好习惯,因为这会降低可移植性。

对于2,首先,我不知道如何在Dockerfile中成功运行./mvnw。其次,我不确定通过 Dockerfile 构建 Spring Boot JAR 是否是一个好习惯,因为我没有看到任何“Docker for Spring Boot”教程告诉这样做。

那么,解决我的情况的最佳做法是什么?我是 Docker 新手。感谢您的评论和回答!

【问题讨论】:

  • 因为您当前的方法存在多个缺陷。您可以尝试以下操作:构建 Jar -> 在 openjdk docker 映像中打包该 jar
  • 我自己试过了,总是可以使用,因为您可以使用主机系统生成 jar 和 dockerfile 将其包装在图像中
  • 为什么要在图像中构建 jar?这是您需要满足的约束条件吗?通常你会在 Docker 世界之外构建一个 jar,然后使用这个外部构建的工件构建一个图像。正如@jazz 所说的那样
  • 要构建一个 java 项目,您显然需要客户端上的 java 或使用多阶段 Dockerfile 来做同样的事情。您遵循的指南假定为第一个。

标签: docker spring-boot dockerfile docker-image


【解决方案1】:

您可以安装 maven 并直接在构建中运行编译。通常,这将是一个多阶段构建,以避免将整个 jdk 包含在推送的映像中:

FROM openjdk:8-jdk-alpine as build
RUN apk add --no-cache maven
WORKDIR /java
COPY . /java
RUN mvn package -Dmaven.test.skip=true
EXPOSE 8080
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/java/target/myproject-0.0.1-SNAPSHOT.jar"]

以上是我过去做过的同一个例子的简化版本。您可能需要调整入口点中的文件名,但关键步骤是安装 maven 并在您的构建中运行它。

【讨论】:

  • 我真的很喜欢你的回答。我见过很多 Dockerfile 可以用来运行一个 maven 项目,除了在本地安装 docker 之外,你不需要安装任何东西,但是它们中的大多数在每次启动容器时都运行 maven,我正在寻找一种方法仅在我重建 docker 映像时运行 maven。谢谢。
【解决方案2】:

从您的第二个示例中,我认为您误解了 Docker 如何构建映像。当 Docker 执行RUN ./mvnw package 时,文件mvnw 必须存在于正在构建的映像的文件系统中,这意味着您应该在上一步中具有类似COPY mvnw . 的指令 - 这会将文件从本地文件系统复制到图片。

您可能需要在调用./mvnw 之前复制图像中的整个项目结构,正如@BMitch 的回复所暗示的那样。

此外,正如@BMitch 所说,要生成小尺寸图像,通常建议使用多阶段构建,其中第一阶段安装每个依赖项,但最终图像只有您的 JAR。

您可以尝试以下方法:

# First stage: build fat JAR

# Select base image.
# (The "AS builder" gives a name to the stage that we will need later)
# (I think it's better to use a slim image with Maven already installed instead
#  than ./mvnw. Otherwise you could need to give execution rights to your file
#  with instructions like "RUN chmod +x mvnw".)
FROM maven:3.6.3-openjdk-8-slim AS builder

# Set your preferred working directory
# (This tells the image what the "current" directory is for the rest of the build)
WORKDIR /opt/app

# Copy everything from you current local directory into the working directory of the image
COPY . .

# Compile, test and package
# (-e gives more information in case of errors)
# (I prefer to also run unit tests at this point. This may not be possible if your tests
#  depend on other technologies that you don't whish to install at this point.)
RUN mvn -e clean verify

###

# Second stage: final image containing only WAR files

# The base image for the final result can be as small as Alpine with a JRE
FROM openjdk:8-jre-alpine

# Once again, the current directory as seen by your image
WORKDIR /opt/app

# Get artifacts from the previous stage and copy them to the new image.
# (If you are confident the only JAR in "target/" is your package, you could NOT
#  use the full name of the JAR and instead something like "*.jar", to avoid updating
#  the Dockerfile when the version of your project changes.)
COPY --from=builder /opt/app/target/*.jar ./

# Expose whichever port you use in the Spring application
EXPOSE 8080

# Define the application to run when the Docker container is created.
# Either ENTRYPOINT or CMD.
# (Optionally, you could define a file "entrypoint.sh" that can have a more complex
#  startup logic.)
# (Setting "java.security.egd" when running Spring applications is good for security
#  reasons.)
ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -jar /opt/app/*.war

【讨论】:

    猜你喜欢
    • 2016-11-21
    • 2017-02-11
    • 2018-05-15
    • 2020-11-14
    • 2019-07-31
    • 2018-04-01
    • 2018-07-12
    • 2018-08-27
    • 1970-01-01
    相关资源
    最近更新 更多