
本文详解如何通过多阶段构建在 docker 中安全、高效地编译 maven 项目,并解决因路径不一致导致的 `lstat target: no such file or directory` 构建错误。
在 Docker 中构建 Java 应用时,使用 Maven 多阶段构建(multi-stage build)是最佳实践:第一阶段负责编译和打包,第二阶段仅包含运行时依赖(如 JRE),从而显著减小镜像体积、提升安全性与可移植性。但实践中,一个常见陷阱是未正确指定构建产物的来源路径,导致 COPY target/*.jar 失败——正如错误信息所示:lstat /var/lib/docker/tmp/buildkit-mount.../target: no such file or directory。
根本原因在于:Docker 的 COPY 指令在非构建阶段(即第二阶段)中无法直接访问前一阶段(builder)的工作目录或文件系统。你不能像本地开发那样假设 target/ 目录天然存在;必须显式声明从哪个构建阶段(--from=builder)复制哪些文件,并提供绝对路径。
✅ 正确做法是使用 COPY --from=
FROM maven:3.8.1-openjdk-17 AS builder COPY src/ /tmp/src/ COPY pom.xml /tmp/ WORKDIR /tmp RUN mvn clean install -B # -B 启用批处理模式,避免交互提示 FROM openjdk:17-jre-slim # ⚠️ 关键优化:改用精简 JRE 镜像,而非完整 Maven 镜像 WORKDIR /app COPY --from=builder /tmp/target/*.jar app.jar EXPOSE 8081 ENTRYPOINT ["java", "-Dspring.profiles.active=docker", "-jar", "app.jar"]
? 关键改进说明:
- ✅ 使用 --from=builder 显式引用前一构建阶段;
- ✅ 路径 /tmp/target/*.jar 是 builder 阶段中的绝对路径,与 WORKDIR /tmp 严格对应;
- ✅ 第二阶段切换为 openjdk:17-jre-slim(约 150MB),而非 maven:3.8.1-openjdk-17(超 600MB),彻底移除 Maven、源码、缓存等无关内容,大幅缩小最终镜像;
- ✅ 添加 -B 参数使 Maven 运行更稳定(尤其在 CI/CD 环境中);
- ✅ 可选地在 java -jar 中加入 JVM 参数(如 Spring Profile),增强运行时灵活性。
⚠️ 注意事项:
- 不要在第二阶段 COPY 前执行 WORKDIR /tmp/app 并期望 target/ 存在——它根本不存在;
- 若项目含子模块,请确保 pom.xml 结构支持多模块构建,且 mvn clean install 在根目录执行;
- 推荐将 src/ 和 pom.xml 复制到 builder 阶段的合理路径(如 /workspace),而非 /tmp(临时目录语义易引发混淆),例如:
WORKDIR /workspace COPY . . RUN mvn clean package -DskipTests COPY --from=builder /workspace/target/*.jar /app/app.jar
总结:Docker 多阶段构建不是“连续 shell 会话”,每个阶段拥有独立文件系统。务必用 --from 明确阶段间依赖,并用绝对路径定位产物。遵循此原则,即可稳定实现 Maven 项目的容器化构建与轻量部署。










