侧边栏壁纸
  • 累计撰写 67 篇文章
  • 累计创建 28 个标签
  • 累计收到 21 条评论

目 录CONTENT

文章目录

Spring Boot 3.x 集成 Docker 多阶段构建:构建最小可运行环境镜像

Administrator
2026-03-21 / 0 评论 / 0 点赞 / 0 阅读 / 0 字 / 正在检测是否收录...
温馨提示:
部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

前言

在微服务架构时代,Docker 已成为应用交付的标准方式。然而,很多开发者在构建 Spring Boot 镜像时,往往采用"一把梭"的方式:直接打包 JAR + 基础镜像 + 启动命令。这种方式不仅镜像体积庞大(通常 300MB+),还存在启动慢、安全隐患多等问题。

今天,我将分享一套完整的 Spring Boot Docker 镜像构建最佳实践,结合多阶段构建分层构建Jib 等技术,让你的镜像体积缩小 80%,启动时间提升 50%


一、传统构建方式的问题

先来看一个常见的 Dockerfile:

FROM openjdk:17
COPY target/myapp.jar /app/myapp.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app/myapp.jar"]

看似简单,但问题重重:

问题说明
镜像体积过大openjdk:17 基础镜像约 500MB,加上 JAR 后轻松超过 600MB
启动速度慢JVM 冷启动 + 全量类加载,通常需要 15-30 秒
安全风险高包含完整的 JDK(可用于编译),攻击面大
层缓存失效任何代码改动都需要重新构建整个镜像层
依赖重复下载每次构建都需要重新下载 Maven/Gradle 依赖

二、多阶段构建:从入门到精通

2.1 基础版:分离构建与运行环境

# ========== 第一阶段:构建 ==========
FROM maven:3.9-eclipse-temurin-17 AS builder

WORKDIR /build

# 利用 Maven 层缓存,先复制 pom.xml
COPY pom.xml .
RUN mvn dependency:go-offline -B

# 复制源码并构建
COPY src ./src
RUN mvn package -DskipTests -B

# ========== 第二阶段:运行 ==========
FROM eclipse-temurin:17-jre-alpine

WORKDIR /app

# 只复制最终产物
COPY --from=builder /build/target/myapp.jar app.jar

# 创建非 root 用户
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

EXPOSE 8080
ENTRYPOINT ["java", "-XX:+UseContainerSupport", "-XX:MaxRAMPercentage=75.0", "-jar", "app.jar"]

优化效果

  • 镜像体积:~600MB → ~180MB(减少 70%)
  • 利用 Alpine Linux 的小巧优势
  • 通过 MaxRAMPercentage 适配容器内存限制

2.2 进阶版:GraalVM 原生镜像

如果追求极致性能,可以考虑 GraalVM Native Image:

# ========== 第一阶段:构建原生镜像 ==========
FROM oracle/graalvm-ce:17 AS native-builder

WORKDIR /build

# 安装 native-image 工具
RUN gu install native-image

COPY pom.xml .
COPY src ./src
RUN mvn package -Pnative -DskipTests

# ========== 第二阶段:最小化运行时 ==========
FROM ubuntu:22.04

WORKDIR /app

COPY --from=native-builder /build/target/myapp app

EXPOSE 8080
ENTRYPOINT ["./app"]

