之前的 Dockerfile
假设胖 JAR 已在命令上构建。我们也可以使用多阶段构建在 Docker 中执行该步骤,将结果从一个镜像复制到另一个镜像。使用 Maven 的示例:
Dockerfile
FROM openjdk:8-jdk-alpine as build
WORKDIR /workspace/app
COPY mvnw .
COPY .mvn .mvn
COPY pom.xml .
COPY src src
RUN ./mvnw install -DskipTests
RUN mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar)
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG DEPENDENCY=/workspace/app/target/dependency
COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF
COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","hello.Application"]
第一个图像标记为 “build”,用于运行 Maven 并构建胖 jar,然后解压缩它。拆包也可以由 Maven 或 Gradle 完成(这是《入门指南》中采用的方法)- 确实没有太大区别,只是必须编辑构建配置并添加插件。
请注意,源代码以分为四层。后面的层包含构建配置和应用的源代码,而前面的层包含构建系统本身(Maven 包装器)。这是一个很小的优化,也意味着我们不必将 target
复制到 docker 镜像,即使是用于构建的临时目录也是如此。
因为必须在第一个 RUN
部分中重新创建 Maven 缓存,所以每个更改源代码的构建都会很慢。但是,我们拥有一个完全独立的构建,只要拥有 docker,任何人都可以运行该应用来运行我们的应用。这在某些环境中可能非常有用,例如,我们需要与不懂 Java 的人共享代码的时候。
实验特性
Docker 18.06 带有一些 “实验性” 功能,其中包括一种缓存构建依赖项的方法。要打开它们,我们需要在守护进程(dockerd
) 中有一个标志,并且在运行客户端时还需要一个环境变量,然后可以在 Dockerfile
中添加第一行魔术:
Dockerfile
# syntax=docker/dockerfile:experimental
然后 RUN
指令接受一个新的标志 --mount。这是一个完整的示例:
Dockerfile
# syntax=docker/dockerfile:experimental
FROM openjdk:8-jdk-alpine as build
WORKDIR /workspace/app
COPY mvnw .
COPY .mvn .mvn
COPY pom.xml .
COPY src src
RUN --mount=type=cache,target=/root/.m2 ./mvnw install -DskipTests
RUN mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar)
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG DEPENDENCY=/workspace/app/target/dependency
COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF
COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","hello.Application"]
然后运行它:
$ DOCKER_BUILDKIT=1 docker build -t myorg/myapp .
...
=> /bin/sh -c ./mvnw install -DskipTests 5.7s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:3defa...
=> => naming to docker.io/myorg/myapp
使用实验性功能,我们可以在控制台上获得不同的输出,但是我们可以看到,一旦缓存变暖,Maven 构建现在只需要几秒钟而不是几分钟。
该 Dockerfile
配置的 Gradle 版本非常相似:
Dockerfile
# syntax=docker/dockerfile:experimental
FROM openjdk:8-jdk-alpine AS build
WORKDIR /workspace/app
COPY . /workspace/app
RUN --mount=type=cache,target=/root/.gradle ./gradlew clean build
RUN mkdir -p build/dependency && (cd build/dependency; jar -xf ../libs/*.jar)
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG DEPENDENCY=/workspace/app/build/dependency
COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF
COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","hello.Application"]
这些功能处于试验性阶段时,用于打开和关闭 buildkit 的选项取决于我们所使用的
docker
版本。检查文档以了解我们拥有的版本(上面的示例对于docker
18.0.6 是正确的)。