前言

作为一个刚毕业的计算机专业学生,我一直在思考如何把自己的毕设项目——基于 SpringBoot+Vue 的在线考试系统——从本地开发环境迁移到生产环境。传统的部署方式需要手动配置 JDK、MySQL、Nginx,换台机器就要重新折腾一遍,效率低且容易出错。

直到我接触了 Docker,这一切变得简单起来。

这篇文章记录了我将整个考试系统 Docker 化的完整过程:从编写 Dockerfile、本地多容器联调,到推送到阿里云镜像仓库,最后让其他人也能一键部署。

如果你也想把自己的项目容器化,希望这篇实战记录能帮你少走弯路。

一、项目背景

我的在线考试系统采用前后端分离架构:

后端:Spring Boot 2.x + MyBatis-Plus + MySQL 5.7

前端:Vue 2 + Element UI

数据库:MySQL 5.7,数据库名 online_exam_2026

在本地开发时,我直接运行 mvn spring-boot:run 和 npm run serve,但这种方式有几个问题:

1. 依赖环境不一致,换台电脑可能跑不起来

2. 数据库需要手动安装配置

3. 部署到服务器需要重复配置环境

Docker 化可以一次性解决这些问题。

二、环境准备

2.1 操作系统

我使用的是 CentOS 7,运行在 VMware 虚拟机中。

2.2 安装 Docker

CentOS 7 默认仓库里的 Docker 版本太旧(1.13),需要先卸载旧版本,再安装 Docker CE 26.1.4。

// bash
# 卸载旧版本
sudo yum remove docker docker-common docker-selinux docker-engine

# 安装依赖
sudo yum install -y yum-utils device-mapper-persistent-data lvm2

# 添加 Docker 仓库
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

# 安装 Docker CE
sudo yum install -y docker-ce docker-ce-cli containerd.io

# 启动并设置开机自启
sudo systemctl start docker
sudo systemctl enable docker

# 验证安装
docker --version
# 输出:Docker version 26.1.4, build ...

踩坑记录:CentOS 7 的内核版本较低,安装新版 Docker 时可能会遇到兼容性问题。我参考了 Docker 官方文档,使用 --setopt=obsoletes=0 参数解决了依赖冲突。

三、项目打包

3.1 后端打包

Spring Boot 项目需要打包成可执行的 JAR 文件。

踩坑警告:确保 pom.xml 中配置了 spring-boot-maven-plugin,否则打包出来的 JAR 没有主类清单,运行时会报错 no main manifest attribute

// xml
<!-- pom.xml 中必须包含 -->
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

执行打包命令:

// bash
cd ~/exam-system-docker/ExamSystem/OnlineExamSystemApi
mvn clean package -DskipTests

打包成功后,target/ 目录下会生成 OnlineExamSystemApi-1.0.jar

3.2 前端打包

Vue 项目需要构建生成静态文件。

// bash
cd ~/exam-system-docker/ExamSystem/OnlineExamSystemVue
npm install
npm run build

构建完成后,dist/ 目录包含所有静态资源。

四、编写 Dockerfile

4.1 后端 Dockerfile

我采用多阶段构建(multi-stage build),第一阶段用 Maven 编译,第二阶段用轻量的 JRE 运行,这样可以显著减小镜像体积。

// dockerfile
# 阶段1:Maven 构建
FROM maven:3.8-openjdk-8 AS builder
WORKDIR /build

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

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

# 阶段2:运行环境
FROM openjdk:8-jre-slim
WORKDIR /app

