在微服务和云原生架构日益普及的今天,Docker 已成为 Java 应用部署的事实标准。然而,许多开发者在 Windows 环境下尝试将 Spring Boot 等 Java 项目打包成 Docker 镜像时,常会遇到权限、路径或脚本兼容性问题------例如经典的错误:
bash
/bin/sh: 1: touch: not found
or
exec: "sh": executable file not found in $PATH
甚至在 Dockerfile 中执行 RUN sh -c 'touch /app.jar' 时失败。本文将手把手教你如何在 Windows 系统下正确构建 Java 项目的 Docker 镜像,并成功部署运行,避开常见陷阱。
一、准备工作
1. 安装必要工具
- JDK 8/11/17+ :用于编译 Java 项目
- Maven 或 Gradle:构建工具(本文以 Maven 为例)
- Docker Desktop for Windows:确保已启用 WSL2 后端(推荐)
- IDE(如 IntelliJ IDEA 或 VS Code)
✅ 建议:使用 WSL2(Windows Subsystem for Linux)可极大减少跨平台兼容问题。
2. 构建可执行 JAR 包
以 Spring Boot 项目为例,在项目根目录执行:
go
mvn clean package -DskipTests
生成的 JAR 文件通常位于 target/your-app.jar。
二、编写正确的 Dockerfile
关键点:避免在非 Linux 环境下使用不兼容的 shell 命令,并选择合适的 base 镜像。
bash
# 使用官方 OpenJDK 镜像(Alpine 更小,但注意 glibc 兼容性)
FROM openjdk:17-jdk-slim
# 设置工作目录
WORKDIR /app
# 将本地 JAR 文件复制到容器中
COPY target/*.jar app.jar
# 【关键】解决"touch"问题:仅在需要修改时间戳时才使用
# 实际上,现代 Spring Boot 应用通常不需要 touch
# 若你看到旧教程中的 RUN touch /app.jar,可直接删除!
# 暴露端口(根据应用配置调整)
EXPOSE 8080
# 启动应用
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
⚠️ 注意:
- 不要写
RUN sh -c 'touch /app.jar'------ 这是早期为解决 Spring Boot 在某些只读文件系统中启动失败而引入的 hack,在现代 Docker 和 Spring Boot 版本中已不再需要。- 如果你坚持使用 Alpine 镜像(
openjdk:17-alpine),请确保你的应用不依赖 glibc(Alpine 使用 musl libc),否则可能报错。
三、在 Windows 下构建镜像
打开 PowerShell 或 CMD(建议在项目根目录操作):
perl
# 构建镜像(注意末尾的 . 表示上下文路径)
docker build -t my-java-app:1.0 .
# 查看镜像是否创建成功
docker images
🔍 常见问题排查:
- 文件路径错误 :确保
COPY target/*.jar app.jar中的target目录存在且包含 JAR。- 换行符问题(CRLF vs LF) :若使用 Git Bash 或 WSL,确保 Dockerfile 为 Unix 换行(LF)。可在 VS Code 右下角切换。
- 权限问题:Windows 一般不会出现,但在 WSL 中需确保文件可读。
四、运行容器
css
docker run -d --name my-app -p 8080:8080 my-java-app:1.0
-d:后台运行-p 8080:8080:将宿主机 8080 端口映射到容器 8080 端口
查看日志验证是否启动成功:
perl
docker logs -f my-app
五、优化建议(进阶)
1. 多阶段构建(减小镜像体积)
bash
# 构建阶段
FROM maven:3.8-openjdk-17 AS builder
WORKDIR /build
COPY pom.xml .
COPY src ./src
RUN mvn clean package -DskipTests
# 运行阶段
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY --from=builder /build/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
2. 使用 .dockerignore
避免将 target/ 以外的临时文件(如 .git, *.log)复制进镜像:
bash
.git
*.log
README.md
Dockerfile
.dockerignore
3. 健康检查(可选)
bash
HEALTHCHECK --interval=30s --timeout=3s \
CMD curl -f http://localhost:8080/actuator/health || exit 1
六、关于 "RUN touch /app.jar" 的真相
该命令最早出现在 Spring Boot 官方早期文档中,目的是确保 JAR 文件的时间戳不是未来时间(某些构建环境如 CI/CD 可能导致时间异常),从而避免 Spring Boot 启动时因"非法时间"拒绝加载。
但在以下情况下完全不需要:
- 本地正常构建(
mvn package) - 使用现代 JDK 和 Spring Boot(2.3+)
- 容器文件系统可写(默认情况)
因此,除非你明确遇到时间戳相关错误,否则应删除该行,避免引入不必要的复杂性。
结语
在 Windows 下打包 Java 项目为 Docker 容器并非难事,关键在于理解 Docker 构建上下文、选择合适的 base 镜像,并摒弃过时的"经验代码"。通过本文的步骤,你可以快速、可靠地将 Spring Boot 应用容器化,并为后续部署到 Kubernetes 或云平台打下基础。
💡 最后提醒:开发环境尽量贴近生产环境。若生产使用 Linux,建议在 WSL2 或 Linux 虚拟机中测试 Docker 镜像,以提前暴露兼容性问题。