Docker 部署 SpringBoot 项目实战(生产环境可用)
引言:随着微服务架构的普及,Docker 因其轻量、可移植、隔离性强的特性,已成为 Java 后端项目部署的主流方案。本文针对有基础的 Java 后端开发者,提供一套生产环境可用的 SpringBoot 项目 Docker 部署完整流程,涵盖环境准备、镜像构建、容器编排、问题排查四大核心环节,所有代码可直接复制使用,同时记录实战中的典型踩坑点及解决方案。
一、环境准备:生产环境基础配置
1.1 服务器环境要求
生产环境推荐配置(最小化):
-
操作系统:CentOS 7.x / Ubuntu 20.04 LTS(64位)
-
内存:2GB+(建议4GB及以上,支撑 SpringBoot 运行+容器基础占用)
-
磁盘:20GB+ 可用空间
-
网络:确保 80/443 端口开放(业务端口)、2375/2376 端口(Docker 远程访问,生产环境建议关闭公网访问)
1.2 Docker 环境安装与配置
以下以 CentOS 7 为例,提供 Docker 官方推荐的安装步骤(可直接复制执行):
# 1. 卸载旧版本(若有)
yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine
# 2. 安装依赖包
yum install -y yum-utils device-mapper-persistent-data lvm2
# 3. 设置 Docker 官方镜像源(国内推荐阿里云镜像源,速度更快)
yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# 4. 安装 Docker CE(社区版,生产环境足够使用)
yum install -y docker-ce docker-ce-cli containerd.io
# 5. 启动 Docker 并设置开机自启
systemctl start docker
systemctl enable docker
# 6. 验证 Docker 安装成功
docker --version # 输出类似:Docker version 24.0.7, build afdd53b
docker run hello-world # 运行测试镜像,输出 "Hello from Docker!" 即成功
1.3 SpringBoot 项目预处理
确保 SpringBoot 项目满足以下条件(生产环境适配):
-
项目已打包为 Jar 包(通过
mvn clean package -Dmaven.test.skip=true构建,跳过测试) -
配置文件(application.yml/properties)中,端口、数据库连接等参数可通过环境变量注入(避免硬编码)
-
排除不必要的依赖(减小 Jar 包体积,提升镜像构建速度)
示例配置(application.yml,通过环境变量注入端口和数据库地址):
server:
port: ${SERVER_PORT:8080} # 默认8080,可通过环境变量覆盖
spring:
datasource:
url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:test}?useSSL=false&serverTimezone=Asia/Shanghai
username: ${DB_USER:root}
password: ${DB_PASS:root}
二、镜像构建:生产级 Dockerfile 编写与优化
2.1 编写生产级 Dockerfile
在 SpringBoot 项目根目录创建 Dockerfile(注意首字母大写),采用多阶段构建(减小镜像体积,避免冗余依赖):
# 第一阶段:构建阶段(使用 Maven 镜像编译项目,仅用于生成 Jar 包)
FROM maven:3.8.8-openjdk-17 AS builder
# 设置工作目录
WORKDIR /app
# 复制项目 pom.xml 文件(先复制 pom.xml,利用 Docker 缓存机制,提升后续构建速度)
COPY pom.xml .
# 复制项目源码
COPY src ./src
# 编译打包(跳过测试,生产环境构建建议跳过)
RUN mvn clean package -Dmaven.test.skip=true
# 第二阶段:运行阶段(使用轻量级 OpenJDK 镜像,减小最终镜像体积)
FROM openjdk:17-jdk-slim
# 设置时区(生产环境必须配置,避免时间相关问题)
ENV TZ=Asia/Shanghai
# 创建非 root 用户(生产环境禁止使用 root 运行容器,提升安全性)
RUN addgroup --system appgroup && adduser --system appuser --ingroup appgroup
# 设置工作目录
WORKDIR /app
# 从构建阶段复制 Jar 包到当前镜像(注意 Jar 包名称需与项目实际一致)
COPY --from=builder /app/target/*.jar app.jar
# 切换为非 root 用户
USER appuser
# 启动命令(指定 JVM 优化参数,生产环境必备)
ENTRYPOINT ["java", "-Xms512m", "-Xmx1024m", "-jar", "app.jar"]
关键说明:
-
多阶段构建:第一阶段使用 Maven 镜像编译,第二阶段仅保留运行所需的 JDK 和 Jar 包,最终镜像体积可从数百 MB 减小到 200MB 左右
-
非 root 用户运行:避免容器内进程拥有过高权限,降低安全风险
-
JVM 参数优化:
-Xms512m(初始堆内存)、-Xmx1024m(最大堆内存),根据服务器内存调整 -
时区配置:解决容器内时间与主机不一致的问题,避免日志、业务时间错乱
2.2 镜像构建命令与验证
将 SpringBoot 项目的 Dockerfile 和 Jar 包(若未使用多阶段构建,需提前将 Jar 包上传到服务器)上传到服务器的同一目录,执行以下命令构建镜像:
# 构建镜像,-t 指定镜像名称:版本号(建议版本号语义化,如 v1.0.0)
docker build -t springboot-demo:v1.0.0 .
# 验证镜像是否构建成功
docker images | grep springboot-demo
# 输出类似:springboot-demo v1.0.0 xxxxxxxx 2 minutes ago 215MB
2.3 镜像优化技巧(生产环境必看)
-
使用
.dockerignore文件:排除不需要复制到镜像的文件(如 .git、target/*、logs 等),减少构建上下文大小,提升构建速度。示例.dockerignore:.git.gitignoretarget/*logs*.md -
选择轻量级基础镜像:优先使用
openjdk:xx-jdk-slim而非完整版openjdk:xx-jdk,或更轻量的alpine版本(注意 alpine 版本可能存在字体、依赖问题,生产环境需测试验证) -
合并 RUN 命令:使用
&& 合并多个 RUN 指令,减少镜像分层(每一个 RUN 指令都会创建一个镜像层,分层过多会增加镜像体积)
三、容器编排:Docker Compose 生产级部署
生产环境中,SpringBoot 项目通常需要依赖数据库、Redis 等中间件,使用 Docker Compose 可实现多容器的统一编排、启动、停止,简化部署流程。
3.1 安装 Docker Compose
# 下载 Docker Compose(适用于 Linux x86_64 架构)
curl -L "https://github.com/docker/compose/releases/download/v2.23.3/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
# 赋予执行权限
chmod +x /usr/local/bin/docker-compose
# 验证安装成功
docker-compose --version
# 输出类似:Docker Compose version v2.23.3
3.2 编写 docker-compose.yml
在项目目录创建 docker-compose.yml 文件,包含 SpringBoot 服务、MySQL 服务(示例依赖),配置持久化、网络、环境变量等生产级特性:
version: '3.8' # Compose 文件版本,与 Docker 版本兼容(参考官方文档)
services:
# SpringBoot 服务
springboot-app:
image: springboot-demo:v1.0.0 # 镜像名称:版本号,与构建时一致
container_name: springboot-app # 容器名称,便于管理
restart: always # 生产环境必备:容器异常退出时自动重启
ports:
- "8080:8080" # 端口映射:主机端口:容器端口(生产环境建议避免使用默认端口,如 8080 可改为 80)
environment:
- SERVER_PORT=8080 # 覆盖应用端口
- DB_HOST=mysql # 数据库主机名(与下方 MySQL 服务名一致,Docker 内部可通过服务名访问)
- DB_PORT=3306
- DB_NAME=demo_db
- DB_USER=demo_user
- DB_PASS=Demo@123456 # 生产环境密码建议通过环境变量注入,避免硬编码
volumes:
- ./logs:/app/logs # 日志持久化:主机目录./logs 挂载到容器 /app/logs(便于查看日志)
- ./config:/app/config # 配置文件挂载(生产环境可通过挂载修改配置,无需重新构建镜像)
networks:
- app-network # 加入自定义网络,与其他服务隔离
depends_on:
- mysql # 依赖 MySQL 服务,确保 MySQL 先启动
# MySQL 服务(生产环境建议使用主从架构,此处为单节点示例)
mysql:
image: mysql:8.0.33
container_name: mysql
restart: always
ports:
- "3306:3306"
environment:
- MYSQL_ROOT_PASSWORD=Root@123456
- MYSQL_DATABASE=demo_db # 初始化数据库名称
- MYSQL_USER=demo_user # 初始化用户
- MYSQL_PASSWORD=Demo@123456
volumes:
- mysql-data:/var/lib/mysql # 数据持久化:避免容器删除后数据丢失
- ./mysql/init.sql:/docker-entrypoint-initdb.d/init.sql # 初始化 SQL 脚本(创建表、插入初始数据)
networks:
- app-network
command: --default-authentication-plugin=mysql_native_password # 解决 MySQL 8.0 密码认证问题
# 自定义网络(桥接模式,隔离不同服务)
networks:
app-network:
driver: bridge
# 数据卷(持久化数据)
volumes:
mysql-data:
3.3 服务启停与状态查看
# 启动所有服务(后台运行,-d 参数)
docker-compose up -d
# 查看服务状态
docker-compose ps
# 输出类似:
# Name Command State Ports
# --------------------------------------------------------------------------------
# mysql docker-entrypoint.sh --def ... Up 0.0.0.0:3306->3306/tcp
# springboot-app java -Xms512m -Xmx1024m -ja ... Up 0.0.0.0:8080->8080/tcp
# 查看 SpringBoot 服务日志(实时日志,-f 参数)
docker-compose logs -f springboot-app
# 停止所有服务(保留容器和数据)
docker-compose stop
# 停止并删除容器、网络(数据卷不会删除)
docker-compose down
# 停止并删除容器、网络、数据卷(谨慎使用,会丢失持久化数据)
docker-compose down -v
3.4 生产环境额外配置
-
配置 Docker Compose 开机自启:
# 创建系统服务文件vi /etc/systemd/system/docker-compose-app.service# 文件内容(注意修改 WorkingDirectory 为 docker-compose.yml 所在目录)[Unit]Description=Docker Compose App ServiceRequires=docker.serviceAfter=docker.service[Service]Type=simpleWorkingDirectory=/usr/local/springboot-demoExecStart=/usr/local/bin/docker-compose up -dExecStop=/usr/local/bin/docker-compose downRestart=always[Install]WantedBy=multi-user.target# 启用并启动服务systemctl enable docker-compose-appsystemctl start docker-compose-app# 验证服务状态systemctl status docker-compose-app -
日志轮转:生产环境日志会持续增长,需配置日志轮转(修改 /etc/logrotate.d/docker-compose-app),避免磁盘占满
四、问题排查与踩坑记录
4.1 典型问题及解决方案
问题 1:端口冲突(容器启动失败,提示 port is already allocated)
现象:执行 docker-compose up 时,提示 Error starting userland proxy: listen tcp4 0.0.0.0:8080: bind: address already in use
原因:主机 8080 端口已被其他进程占用
解决方案:
# 1. 查找占用端口的进程
netstat -tuln | grep 8080 # 或 lsof -i:8080
# 输出类似:tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN 1234/java
# 2. 若进程无用,停止进程
kill -9 1234 # 1234 为进程 ID
# 3. 若进程有用,修改 SpringBoot 服务的端口映射(修改 docker-compose.yml)
ports:
- "8081:8080" # 主机端口改为 8081,容器端口保持 8080
问题 2:权限不足(日志无法写入、配置文件无法读取)
现象:SpringBoot 服务启动后,日志文件未生成,或提示 Permission denied 无法读取配置文件
原因:Docker 挂载目录时,主机目录的权限不足,容器内非 root 用户无法读写
解决方案:
# 1. 查看主机挂载目录的权限
ls -ld ./logs
# 输出类似:drwxr-xr-x 2 root root 4096 Nov 1 10:00 ./logs(root 用户拥有权限)
# 2. 修改目录权限,允许其他用户读写(生产环境建议更精细的权限控制,而非 777)
chmod 777 -R ./logs ./config
# 3. (推荐)修改目录所有者为容器内的 appuser(需先知道 appuser 的 UID)
# 查看容器内 appuser 的 UID(进入容器执行)
docker exec -it springboot-app id appuser
# 输出类似:uid=1001(appuser) gid=1001(appgroup) groups=1001(appgroup)
# 4. 主机修改目录所有者
chown -R 1001:1001 ./logs ./config
问题 3:镜像构建失败(Maven 依赖下载超时)
现象:执行 docker build 时,Maven 下载依赖缓慢或超时,提示 Failed to transfer file...
原因:默认使用 Maven 中央仓库,国内网络访问缓慢
解决方案:在 pom.xml 中配置阿里云 Maven 镜像:
<repositories>
<repository>
<id>aliyunmaven</id>
<url>https://maven.aliyun.com/repository/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>aliyunmaven</id>
<url>https://maven.aliyun.com/repository/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
4.2 验证部署效果
部署完成后,需从以下几个维度验证效果(生产环境必做):
-
验证容器运行状态:
docker-compose ps # 确保所有服务 State 为 Up -
验证接口可用性: 假设 SpringBoot 项目有一个健康检查接口
/actuator/health,执行以下命令验证:curl http://localhost:8080/actuator/health# 输出类似:{"status":"UP"} 即接口正常截图说明:打开浏览器访问http://服务器公网IP:8080/actuator/health,页面显示{"status":"UP"},说明服务正常运行。 -
验证数据持久化: 1. 向 MySQL 数据库插入一条测试数据(通过项目接口或直接连接 MySQL);2. 执行
docker-compose down停止服务,再执行docker-compose up -d重启服务;3. 查询数据是否存在,若存在则数据持久化成功。截图说明:通过 Navicat 等工具连接服务器 MySQL(地址:服务器公网IP,端口:3306,用户:demo_user),查询测试数据是否存在。
五、总结与拓展
5.1 核心总结
本文提供了一套生产环境可用的 Docker 部署 SpringBoot 项目完整流程,核心要点:
-
环境准备:优先选择稳定的操作系统,正确安装 Docker 并配置开机自启;
-
镜像构建:采用多阶段构建减小镜像体积,使用非 root 用户提升安全性,通过
.dockerignore优化构建流程; -
容器编排:使用 Docker Compose 管理多容器服务,配置持久化、自动重启、自定义网络等生产级特性;
-
问题排查:重点关注端口冲突、权限不足、依赖下载等典型问题,掌握日志查看、进程查询等排查技巧。
5.2 拓展学习资源
-
Docker 官方文档:https://docs.docker.com/(权威指南,涵盖所有核心特性);
-
Docker Compose 官方文档:https://docs.docker.com/compose/(详细介绍多容器编排技巧);
-
SpringBoot 官方部署指南:https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#deploying(SpringBoot 官方推荐的部署方案);
-
《Docker 实战》(书籍):深入讲解 Docker 在生产环境的应用场景和最佳实践。
后续可拓展学习:Docker 镜像仓库(Harbor)搭建、Docker 容器监控(Prometheus + Grafana)、Kubernetes 容器编排(适用于大规模集群部署)。
浙公网安备 33010602011771号