Docker 与 Java 入门:容器化为何改变开发模式
现代软件开发离不开容器化技术。Docker 已成为打包、交付和运行应用程序的事实标准。对于 Java 开发者而言,使用 Docker 解决了两个关键问题:"在我机器上能运行" 和环境配置的复杂性。本文将详细拆解如何将 Java 应用容器化,从简单的 JAR 文件到基于 Spring Boot 的复杂微服务。
Docker 允许将应用程序及其依赖项(JVM、库、系统工具)隔离在轻量级可移植容器中。这对 Java 尤为重要,因为 JDK 版本、内存设置和系统库会显著影响程序行为。
您将从本文中学到
- 为 Java 应用创建 Dockerfile
- 优化镜像体积
- 处理多模块 Maven/Gradle 项目
- 安全性和性能的最佳实践
1. 基础篇:为 Java 应用创建 Dockerfile
我们从简单示例开始。假设您有一个打包成 JAR 文件的 Java 应用程序。以下是将它打包到容器中的最小 Dockerfile:
# 使用官方 OpenJDK 镜像
FROM openjdk:17-jdk-slim
# 设置工作目录
WORKDIR /app
# 将 JAR 文件复制到容器中
COPY target/myapp.jar app.jar
# 暴露应用程序监听的端口
EXPOSE 8080
# 运行命令
ENTRYPOINT ["java", "-jar", "app.jar"]
这个 Dockerfile 做了三件简单的事:使用 JDK 17 的基础镜像,复制编译好的 JAR,然后运行它。但在实际项目中,这种方法效率低下——每次代码变更时,Docker 都会重建整个镜像,包括重新复制 JAR。
多阶段构建
为了分离构建和执行阶段,请使用多阶段 Dockerfile。这通过排除构建工具(Maven、Gradle、编译用的 JDK)来减小最终镜像体积。
# 构建阶段
FROM maven:3.9.6-eclipse-temurin-17 AS builder
WORKDIR /build
COPY pom.xml .
RUN mvn dependency:go-offline
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"]
第一阶段使用完整的 Maven 镜像进行编译,第二阶段使用最小的 JRE 镜像来运行。最终镜像体积约为 200 MB,而不是 700+ MB。
2. 镜像优化:减小体积并加速构建
大型镜像会拖慢部署速度并增加加载时间。以下是一些针对 Java 的技术:
使用 JLink 创建最小 JRE
从 Java 9 开始,您可以创建仅包含必要模块的自定义 JRE。这能大幅减小镜像体积。
FROM openjdk:17-jdk-slim AS jre-builder
RUN jlink \
--add-modules java.base,java.logging,java.sql \
--strip-debug \
--no-man-pages \
--no-header-files \
--compress=2 \
--output /jre
FROM debian:bookworm-slim
COPY --from=jre-builder /jre /jre
WORKDIR /app
COPY target/myapp.jar app.jar
ENV PATH="/jre/bin:${PATH}"
ENTRYPOINT ["java", "-jar", "app.jar"]
这样的镜像重量可能只有 40-60 MB,而不是 200+ MB。
Docker 层缓存
Docker 会缓存每一层。为了加速构建,请将不常更改的文件(依赖项)放在 Dockerfile 的开头,将频繁更改的文件(源代码)放在结尾。
FROM maven:3.9.6-eclipse-temurin-17 AS builder
WORKDIR /build
# 首先,只复制 pom.xml 并下载依赖项
COPY pom.xml .
RUN mvn dependency:go-offline
# 然后复制源代码并构建
COPY src ./src
RUN mvn clean packag