# 从构建阶段复制 jar 包
COPY --from=builder /build/target/*.jar app.jar

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "app.jar"]

关键设计点

使用 maven:3.8-openjdk-8 作为构建环境,确保 JDK 版本一致

先复制 pom.xml 并下载依赖,利用 Docker 层缓存,后续构建更快

运行阶段使用 openjdk:8-jre-slim,体积更小

最终镜像大小:656MB

4.2 前端 Dockerfile

同样使用多阶段构建,Node.js 编译,nginx 提供静态文件服务。

// dockerfile
# 阶段1:Node 构建
FROM node:14 AS builder
WORKDIR /build

# 复制依赖文件
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile

# 复制源码并构建
COPY . .
RUN yarn build

# 阶段2:nginx 提供静态文件
FROM nginx:alpine

# 复制构建产物
COPY --from=builder /build/dist /usr/share/nginx/html

# 复制 nginx 配置
COPY nginx.conf /etc/nginx/conf.d/default.conf

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]

4.3 Nginx 配置

前端需要反向代理后端 API,配置如下:

// nginx
server {
    listen 80;
    server_name localhost;

    # 前端静态文件
    location / {
        root /usr/share/nginx/html;
        index index.html;
        try_files $uri $uri/ /index.html;
    }

    # 反向代理后端 API
    location /api/ {
        proxy_pass http://backend:8080/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

注意proxy_pass http://backend:8080/ 中的 backend 是 docker-compose 中定义的服务名,Docker 网络会自动解析。

最终镜像大小:74.7MB

五、docker-compose 编排

三个容器需要协同工作,使用 docker-compose 可以一键启动。

// yaml
version: '3.8'

services:
  # MySQL 数据库
  mysql:
    image: mysql:5.7
    container_name: exam-mysql
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: online_exam_2026
    ports:
      - "3306:3306"
    volumes:
      - mysql-data:/var/lib/mysql
      - ./online_exam.sql:/docker-entrypoint-initdb.d/init.sql:ro
    networks:
      - exam-network
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5

  # 后端 API
  backend:
    build: ./OnlineExamSystemApi
    container_name: exam-backend
    ports:
      - "8080:8080"
    environment:
      SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/online_exam_2026?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
      SPRING_DATASOURCE_USERNAME: root
      SPRING_DATASOURCE_PASSWORD: root
    depends_on:
      mysql:
        condition: service_healthy
    networks:
      - exam-network
    restart: on-failure

  # 前端
  frontend:
    build: ./OnlineExamSystemVue
    container_name: exam-frontend
    ports:
      - "80:80"
    depends_on:
      - backend
    networks:
      - exam-network

volumes:
  mysql-data:

networks:
  exam-network:
    driver: bridge

关键配置说明

1. MySQL 容器

使用 healthcheck 确保数据库完全启动后再启动后端

自动导入 online_exam.sql 初始化数据库

数据持久化到 mysql-data 卷

2. 后端容器

通过环境变量覆盖数据库连接配置,指向 mysql 容器

depends_on 配合 healthcheck 确保启动顺序

3. 前端容器

映射 80 端口,访问 http://localhost 即可

六、本地构建与启动

6.1 构建镜像

// bash
cd ~/exam-system-docker/ExamSystem
docker-compose build

首次构建会下载基础镜像,需要一些时间。后续构建会利用缓存,速度很快。

6.2 启动所有服务

// bash
docker-compose up -d

-d 参数表示后台运行。

6.3 查看运行状态

// bash
docker-compose ps

输出:

Name                Command               State           Ports
----------------------------------------------------------------------
exam-backend    java -jar app.jar         Up      0.0.0.0:8080->8080/tcp
exam-frontend   nginx -g daemon off;      Up      0.0.0.0:80->80/tcp
exam-mysql      docker-entrypoint.sh      Up      0.0.0.0:3306->3306/tcp

6.4 访问系统

打开浏览器,访问 http://192.168.120.131(你的虚拟机 IP),即可看到考试系统登录页面。

默认账号

管理员:9991 / 123456

教师:20081001 / 123456

学生:2022084 / 123456

七、推送到阿里云镜像仓库

本地部署成功后,我把镜像推送到阿里云容器镜像服务(ACR),方便在其他机器上部署。

7.1 登录阿里云 ACR

// bash
docker login crpi-paclr97bkf1e2svc.cn-qingdao.personal.cr.aliyuncs.com --username=nick0477683133

输入密码后看到 Login Succeeded 表示登录成功。

7.2 打 tag

// bash
# 后端镜像
docker tag examsystem-backend:latest crpi-paclr97bkf1e2svc.cn-qingdao.personal.cr.aliyuncs.com/fantastic6-exam/examsystem-backend:v1

# 前端镜像
docker tag examsystem-frontend:latest crpi-paclr97bkf1e2svc.cn-qingdao.personal.cr.aliyuncs.com/fantastic6-exam/examsystem-frontend:v1

踩坑记录:命名空间必须是阿里云控制台中已创建的,否则会报 denied: requested access to the resource is denied。我的命名空间是 fantastic6-exam,需要提前在控制台创建。

7.3 推送镜像

// bash
docker push crpi-paclr97bkf1e2svc.cn-qingdao.personal.cr.aliyuncs.com/fantastic6-exam/examsystem-backend:v1
docker push crpi-paclr97bkf1e2svc.cn-qingdao.personal.cr.aliyuncs.com/fantastic6-exam/examsystem-frontend:v1

后端镜像 656MB,前端镜像 74.7MB,推送需要几分钟,取决于网络速度。

八、其他人如何部署

现在任何人都可以一键部署我的考试系统了!

8.1 前提条件

安装 Docker 和 docker-compose

网络能访问阿里云镜像仓库

8.2 创建 docker-compose.yml

在任意目录创建 docker-compose.yml

// yaml
version: '3.8'

services:
  mysql:
    image: mysql:5.7
    container_name: exam-mysql
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: online_exam_2026
    ports:
      - "3306:3306"
    volumes:
      - mysql-data:/var/lib/mysql
    networks:
      - exam-network
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5

  backend:
    image: crpi-paclr97bkf1e2svc.cn-qingdao.personal.cr.aliyuncs.com/fantastic6-exam/examsystem-backend:v1
    container_name: exam-backend
    ports:
      - "8080:8080"
    environment:
      SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/online_exam_2026?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
      SPRING_DATASOURCE_USERNAME: root
      SPRING_DATASOURCE_PASSWORD: root
    depends_on:
      mysql:
        condition: service_healthy
    networks:
      - exam-network
    restart: on-failure

  frontend:
    image: crpi-paclr97bkf1e2svc.cn-qingdao.personal.cr.aliyuncs.com/fantastic6-exam/examsystem-frontend:v1
    container_name: exam-frontend
    ports:
      - "80:80"
    depends_on:
      - backend
    networks:
      - exam-network

volumes:
  mysql-data:

networks:
  exam-network:
    driver: bridge

注意:这里使用 image 直接引用阿里云镜像,不需要 build 字段。

8.3 下载并初始化数据库

由于数据库初始化脚本没有打包在镜像中,需要手动下载并导入。

// bash
# 下载 SQL 文件(假设你托管在 GitHub 或其他地方)
wget https://your-repo.com/online_exam.sql

# 启动 MySQL 容器
docker-compose up -d mysql

# 等待 MySQL 完全启动后,导入数据
docker exec -i exam-mysql mysql -uroot -proot online_exam_2026 < online_exam.sql

8.4 启动所有服务

// bash
docker-compose up -d

等待镜像下载完成(首次约 700MB),然后访问 http://你的服务器IP 即可。

8.5 登录系统

管理员:9991 / 123456

教师:20081001 / 123456

学生:2022084 / 123456

九、总结与收获

9.1 完成的工作

✅ 编写后端 Dockerfile(多阶段构建,656MB)

✅ 编写前端 Dockerfile(多阶段构建,74.7MB)

✅ 编写 docker-compose.yml 编排三容器

✅ 本地三容器联调成功

✅ 推送到阿里云镜像仓库

✅ 实现一键部署方案

9.2 踩过的坑

1. Spring Boot 打包失败:忘记配置 spring-boot-maven-plugin,导致 JAR 无主类清单

2. Docker 镜像名大小写:镜像名必须小写,ExamSystem 会报错

3. 命名空间不存在:推送时报 denied,需要先在阿里云控制台创建命名空间

4. 数据库连接失败:后端启动时 MySQL 还没准备好,使用 healthcheck + depends_on 解决

十、资源链接

Docker 官方文档:https://docs.docker.com/

阿里云容器镜像服务:https://www.aliyun.com/product/acr

写在最后

从一个本地跑起来的项目,到容器化部署、推送到云端,这个过程让我对 Docker 有了更深入的理解。

Docker 不仅仅是"打包工具",它改变了我对软件交付的认知:一次构建,到处运行

如果你也有自己的项目想要容器化,不妨从写一个简单的 Dockerfile 开始。遇到问题时,多查文档,多动手实验,踩过的坑都会变成宝贵的经验。

如果这篇文章对你有帮助,欢迎点赞、收藏、分享。有任何问题,欢迎在评论区交流讨论!

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