1. 前言

在当下的项目开发中,无需再遵循 “从头学到尾” 的传统路径。借助 AI 辅助,我们完全可以加速实践进程,在动手操作中高效掌握技能。其中,以 AI 助力 Docker 容器化部署,已成为提升项目落地效率的核心方案之一。通过 Docker 技术,我们能将应用程序与所有依赖环境打包整合,从根本上保障其在不同运行环境中的一致性与稳定性。本文将以 Gemini 3.0 为例,详细拆解如何借助 AI 工具,从零开始完成前后端分离项目的阿里云服务器部署流程。为规避不同软件图形化界面带来的操作差异,确保教程普适性,以下所有操作均通过命令行完成。

2. 原理概述

2.1 核心概念比喻 (餐厅模型)

为了方便理解,我们可以把这次部署比作在阿里云这块地皮上开一家餐厅

  1. 服务器 (ECS) = 地皮
  • 公网 IP = 街道门牌号,顾客通过它找到你。
  • 私网 IP = 厨房内部通道,只有员工能走。
  1. Docker = 集装箱/预制房
  • 传统方式需要在服务器上一点点搭建环境(砌墙、铺管),容易出错。
  • Docker 方式是在工厂(你本地电脑)把厨房、前台、仓库都做成标准的“集装箱”镜像。运到服务器上,直接放下就能用,环境完全一致。
  1. Docker Compose = 施工图纸
  • 它告诉工头:把“MySQL集装箱”放在左边,把“后端集装箱”放在中间,把“前端集装箱”放在门口,并接通它们之间的水电(网络)。

2.2 技术原理与数据流向

当用户访问你的网站时,数据是这样流动的:

  1. 第一层:前台接待 (Nginx 容器)
  • 位置:监听服务器 80 端口
  • 职责
    • 静态页面:用户访问首页,Nginx 直接返回打包好的 html/css/js 文件。
    • 反向代理:用户点击登录(请求 /api/login),Nginx 识别出 /api 标记,将请求偷偷转发给后端的 8080 端口。
  • 作用:彻底解决跨域问题。浏览器认为所有资源都来自 80 端口(同源),不知道后端的真实存在。
  1. 第二层:后厨烹饪 (Backend 容器)
  • 位置:运行在 Docker 内部网络 8080 端口(不对外开放)。
  • 职责:接收 Nginx 转来的请求,执行业务逻辑(如验证密码),并向数据库查询数据。
  1. 第三层:仓库保管 (MySQL 容器)
  • 位置:运行在 Docker 内部网络 3306 端口(不对外开放)。
  • 职责:存储和提供数据。

3. 环境准备

3.1 服务器准备

  • 服务器:阿里云 ECS (推荐 Ubuntu 20.04/22.04)
  • 工具:SSH 客户端 (Xshell, PuTTY, 或 VS Code Terminal)

3.2 安全组配置 (防火墙)

为了让外网能访问你的网站,同时保证服务器安全,请在阿里云控制台配置以下规则:

端口范围 协议 授权对象 用途 必须性
80/80 TCP 0.0.0.0/0 HTTP 网站访问。用户访问网站的默认入口。 必须
22/22 TCP 0.0.0.0/0 SSH 远程连接。用于上传文件和执行命令。 必须

安全提示:以下端口建议关闭(不要开放)

  • 8080 (后端端口):我们使用 Nginx (80端口) 反向代理转发请求给后端,外部无需直接访问 8080,关闭可防止绕过代理的攻击。
  • 3306 (数据库端口):数据库运行在 Docker 内部网络,后端通过内网连接。对外开放 3306 极易遭受勒索病毒攻击。

4. AI 交互指令参考 (Prompt)

你可以将以下提示词发送给 AI (如 ChatGPT, Claude, Copilot),让它为你生成定制化的配置文件:

我需要将这个项目通过Docker部署到阿里云Ubuntu 22.04 64位服务器(公网IP:xx.xx.xx.xx,域名:xxxx)。我是零基础小白,要求如下:
1. 服务器无任何预装环境,需从基础配置开始;
2. 本地完成前后端打包后再上传至服务器,禁止在服务器端打包;
3. 前端项目位于frontend文件夹(基于React脚手架开发),后端项目位于backend文件夹(基于SpringBoot开发),数据库使用MySQL;
4. 前端需通过Nginx反向代理至后端(后端端口8080),MySQL端口为3306;
5. 重点解决跨域问题;
6. 需提供详细的操作步骤、对应命令(说明操作目的),并生成所需的Dockerfile和docker-compose.yml文件。

