Docker 部署
将 Spring Boot 应用打包成 Docker 镜像,是现代部署的标准实践。本文涵盖镜像构建、多阶段构建、docker-compose 编排、健康检查等常见场景。
一、Dockerfile 基础写法
简单版(适合快速验证)
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
# 复制 fat jar
COPY target/myapp-*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]生产推荐:分层拷贝
Spring Boot 默认将 fat jar 按依赖/快照/应用代码分层,利用 Docker 层缓存加速增量构建:
# 先解压 fat jar(构建时执行一次)
java -Djarmode=layertools -jar target/myapp.jar extract --destination target/extractedFROM eclipse-temurin:21-jre-alpine AS builder
WORKDIR /extracted
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
RUN java -Djarmode=layertools -jar app.jar extract
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
# 按变化频率从低到高分层(低频层在上,充分利用缓存)
COPY --from=builder /extracted/dependencies/ ./
COPY --from=builder /extracted/spring-boot-loader/ ./
COPY --from=builder /extracted/snapshot-dependencies/ ./
COPY --from=builder /extracted/application/ ./
EXPOSE 8080
ENTRYPOINT ["org.springframework.boot.loader.launch.JarLauncher"]二、多阶段构建(含编译)
将编译和运行环境隔离,最终镜像不含 JDK 和 Maven:
# ── 阶段一:编译 ──────────────────────────────────────────
FROM maven:3.9-eclipse-temurin-21 AS build
WORKDIR /src
# 先复制 pom.xml 下载依赖(利用缓存)
COPY pom.xml .
RUN mvn dependency:go-offline -q
# 再复制源码编译
COPY src ./src
RUN mvn package -DskipTests -q
# ── 阶段二:解压分层 ──────────────────────────────────────
FROM eclipse-temurin:21-jre-alpine AS extract
WORKDIR /extracted
COPY --from=build /src/target/*.jar app.jar
RUN java -Djarmode=layertools -jar app.jar extract
# ── 阶段三:运行镜像 ──────────────────────────────────────
FROM eclipse-temurin:21-jre-alpine
# 创建非 root 用户,提升安全性
RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring
WORKDIR /app
COPY --from=extract /extracted/dependencies/ ./
COPY --from=extract /extracted/spring-boot-loader/ ./
COPY --from=extract /extracted/snapshot-dependencies/ ./
COPY --from=extract /extracted/application/ ./
EXPOSE 8080
# 配置 JVM 参数(适配容器内存限制)
ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0"
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS org.springframework.boot.loader.launch.JarLauncher"]三、使用 Buildpack(无需 Dockerfile)
Spring Boot Maven/Gradle 插件内置 Cloud Native Buildpacks 支持,无需手写 Dockerfile:
# Maven
./mvnw spring-boot:build-image -Dspring-boot.build-image.imageName=myapp:latest
# Gradle
./gradlew bootBuildImage --imageName=myapp:latest<!-- pom.xml:自定义镜像名和基础镜像 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<name>registry.example.com/myapp:${project.version}</name>
<builder>paketobuildpacks/builder-jammy-base</builder>
<env>
<BP_JVM_VERSION>21</BP_JVM_VERSION>
<BPE_JAVA_TOOL_OPTIONS>-XX:MaxRAMPercentage=75.0</BPE_JAVA_TOOL_OPTIONS>
</env>
</image>
</configuration>
</plugin>Buildpack 优势:自动分层、安全修复、无需维护 Dockerfile;劣势:首次构建较慢,定制灵活性略低。
四、docker-compose 本地开发
# docker-compose.yml
services:
app:
build: .
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=docker
- SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/mydb
- SPRING_DATASOURCE_USERNAME=postgres
- SPRING_DATASOURCE_PASSWORD=secret
- SPRING_DATA_REDIS_HOST=redis
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:8080/actuator/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: mydb
POSTGRES_USER: postgres
POSTGRES_PASSWORD: secret
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
command: redis-server --requirepass secret
volumes:
- redis_data:/data
volumes:
postgres_data:
redis_data:# docker-compose.override.yml(本地开发覆盖,不提交到仓库)
services:
app:
volumes:
- ./target:/app/target # 热部署时挂载
environment:
- SPRING_DEVTOOLS_RESTART_ENABLED=true五、配置与密钥管理
通过环境变量覆盖配置
Spring Boot 自动将环境变量映射到配置属性(. → _,大写):
# SPRING_DATASOURCE_URL → spring.datasource.url
docker run -e SPRING_DATASOURCE_URL=jdbc:postgresql://... myappapplication-docker.yml
# src/main/resources/application-docker.yml
spring:
datasource:
url: ${DB_URL:jdbc:postgresql://db:5432/mydb}
username: ${DB_USER:postgres}
password: ${DB_PASSWORD}
data:
redis:
host: ${REDIS_HOST:redis}
password: ${REDIS_PASSWORD}
logging:
level:
root: INFO
# 容器环境输出 JSON 便于日志聚合
pattern:
console: '{"time":"%d","level":"%level","logger":"%logger","msg":"%msg"}%n'Docker Secrets(生产推荐)
# docker-compose.yml(Swarm 模式)
services:
app:
environment:
- DB_PASSWORD_FILE=/run/secrets/db_password
secrets:
- db_password
secrets:
db_password:
external: true六、健康检查与优雅停机
# application.yml
management:
endpoint:
health:
show-details: always
endpoints:
web:
exposure:
include: health,info,metrics
health:
livenessstate:
enabled: true
readinessstate:
enabled: true容器健康检查路径:
| 路径 | 含义 | 用于 |
|---|---|---|
/actuator/health/liveness | 应用是否存活 | Docker HEALTHCHECK / K8s livenessProbe |
/actuator/health/readiness | 应用是否就绪 | K8s readinessProbe |
/actuator/health | 综合健康状态 | 监控面板 |
SIGTERM 信号处理与优雅停机详见 优雅停机;Actuator 端点配置详见 Actuator监控。
七、JVM 容器适配
# 推荐 JVM 参数(Java 11+ 原生支持容器资源限制)
JAVA_OPTS="\
-XX:+UseContainerSupport \ # 读取 cgroup 内存限制(默认已开启)
-XX:MaxRAMPercentage=75.0 \ # 最多使用容器内存的 75%
-XX:InitialRAMPercentage=50.0 \ # 初始堆大小
-XX:+ExitOnOutOfMemoryError \ # OOM 时退出让容器重启
-XX:+HeapDumpOnOutOfMemoryError \ # OOM 时输出 heap dump
-XX:HeapDumpPath=/tmp/heapdump.hprof \
-Dfile.encoding=UTF-8 \
-Duser.timezone=Asia/Shanghai"# Dockerfile 中使用
ENV JAVA_TOOL_OPTIONS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -Dfile.encoding=UTF-8"
JAVA_TOOL_OPTIONS会被所有 JVM 进程自动读取;JAVA_OPTS需要在 ENTRYPOINT 中显式引用。
八、镜像优化建议
| 措施 | 效果 |
|---|---|
| 使用 Alpine/Distroless 基础镜像 | 减少镜像体积 100~200 MB |
| 分层构建(依赖层在前) | 仅代码变更时无需重新下载依赖层 |
| 非 root 用户运行 | 降低安全风险 |
.dockerignore 排除无关文件 | 缩短构建上下文传输时间 |
| 多阶段构建排除 JDK/Maven | 最终镜像仅含 JRE |
| GraalVM 原生镜像 | 极致压缩,启动 < 100 ms |
.dockerignore 示例:
.git
.gitignore
target/
*.md
*.log
.idea/
*.iml
常用命令
# 构建镜像
docker build -t myapp:1.0.0 .
# 运行容器
docker run -d \
--name myapp \
-p 8080:8080 \
-e SPRING_PROFILES_ACTIVE=docker \
--memory=512m \
--cpus=1.0 \
myapp:1.0.0
# 查看日志
docker logs -f myapp
# 进入容器
docker exec -it myapp sh
# 启停(docker-compose)
docker-compose up -d
docker-compose down -v # -v 同时删除 volume相关链接
- K8s部署 — 将容器部署到 Kubernetes
- 优雅停机 — SIGTERM 处理与滚动更新
- GraalVM原生编译 — 编译为原生可执行文件,镜像体积更小
- Actuator监控 — 健康检查端点配置
- 配置管理 — 多环境配置与外部化配置
- 日志 — 容器环境日志格式与采集
- 链路追踪 — 分布式追踪与容器部署