项目如何用Docker一键部署?(个人常见避坑指南)
【避坑实录】Vue3+SpringBoot项目Docker化部署:从403报错、连接拒绝到一键启动的完整复盘
前言:为什么要折腾Docker?
作为学生,欢迎各位访问我的初次正式项目WenJi并指出问题,感激不尽🙏
最近完成了项目的开发。在本地开发时,一切都很美好:IDEA启动后端,Vite启动前端,联调顺畅。
但当我想把项目部署到服务器,或者打包发给同学演示时,问题来了:
- 对方电脑没装JDK 17怎么办?
- 本地MySQL版本太低不支持怎么办?
- Nginx反向代理配置太麻烦,不想在服务器上手动改配置文件怎么办?
为了解决这些“环境不一致”的噩梦,我决定尝试学习采用 Docker 容器化技术。本以为
docker-compose up一下就能搞定,结果从镜像源配置到网络通信,一路踩雷。本文将结合“文及”项目的实际代码,分享从本地开发到 Docker 化的完整实战过程,重点记录那些**“如果不注意就会卡一两天”**的坑。
项目架构与目录规划
为了实现容器化编排,我们需要对项目结构进行标准化梳理。针对“文及”项目,我设计了如下的目录结构,核心在于通过 docker-compose.yml 统一管理所有服务:
WenJi/
├── docker-compose.yml # 核心编排文件
├── .env # 环境变量(存放数据库密码等敏感信息)
├── backend/ # 后端 SpringBoot 模块
│ ├── Dockerfile # 后端镜像构建脚本
│ └── src/main/resources/
│ └── sql/ # 数据库初始化脚本
└── frontend/ # 前端 Vue3 模块
├── Dockerfile # 前端镜像构建脚本
└── nginx.conf # Nginx 反向代理配置
踩坑实录与解决方案
坑点一:镜像下载慢与403报错(网络问题)
现象:执行 docker-compose up 时,控制台报错:
ERROR: failed to solve: pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed
或者下载速度只有几KB/s,甚至直接超时。
原因分析:
- Docker Hub 官方源在国内访问受限。
- 很多网上的教程推荐的加速器(如某些公共源)现在需要登录认证,导致匿名访问时被拒绝(403 Forbidden)。
** 解决方案**:
推荐使用中科大或网易的公共镜像源,这两个目前不需要认证且比较稳定。
操作步骤:
- 打开 Docker Desktop -> Settings -> Docker Engine。
- 修改
registry-mirrors配置:
{
"registry-mirrors": [
"https://docker.mirrors.ustc.edu.cn",
"https://hub-mirror.c.163.com"
]
}
- 点击 Apply & Restart。
- 关键一步:清理缓存,否则 Docker 还会尝试用旧的错误地址。
docker system prune -a -f
坑点二:OpenJDK镜像失效(基础镜像问题)
现象:在构建后端镜像时,提示 openjdk:17-jdk-slim 拉取失败,或者拉下来的镜像运行报错。
原因分析:
Oracle 修改了 OpenJDK 的授权协议,Docker Hub 上的官方 openjdk 镜像已经不再维护或存在兼容性问题。
** 解决方案**:
换成 Eclipse 基金会维护的 eclipse-temurin 镜像(OpenJDK 的开源发行版,完全兼容)。
修改后端 Dockerfile:
# --- 运行阶段 ---
# 推荐使用 Alpine 版本,体积仅为 200MB 左右
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
# 复制构建好的 jar 包
COPY target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
坑点三:后端连不上数据库(网络通信问题)
现象:后端容器启动后,日志疯狂报错:
java.sql.SQLException: Communications link failure
...
Caused by: java.net.ConnectException: Connection refused
原因分析:
这是新手最容易踩的坑!在 SpringBoot 的 application.yml 或环境变量中,我们习惯写 jdbc:mysql://localhost:3306...。
但在 Docker 容器内部,localhost 指的是容器自己,而不是宿主机,更不是旁边的 MySQL 容器。
** 解决方案**:
使用 Docker Compose 的服务名(Service Name)作为主机名。Docker Compose 会自动构建一个内部 DNS,让容器之间可以通过服务名互相访问。
修改 docker-compose.yml:
services:
# 1. 数据库服务
wenji-mysql:
image: mysql:8.0
container_name: wenji-mysql
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: wenji_db
volumes:
- mysql_data:/var/lib/mysql
# 注意:这里的服务名 wenji-mysql 就是后端要用的 host
# 2. 后端服务
wenji-backend:
build: ./backend
container_name: wenji-backend
depends_on:
- wenji-mysql
environment:
# 关键点:使用容器名 wenji-mysql 代替 localhost
# 端口使用容器内部端口 3306,不要用宿主机的映射端口
SPRING_DATASOURCE_URL: jdbc:mysql://wenji-mysql:3306/wenji_db
SPRING_DATASOURCE_USERNAME: root
SPRING_DATASOURCE_PASSWORD: ${MYSQL_ROOT_PASSWORD}
ports:
- "8080:8080"
坑点四:容器重启数据丢失(数据持久化问题)
现象:在“文及”项目里新建了文档,重启容器(docker-compose restart)或者删除容器后,再次访问发现数据全没了,回到了初始状态。
原因分析:
容器是“用完即扔”的 ephemeral(临时)环境。MySQL 默认将数据存储在容器内的 /var/lib/mysql 目录。一旦容器被删除,这个目录也就随之消失了。
** 解决方案**:
使用 Volume(数据卷) 挂载,将容器内的数据目录映射到宿主机(或 Docker 管理的命名卷)。
修改 docker-compose.yml:
services:
wenji-mysql:
# ...其他配置
volumes:
- mysql_data:/var/lib/mysql # 使用命名卷,Docker 会自动管理存储位置
# 或者使用绑定挂载:- ./mysql_data:/var/lib/mysql
# 在文件末尾定义卷
volumes:
mysql_data:
这样,即使你把容器删得干干净净,数据依然安全地保存在 mysql_data 卷中,下次启动新容器挂载该卷,数据依然存在。
坑点五:中文乱码(编码与字体问题)
现象:
项目启动后,前端页面显示的中文变成问号,终端查询数据库数据和查看后端日志时,中文日志显示为乱码。
原因分析:
这是一个“连环坑”,涉及三个层面:
- MySQL 层面:Docker 官方 MySQL 镜像默认字符集可能是
latin1,不支持中文。 - 操作系统层面:为了追求镜像体积最小化,后端使用的
Alpine或精简版Ubuntu镜像通常默认不包含中文字体库,且系统语言环境(Locale)默认为POSIX或C,不支持 UTF-8。 - JVM 层面:Java 应用如果没有显式指定编码,会继承操作系统的默认编码,导致读写文件时出现乱码。
解决方案:
1. 修改 MySQL 配置
在项目中创建一个 my.cnf 配置文件,并挂载到容器中,强制指定字符集为 utf8mb4。
-
backend/conf/my.cnf内容:[mysqld] character-set-server=utf8mb4 collation-server=utf8mb4_unicode_ci [client] default-character-set=utf8mb4 -
修改
docker-compose.yml挂载配置:services: wenji-mysql: volumes: - ./backend/conf/my.cnf:/etc/mysql/conf.d/my.cnf # 挂载自定义配置 - mysql_data:/var/lib/mysql
2. 修复后端镜像的 Locale 与字体
修改后端的 Dockerfile,安装中文字体支持并设置环境变量。
-
修改 backend/Dockerfile:
FROM eclipse-temurin:17-jre-alpine # 1. 安装字体库和中文字体包 RUN apk add --no-cache ttf-dejavu fontconfig && \ apk add --no-cache font-noto-cjk # 2. 设置系统语言环境变量 ENV LANG=zh_CN.UTF-8 ENV LC_ALL=zh_CN.UTF-8 WORKDIR /app COPY target/*.jar app.jar # 3. 启动时强制指定 JVM 文件编码 ENTRYPOINT ["java", "-Dfile.encoding=UTF-8", "-jar", "app.jar"]
最终成果:一键启动的快感
经过上述避坑,我们的 docker-compose.yml 终于能完美工作了。现在,无论是部署到服务器,还是发给同学演示,只需要做一件事:
在项目根目录执行:
docker-compose up -d
Docker Compose 会自动:
- 拉取 MySQL、Nginx 基础镜像。
- 根据 Dockerfile 构建后端和前端的自定义镜像。
- 启动容器,并按照
depends_on的顺序确保数据库先于后端启动。 - 通过 Nginx 反向代理,用户访问
http://localhost即可直接访问前端,前端请求/api时会自动转发到后端容器。
总结
通过这次“文及”项目的 Docker 化实践,我深刻体会到了容器化技术的优势,也明白了“环境一致性”的重要性。
- 网络问题:善用国内镜像源,避开 403 陷阱。
- 基础镜像:弃用官方 OpenJDK,拥抱 Eclipse Temurin。
- 网络通信:容器间通信用服务名,别用 localhost。
- 数据持久化:数据库必须挂载 Volume,否则数据必丢。
Docker 不仅仅是部署工具,更是解决“在我电脑上能跑,服务器上就挂了”这一难题,虽然一定程度上确实花了不少时间去熟悉操作。希望这篇实战记录能帮你省下一下午的调试时间!
如果你觉得这篇文章帮你节省了配置环境的时间,别忘了点赞、收藏、关注!
在部署过程中你遇到了哪些奇葩问题?欢迎在评论区留言,我们一起讨论解决!
更多推荐

所有评论(0)