5. 部署流程参考

注:frontend 为前端项目目录、 backend 为后端项目目录。

Step 1: 本地构建 (Build)

采用本地打包,服务器运行的策略,如果将项目上传到服务器打包,会增加服务器负担且效率较低。

  1. 构建前端:在 frontend 目录下运行:

    npm run build

产物:frontend/dist 文件夹

  1. 构建后端:在 backend 目录下运行:

    mvn clean package -DskipTests

产物:backend/target/exam-system-backend-1.0.0.jar

Step 2: 整理部署文件

在本地创建一个 deploy 文件夹,按照以下结构整理文件。这是上传到服务器的最终形态。

deploy/
├── docker-compose.yml       (核心编排文件)
├── exam_system.sql          (数据库初始化脚本,看你的项目命名)
├── backend/
│   └── Dockerfile           (后端镜像构建文件)
│   └── target/
│       └── exam-system-backend-1.0.0.jar  (后端Jar包,看你的项目命名)
└── frontend/
    ├── Dockerfile           (前端镜像构建文件)
    ├── nginx.conf           (Nginx配置文件)
    └── dist/                (前端静态资源)
关键配置参考

1. docker-compose.yml

version: '3.8' 

services:       # 定义服务列表
  # === 1. 数据库服务 ===
  mysql:
    image: mysql:8.0             # 直接从网上下载 mysql 8.0 的官方镜像,不用自己构建
    container_name: exam-mysql   # 给容器起个名字,方便管理
    restart: always              # 如果崩了或者服务器重启了,自动重新启动
    environment:                 # 设置环境变量
      MYSQL_ROOT_PASSWORD: root  # 设置 root 用户的密码
      MYSQL_DATABASE: exam_system # 容器启动时自动创建一个叫 exam_system 的空库
      TZ: Asia/Shanghai          # 设置时区,否则时间会差8小时
    volumes:                     # 挂载数据卷(重要!)
      - ./mysql_data:/var/lib/mysql  # 把容器里的数据库文件映射到宿主机的 mysql_data 目录。
                                     # 即使删了容器,数据还在宿主机上,不会丢。
      - ./exam_system.sql:/docker-entrypoint-initdb.d/init.sql 
                                     # 把我们的 SQL 脚本放进去,MySQL 第一次启动时会自动执行它建表。
    networks:
      - exam-network             # 加入专用网络

  # === 2. 后端服务 ===
  backend:
    build: 
      context: ./backend         # 构建镜像的上下文目录是 ./backend
      dockerfile: Dockerfile     # 使用该目录下的 Dockerfile 文件来构建
    container_name: exam-backend
    restart: always
    environment:
      # 覆盖 application.yml 里的配置
      # 注意这里写的是 mysql,而不是 ip。因为在同一个 Docker 网络里,服务名就是域名。
      SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/exam_system?... 
      SPRING_DATASOURCE_PASSWORD: root
      FILE_UPLOAD_PATH: /app/uploads/ # 告诉后端,文件存到容器里的这个位置
    volumes:
      - ./uploads:/app/uploads   # 把容器里的上传目录映射到宿主机的 uploads 目录。
                                 # 这样用户上传的头像、课件就存在服务器硬盘上,重启容器不会丢。
    ports:
      - "8080:8080"              # (可选) 把容器的8080暴露给宿主机,方便调试。其实有Nginx后可以不暴露。
    depends_on:
      - mysql                    # 告诉 Docker:先启动 mysql,再启动我。
    networks:
      - exam-network

  # === 3. 前端服务 (Nginx) ===
  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    container_name: exam-frontend
    restart: always
    ports:
      - "80:80"                  # 关键!把宿主机的 80 端口(HTTP默认端口)映射到容器的 80 端口。
                                 # 这样用户访问服务器 IP,就是访问这个容器。
    depends_on:
      - backend                  # 习惯上先启动后端
    networks:
      - exam-network

networks:                        # 定义网络
  exam-network:
    driver: bridge               # 创建一个桥接网络,让这三个容器能互相 ping 通。

