详细介绍:Docker 多服务镜像构建完整教程
2025-12-11 23:52 tlnshuju 阅读(10) 评论(0) 收藏 举报本文将手把手教你如何使用 Docker 多阶段构建技术,将一个 Spring Boot 多模块项目构建成三个独立的服务镜像。适合 Java 全栈开发者学习。
目录
项目背景
我们有一个基于若依框架的 Spring Boot 多模块项目,包含三个服务:
- ayy-server:核心业务服务(端口 48087)
- ayy-snailjob-server:分布式任务调度服务(端口 9090)
- ayy-monitor-admin:监控管理服务(端口 8800, 17888)
项目结构如下:
project-root/
├── pom.xml # 父 POM
├── ruoyi-admin/ # 主服务模块
│ ├── pom.xml
│ └── src/
├── ruoyi-extend/
│ ├── ruoyi-monitor-admin/ # 监控服务模块
│ │ ├── pom.xml
│ │ └── src/
│ └── ruoyi-snailjob-server/ # 任务调度服务模块
│ ├── pom.xml
│ └── src/
├── Dockerfile # 多阶段构建文件
└── .dockerignore # Docker 忽略文件
技术架构
核心技术栈
- Java 17:运行环境
- Maven 3.9.6:项目构建工具
- Spring Boot:应用框架
- Docker Multi-stage Build:多阶段构建
- Alpine Linux:精简基础镜像
为什么使用多阶段构建?
传统方式需要:
- 本地编译 → 生成 JAR
- 编写 Dockerfile → 复制 JAR
- 构建镜像
多阶段构建的优势:
✅ 一个 Dockerfile 完成所有事情
✅ 构建环境与运行环境分离(builder 阶段用完即弃)
✅ 最终镜像体积小(只包含 JRE + JAR)
✅ 利用 Docker 缓存加速构建
✅ 一次构建多个镜像
准备工作
1. 安装 Docker
# macOS
brew install docker
# Ubuntu/Debian
sudo apt-get update
sudo apt-get install docker.io
# 验证安装
docker --version
2. 启用 BuildKit(推荐)
BuildKit 是 Docker 的新一代构建引擎,支持并行构建和高级缓存。
# 临时启用
export DOCKER_BUILDKIT=1
# 永久启用(添加到 ~/.bashrc 或 ~/.zshrc)
echo 'export DOCKER_BUILDKIT=1' >> ~/.bashrc
source ~/.bashrc
3. 项目准备
确保项目根目录有以下文件:
pom.xml(父 POM)Dockerfile.dockerignore
Dockerfile 详解
我们的 Dockerfile 分为 4 个阶段:1 个构建阶段 + 3 个运行阶段。
第一阶段:构建阶段(builder)
# -------------------------------
# 构建阶段:使用 Maven 构建产物
# -------------------------------
FROM maven:3.9.6-eclipse-temurin-17 AS builder
WORKDIR /build
# 先复制 pom 文件,利用 Docker 层缓存
COPY pom.xml .
COPY ruoyi-admin/pom.xml ./ruoyi-admin/
COPY ruoyi-extend/ruoyi-monitor-admin/pom.xml ./ruoyi-extend/ruoyi-monitor-admin/
COPY ruoyi-extend/ruoyi-snailjob-server/pom.xml ./ruoyi-extend/ruoyi-snailjob-server/
# 下载依赖(利用缓存层)
RUN --mount=type=cache,target=/root/.m2 \
mvn dependency:go-offline -B || true
# 复制源码
COPY . .
# 构建(跳过测试)
RUN --mount=type=cache,target=/root/.m2 \
mvn -B clean package -DskipTests
关键点解析:
分层复制 POM:先复制 POM 文件再复制源码
- 依赖不变时,Docker 会使用缓存,避免重复下载
Maven 缓存挂载:
--mount=type=cache,target=/root/.m2- Maven 本地仓库会被缓存,大幅加速后续构建
跳过测试:
-DskipTests- 构建镜像时跳过单元测试,节省时间
第二阶段:ayy-server 运行镜像
# -------------------------------
# 运行阶段:ayy-server
# -------------------------------
FROM eclipse-temurin:17-jre-alpine AS ayy-server
# 创建非 root 用户
RUN addgroup -S adduser && adduser -S adduser -G adduser
WORKDIR /app
# 拷贝构建产物
COPY --from=builder --chown=adduser:adduser /build/ruoyi-admin/target/*.jar ./app.jar
# 创建日志和配置目录
RUN mkdir -p /app/logs /app/config && \
chown -R adduser:adduser /app
# 时区设置
ENV TZ=Asia/Shanghai \
JAVA_OPTS="-Xms512m -Xmx1024m -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -Djava.security.egd=file:/dev/./urandom" \
SPRING_PROFILES_ACTIVE=prod \
SERVER_PORT=48087
EXPOSE 48087
USER adduser
# 健康检查(可选)
HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:${SERVER_PORT}/actuator/health || exit 1
ENTRYPOINT ["sh", "-c", "exec java ${JAVA_OPTS} -Dspring.profiles.active=${SPRING_PROFILES_ACTIVE} -jar app.jar"]
关键点解析:
使用 JRE 而非 JDK:
eclipse-temurin:17-jre-alpine:精简镜像(~180MB)- Alpine Linux:基于 musl libc 的超轻量级发行版
安全性考虑:
- 创建非 root 用户运行应用
- 使用
--chown直接设置文件权限
JVM 参数优化:
-Xms512m -Xmx1024m:堆内存配置-XX:+UseG1GC:使用 G1 垃圾回收器-XX:MaxGCPauseMillis=200:最大停顿时间 200ms
健康检查:
- 需要应用开启 Spring Boot Actuator
- 自动检测服务是否健康
灵活配置:
- 通过环境变量控制 JVM 参数和 Spring Profile
- 运行时可通过
-e参数覆盖
第三、四阶段:另外两个服务
结构与 ayy-server 类似,只是:
- 复制的 JAR 路径不同
- 暴露的端口不同
.dockerignore 配置
.dockerignore 文件告诉 Docker 哪些文件不需要复制到镜像中。
# Maven 构建产物(避免复制本地编译的 target 目录)
target/
*/target/
**/target/
**/dependency-reduced-pom.xml
# IDE 配置文件
.idea/
*.iml
.vscode/
.history/
.eclipse/
.settings/
# Git 相关
.git/
.gitignore
.gitattributes
# 日志文件
**/logs/
*.log
# 临时文件
**/*.tmp
**/*.temp
**/*.swp
**/*.swo
**/*.bak
**/*.DS_Store
*~
# Docker 相关(避免递归)
Dockerfile
docker-compose.yml
.dockerignore
# 文档
README.md
docs/
# CI/CD 配置
.github/
.gitlab-ci.yml
Jenkinsfile
# 前端构建产物
**/node_modules/
**/dist/
# 测试覆盖率
**/coverage/
**/.nyc_output/
# 其他
.gitee/
.run/
.image/
script/
为什么需要 .dockerignore?
- 减少构建上下文大小:避免复制无用文件
- 加快构建速度:传输到 Docker daemon 的数据更少
- 避免缓存失效:无关文件变化不会触发重新构建
构建镜像
方式一:单独构建(适合开发测试)
1. 构建 ayy-server
docker build --target ayy-server -t ayy-server:latest .
命令解析:
--target ayy-server:指定构建目标阶段-t ayy-server:latest:镜像名称和标签.:构建上下文(当前目录)
2. 构建 ayy-snailjob-server
docker build --target ayy-snailjob-server -t ayy-snailjob-server:latest .
3. 构建 ayy-monitor-admin
docker build --target ayy-monitor-admin -t ayy-monitor-admin:latest .
方式二:带版本号构建(适合生产发布)
# 定义版本号
VERSION=1.0.0
# 构建三个镜像(同时打 VERSION 和 latest 标签)
docker build --target ayy-server -t ayy-server:${VERSION} -t ayy-server:latest .
docker build --target ayy-snailjob-server -t ayy-snailjob-server:${VERSION} -t ayy-snailjob-server:latest .
docker build --target ayy-monitor-admin -t ayy-monitor-admin:${VERSION} -t ayy-monitor-admin:latest .
方式三:使用脚本批量构建
创建 build.sh:
#!/bin/bash
set -e
VERSION=${1:-latest}
echo "开始构建镜像,版本: ${VERSION}"
# 启用 BuildKit
export DOCKER_BUILDKIT=1
# 构建 ayy-server
echo "构建 ayy-server..."
docker build --target ayy-server -t ayy-server:${VERSION} -t ayy-server:latest .
# 构建 ayy-snailjob-server
echo "构建 ayy-snailjob-server..."
docker build --target ayy-snailjob-server -t ayy-snailjob-server:${VERSION} -t ayy-snailjob-server:latest .
# 构建 ayy-monitor-admin
echo "构建 ayy-monitor-admin..."
docker build --target ayy-monitor-admin -t ayy-monitor-admin:${VERSION} -t ayy-monitor-admin:latest .
echo "✓ 所有镜像构建完成!"
# 显示镜像列表
docker images | grep -E "ayy-server|ayy-snailjob-server|ayy-monitor-admin"
使用方法:
# 赋予执行权限
chmod +x build.sh
# 构建 latest 版本
./build.sh
# 构建指定版本
./build.sh 1.0.0
验证构建结果
# 查看构建的镜像
docker images | grep ayy
# 输出示例:
# ayy-server latest abc123def456 2 minutes ago 350MB
# ayy-snailjob-server latest def456ghi789 3 minutes ago 340MB
# ayy-monitor-admin latest ghi789jkl012 4 minutes ago 345MB
运行容器
基础运行
1. 运行 ayy-server
docker run -d \
--name ayy-server \
-p 48087:48087 \
ayy-server:latest
参数说明:
-d:后台运行--name ayy-server:容器名称-p 48087:48087:端口映射(宿主机:容器)
2. 运行 ayy-snailjob-server
docker run -d \
--name ayy-snailjob-server \
-p 9090:9090 \
ayy-snailjob-server:latest
3. 运行 ayy-monitor-admin
docker run -d \
--name ayy-monitor-admin \
-p 8800:8800 \
-p 17888:17888 \
ayy-monitor-admin:latest
高级运行(带配置)
docker run -d \
--name ayy-server \
-p 48087:48087 \
-e SPRING_PROFILES_ACTIVE=prod \
-e JAVA_OPTS="-Xms1g -Xmx2g" \
-e SPRING_DATASOURCE_URL="jdbc:mysql://192.168.1.100:3306/ayy?useUnicode=true&characterEncoding=utf8" \
-e SPRING_DATASOURCE_USERNAME=root \
-e SPRING_DATASOURCE_PASSWORD=password \
-e SPRING_REDIS_HOST=192.168.1.100 \
-e SPRING_REDIS_PORT=6379 \
-v /data/logs/ayy-server:/app/logs \
-v /data/config/ayy-server:/app/config:ro \
--restart unless-stopped \
ayy-server:latest
高级参数说明:
-e:环境变量注入SPRING_PROFILES_ACTIVE:Spring 环境配置JAVA_OPTS:JVM 参数SPRING_DATASOURCE_*:数据库配置SPRING_REDIS_*:Redis 配置
-v:数据卷挂载/data/logs/ayy-server:/app/logs:日志持久化/data/config/ayy-server:/app/config:ro:配置文件(只读)
--restart unless-stopped:自动重启策略
查看容器状态
# 查看运行中的容器
docker ps
# 查看容器日志
docker logs -f ayy-server
# 查看容器资源使用情况
docker stats ayy-server
# 进入容器(调试用)
docker exec -it ayy-server sh
停止和清理
# 停止容器
docker stop ayy-server ayy-snailjob-server ayy-monitor-admin
# 删除容器
docker rm ayy-server ayy-snailjob-server ayy-monitor-admin
# 删除镜像
docker rmi ayy-server:latest ayy-snailjob-server:latest ayy-monitor-admin:latest
生产部署
使用 Docker Compose(推荐)
创建 docker-compose.yml:
services:
mysql:
image: mysql:8.0
container_name: ayy-mysql
environment:
MYSQL_ROOT_PASSWORD: your_password
MYSQL_DATABASE: ayy
TZ: Asia/Shanghai
ports:
- "3306:3306"
volumes:
- mysql-data:/var/lib/mysql
networks:
- ayy-network
restart: unless-stopped
redis:
image: redis:7-alpine
container_name: ayy-redis
ports:
- "6379:6379"
volumes:
- redis-data:/data
networks:
- ayy-network
restart: unless-stopped
command: redis-server --appendonly yes
ayy-server:
image: ayy-server:latest
container_name: ayy-server
ports:
- "48087:48087"
environment:
SPRING_PROFILES_ACTIVE: prod
JAVA_OPTS: "-Xms1g -Xmx2g"
SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/ayy?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
SPRING_DATASOURCE_USERNAME: root
SPRING_DATASOURCE_PASSWORD: your_password
SPRING_REDIS_HOST: redis
SPRING_REDIS_PORT: 6379
volumes:
- ./logs/ayy-server:/app/logs
networks:
- ayy-network
depends_on:
- mysql
- redis
restart: unless-stopped
ayy-snailjob-server:
image: ayy-snailjob-server:latest
container_name: ayy-snailjob-server
ports:
- "9090:9090"
environment:
SPRING_PROFILES_ACTIVE: prod
JAVA_OPTS: "-Xms512m -Xmx1g"
volumes:
- ./logs/ayy-snailjob-server:/app/logs
networks:
- ayy-network
depends_on:
- mysql
- redis
restart: unless-stopped
ayy-monitor-admin:
image: ayy-monitor-admin:latest
container_name: ayy-monitor-admin
ports:
- "8800:8800"
- "17888:17888"
environment:
SPRING_PROFILES_ACTIVE: prod
JAVA_OPTS: "-Xms512m -Xmx1g"
volumes:
- ./logs/ayy-monitor-admin:/app/logs
networks:
- ayy-network
depends_on:
- mysql
restart: unless-stopped
networks:
ayy-network:
driver: bridge
volumes:
mysql-data:
redis-data:
使用方法:
# 启动所有服务
docker-compose up -d
# 查看服务状态
docker-compose ps
# 查看日志
docker-compose logs -f ayy-server
# 停止所有服务
docker-compose down
# 停止并删除数据卷
docker-compose down -v
推送到镜像仓库
# 登录镜像仓库(以阿里云为例)
docker login --username=your_username registry.cn-hangzhou.aliyuncs.com
# 打标签
docker tag ayy-server:latest registry.cn-hangzhou.aliyuncs.com/your_namespace/ayy-server:1.0.0
docker tag ayy-snailjob-server:latest registry.cn-hangzhou.aliyuncs.com/your_namespace/ayy-snailjob-server:1.0.0
docker tag ayy-monitor-admin:latest registry.cn-hangzhou.aliyuncs.com/your_namespace/ayy-monitor-admin:1.0.0
# 推送镜像
docker push registry.cn-hangzhou.aliyuncs.com/your_namespace/ayy-server:1.0.0
docker push registry.cn-hangzhou.aliyuncs.com/your_namespace/ayy-snailjob-server:1.0.0
docker push registry.cn-hangzhou.aliyuncs.com/your_namespace/ayy-monitor-admin:1.0.0
生产服务器拉取运行
# 拉取镜像
docker pull registry.cn-hangzhou.aliyuncs.com/your_namespace/ayy-server:1.0.0
# 运行
docker run -d \
--name ayy-server \
-p 48087:48087 \
-e SPRING_PROFILES_ACTIVE=prod \
-v /data/logs:/app/logs \
--restart unless-stopped \
registry.cn-hangzhou.aliyuncs.com/your_namespace/ayy-server:1.0.0
常见问题
Q1: 构建很慢,如何优化?
A: 使用以下技巧:
- 启用 BuildKit
export DOCKER_BUILDKIT=1
- 使用国内 Maven 镜像
在项目根目录创建 .mvn/maven.config:
-Dmaven.repo.local=/root/.m2/repository
在 Dockerfile 添加镜像配置:
RUN mkdir -p /root/.m2 && \
echo 'aliyun * https://maven.aliyun.com/repository/public ' > /root/.m2/settings.xml
- 利用缓存
分层复制 POM 文件,依赖不变时会使用缓存。
Q2: 健康检查一直失败?
A: 检查以下几点:
- 确认应用已开启 Actuator
pom.xml 添加依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
application.yml 配置:
management:
endpoints:
web:
exposure:
include: health
endpoint:
health:
show-details: always
- 临时禁用健康检查
在 Dockerfile 中注释掉 HEALTHCHECK 行。
Q3: 镜像太大怎么办?
A: 优化建议:
- 已使用 Alpine 基础镜像(~180MB)
- 清理 Maven 缓存(仅在构建阶段)
- 移除不必要的依赖
查看镜像层大小:
docker history ayy-server:latest
Q4: 如何查看容器内的日志?
A: 三种方式:
# 方式 1: 查看容器标准输出
docker logs -f ayy-server
# 方式 2: 挂载日志目录(推荐)
docker run -v /data/logs:/app/logs ...
# 方式 3: 进入容器查看
docker exec -it ayy-server sh
cd /app/logs
tail -f application.log
Q5: 如何覆盖配置文件?
A: 使用数据卷挂载:
# 准备配置文件
mkdir -p /data/config/ayy-server
cp application-prod.yml /data/config/ayy-server/
# 挂载配置目录
docker run -v /data/config/ayy-server:/app/config:ro ...
在 application.yml 中配置:
spring:
config:
additional-location: file:/app/config/
Q6: 如何回滚到旧版本?
A: 使用版本标签:
# 停止当前版本
docker stop ayy-server
docker rm ayy-server
# 运行旧版本
docker run -d \
--name ayy-server \
-p 48087:48087 \
ayy-server:1.0.0 # 指定旧版本
总结
本教程涵盖了 Docker 多阶段构建的完整流程:
✅ Dockerfile 编写:多阶段构建、层缓存优化
✅ .dockerignore 配置:减少构建上下文
✅ 镜像构建:单独构建、批量构建、版本管理
✅ 容器运行:基础运行、高级配置、日志管理
✅ 生产部署:Docker Compose、镜像仓库、自动化
浙公网安备 33010602011771号