优化效果

  • 启动时间:15-30秒 → 0.05-0.1秒(提升 300 倍
  • 镜像体积:180MB → 80MB
  • 无需 JVM 运行时环境

⚠️ 注意:GraalVM 原生构建耗时较长(3-10分钟),且部分 Spring Boot 特性需要额外配置。

2.3 高级版:分层构建 + 并行下载

利用 Docker 的 --mount 功能,实现依赖缓存:

# syntax=docker/dockerfile:1.4

FROM eclipse-temurin:17-jdk AS builder

WORKDIR /build

# 层缓存:只读挂载 Maven 仓库
RUN --mount=type=cache,target=/root/.m2/repository \
    --mount=type=cache,target=/root/.gradle/caches \
    <<EOF
    ./mvnw dependency:go-offline -B
    ./mvnw package -Pprod -DskipTests -B
    EOF

# ========== 运行时 ==========
FROM eclipse-temurin:17-jre AS runtime

WORKDIR /app

# 利用 Spring Boot 3.x 的分层 JAR 特性
COPY --from=builder /build/target/myapp.jar app.jar

# 创建应用用户
RUN groupadd -r appgroup && useradd -r -g appgroup appuser
USER appuser

HEALTHCHECK --interval=30s --timeout=3s --start-period=60s \
    CMD wget -q --spider http://localhost:8080/actuator/health || exit 1

EXPOSE 8080
ENTRYPOINT ["java", "-XX:+UseContainerSupport", "-jar", "app.jar"]

三、Jib:无需 Dockerfile 的容器化方案

对于不想编写 Dockerfile 的开发者,Google 的 Jib 是绝佳选择。

3.1 Maven 集成

<plugin>
    <groupId>com.google.cloud.tools</groupId>
    <artifactId>jib-maven-plugin</artifactId>
    <version>3.4.0</version>
    <configuration>
        <from>
            <image>eclipse-temurin:17-jre</image>
        </from>
        <to>
            <image>registry.cn-hangzhou.aliyuncs.com/myrepo/myapp</image>
            <tags>
                <tag>${project.version}</tag>
                <tag>latest</tag>
            </tags>
        </to>
        <container>
            <jvmFlags>
                <jvmFlag>-XX:+UseContainerSupport</jvmFlag>
                <jvmFlag>-XX:MaxRAMPercentage=75.0</jvmFlag>
            </jvmFlags>
            <ports>
                <port>8080</port>
            </ports>
            <creationTime>USE_CURRENT_TIMESTAMP</creationTime>
        </container>
    </configuration>
</plugin>

3.2 构建命令

# 构建并推送到远程仓库
mvn clean package jib:build

# 构建到本地 Docker daemon(用于本地测试)
mvn clean package jib:dockerBuild

Jib 的优势

  • 🚀 极速构建:增量构建,无需重新构建整个镜像
  • 🔄 层缓存优化:自动将依赖和类分层,优化分发效率
  • 🔒 安全性:默认使用非 root 用户
  • 📦 无需 Dockerfile:纯配置方式,易于维护

四、Docker Compose 编排实战

构建好镜像后,如何本地快速启动?看看这个生产级配置:

# docker-compose.yml
version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    image: myapp:1.0.0
    container_name: myapp
    ports:
      - "8080:8080"
    environment:
      - SPRING_PROFILES_ACTIVE=prod
      - JAVA_OPTS=-Xmx512m -Xms256m
      - TZ=Asia/Shanghai
    depends_on:
      mysql:
        condition: service_healthy
      redis:
        condition: service_healthy
    networks:
      - backend
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 768M
        reservations:
          memory: 256M

  mysql:
    image: mysql:8.0
    container_name: mysql
    environment:
      - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
      - MYSQL_DATABASE=myapp
      - TZ=Asia/Shanghai
    volumes:
      - mysql_data:/var/lib/mysql
    ports:
      - "3306:3306"
    networks:
      - backend
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    container_name: redis
    command: redis-server --requirepass ${REDIS_PASSWORD}
    volumes:
      - redis_data:/data
    ports:
      - "6379:6379"
    networks:
      - backend
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 3s
      retries: 5

networks:
  backend:
    driver: bridge

volumes:
  mysql_data:
  redis_data:

五、性能对比与总结

构建方式镜像体积首次启动再次启动推荐场景
传统方式600MB20s20s快速验证
多阶段构建180MB8s2s生产环境
GraalVM80MB0.1s0.1sServerless/FaaS
Jib180MB8s2s追求构建效率

🎯 最佳实践总结

  1. 生产环境推荐:多阶段构建 + eclipse-temurin Alpine 镜像
  2. 极致性能:GraalVM Native Image(需考虑兼容性问题)
  3. 开发效率:Jib + Maven 插件配置
  4. 安全加固:非 root 用户 + 最小化基础镜像 + 健康检查
  5. 监控完善:集成 Actuator + Prometheus + Grafana

六、参考资料


💡 作者留言:本文实践已在多个生产项目验证,如果有任何问题或建议,欢迎留言交流!

0

评论区