2. frontend/nginx.conf (解决跨域)

server {
    listen 80;                 # 监听 80 端口
    server_name localhost;     # 域名,这里写 localhost 即可,因为是在容器里

    # === 规则 1:处理前端页面 ===
    location / {
        root /usr/share/nginx/html;   # 网站文件存放的根目录
        index index.html index.htm;   # 默认首页
        try_files $uri $uri/ /index.html; 
        # ↑ 这行非常关键!React 是单页应用(SPA)。
        # 如果用户访问 /login,Nginx 找不到 login 这个文件夹,
        # try_files 会让它返回 index.html,把路由交给 React 代码去处理,防止 404。
    }

    # === 规则 2:处理后端接口 (反向代理) ===
    location /api/ {
        # 当请求以 /api/ 开头时...

        # rewrite 作用:去掉 /api 前缀。
        # 用户请求: http://zjx-space.fun/api/auth/login
        # 转发给后端: http://backend:8080/auth/login
        # 因为后端接口本身没有 /api 前缀,所以要切掉。
        rewrite ^/api/(.*)$ /$1 break;

        proxy_pass http://backend:8080; # 转发给名为 'backend' 的容器

        # 下面这些是把用户的真实 IP 告诉后端,否则后端看到的 IP 全是 Nginx 的内网 IP
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    # === 规则 3:处理文件上传访问 ===
    location /uploads/ {
        # 如果用户访问图片 http://zjx-space.fun/uploads/avatar.jpg
        # 直接去后端的 8080 端口拿文件
        proxy_pass http://backend:8080/uploads/;
    }
}

3. 前端构建 frontend/Dockerfile

# 第一步:找个底座
FROM nginx:alpine
# 使用 Nginx 的 alpine 版本,这个版本非常小(只有几MB),适合生产环境。

# 第二步:放代码
# 把你本地 build 好的 dist 文件夹(里面是 html/css/js),复制到容器里的网站目录
COPY dist /usr/share/nginx/html

# 第三步:放配置
# 把我们写的 nginx.conf 复制进去,替换掉 Nginx 默认的配置
COPY nginx.conf /etc/nginx/conf.d/default.conf

# 第四步:声明端口
EXPOSE 80

# 第五步:启动
CMD ["nginx", "-g", "daemon off;"]
# 启动 Nginx,并且让它在前台运行(否则容器启动完就退出了)

4. 后端构建 backend/Dockerfile

# 第一步:找个底座
FROM openjdk:17-jdk-slim
# 使用包含 Java 17 的轻量级系统

# 第二步:设置工作目录
WORKDIR /app
# 相当于 cd /app,后面的命令都在这个目录下执行

# 第三步:放 Jar 包
# 这里的路径 target/xxx.jar 是相对于构建上下文(backend目录)的。
# 之前报错就是因为这里没找到文件。
COPY target/exam-system-backend-1.0.0.jar app.jar

# 第四步:声明端口
EXPOSE 8080

# 第五步:启动命令
ENTRYPOINT ["java", "-jar", "app.jar"]
# 容器启动时执行 java -jar app.jar

Step 3: 服务器环境初始化

连接到服务器,执行以下命令安装 Docker。

# 1. 更新软件源并安装 Docker
sudo apt-get update
sudo apt-get install -y docker.io docker-compose

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

# 3. 配置镜像加速器 (解决国内下载慢/超时问题)
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": [
    "https://docker.m.daocloud.io",
    "https://docker.1panel.live",
    "https://hub.rat.dev"
  ]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

Step 4: 上传与启动

  1. 上传文件:在本地终端执行:

    scp -r deploy root@<你的公网IP>:/root/

注:如果提示 “Host key verification failed”,请运行 ssh-keygen -R <你的公网IP> 清除旧指纹。

  1. 启动服务:在服务器终端执行:

    cd /root/deploy
    sudo docker-compose up -d

Step 5: 验证与排错

  • 查看日志: sudo docker-compose logs -f backend
  • 访问网站: 浏览器打开 http://<你的公网IP>
  • 常见错误:
    • COPY failed: 检查 deploy/backend/target 下是否有 jar 包。
    • Connection refused: 检查阿里云安全组是否开放了 80 端口。
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