feat: 添加 Docker 化部署配置与 CI/CD 自动化
- 新增后端 Dockerfile(Node.js 20 Alpine 基础镜像) - 新增前端 Dockerfile(多阶段构建:Vite build + Nginx) - 新增 Nginx 配置(serve 前端静态文件 + 反代 /api) - 新增 docker-compose.dev.yml 和 docker-compose.prod.yml - 新增 GitHub Actions 工作流(自动构建推送阿里云镜像) - 新增 .dockerignore 排除不需要的文件 - 新增部署方案文档
This commit is contained in:
@@ -0,0 +1,20 @@
|
|||||||
|
# ============================================
|
||||||
|
# 生产环境 docker-compose 环境变量示例
|
||||||
|
# 复制此文件为 .env,填入实际值
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
# 后端镜像地址(阿里云容器镜像服务)
|
||||||
|
BACKEND_IMAGE=registry.cn-hangzhou.aliyuncs.com/your-namespace/idc-backend:latest
|
||||||
|
|
||||||
|
# 前端镜像地址(阿里云容器镜像服务)
|
||||||
|
FRONTEND_IMAGE=registry.cn-hangzhou.aliyuncs.com/your-namespace/idc-frontend:latest
|
||||||
|
|
||||||
|
# 指定特定版本(可选,默认 latest)
|
||||||
|
# BACKEND_VERSION=v1.0.0
|
||||||
|
# FRONTEND_VERSION=v1.0.0
|
||||||
|
|
||||||
|
# ---- MySQL 容器配置(如启用本地 MySQL) ----
|
||||||
|
# MYSQL_ROOT_PASSWORD=your_root_password
|
||||||
|
# MYSQL_DATABASE=idc_management
|
||||||
|
# MYSQL_USER=idc_user
|
||||||
|
# MYSQL_PASSWORD=your_mysql_password
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
name: Build and Push Docker Images
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY: registry.cn-hangzhou.aliyuncs.com
|
||||||
|
NAMESPACE: ${{ secrets.ALIYUN_NAMESPACE }}
|
||||||
|
BACKEND_IMAGE: idc-backend
|
||||||
|
FRONTEND_IMAGE: idc-frontend
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-backend:
|
||||||
|
name: Build Backend Image
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: 检出代码
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: 登录阿里云 Docker Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ secrets.ALIYUN_USERNAME }}
|
||||||
|
password: ${{ secrets.ALIYUN_PASSWORD }}
|
||||||
|
|
||||||
|
- name: 提取版本号
|
||||||
|
id: meta
|
||||||
|
run: |
|
||||||
|
if [[ "${{ github.ref_type }}" == "tag" ]]; then
|
||||||
|
VERSION=${GITHUB_REF#refs/tags/}
|
||||||
|
else
|
||||||
|
VERSION=latest
|
||||||
|
fi
|
||||||
|
echo "VERSION=${VERSION}" >> $GITHUB_OUTPUT
|
||||||
|
echo "COMMIT_SHA=${GITHUB_SHA::7}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: 设置 Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: 构建并推送后端镜像
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: ./backend
|
||||||
|
file: ./backend/Dockerfile
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
${{ env.REGISTRY }}/${{ env.NAMESPACE }}/${{ env.BACKEND_IMAGE }}:${{ steps.meta.outputs.VERSION }}
|
||||||
|
${{ env.REGISTRY }}/${{ env.NAMESPACE }}/${{ env.BACKEND_IMAGE }}:${{ steps.meta.outputs.COMMIT_SHA }}
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
|
||||||
|
build-frontend:
|
||||||
|
name: Build Frontend Image
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: 检出代码
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: 登录阿里云 Docker Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ secrets.ALIYUN_USERNAME }}
|
||||||
|
password: ${{ secrets.ALIYUN_PASSWORD }}
|
||||||
|
|
||||||
|
- name: 提取版本号
|
||||||
|
id: meta
|
||||||
|
run: |
|
||||||
|
if [[ "${{ github.ref_type }}" == "tag" ]]; then
|
||||||
|
VERSION=${GITHUB_REF#refs/tags/}
|
||||||
|
else
|
||||||
|
VERSION=latest
|
||||||
|
fi
|
||||||
|
echo "VERSION=${VERSION}" >> $GITHUB_OUTPUT
|
||||||
|
echo "COMMIT_SHA=${GITHUB_SHA::7}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: 设置 Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: 构建并推送前端镜像
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: ./frontend
|
||||||
|
file: ./frontend/Dockerfile
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
${{ env.REGISTRY }}/${{ env.NAMESPACE }}/${{ env.FRONTEND_IMAGE }}:${{ steps.meta.outputs.VERSION }}
|
||||||
|
${{ env.REGISTRY }}/${{ env.NAMESPACE }}/${{ env.FRONTEND_IMAGE }}:${{ steps.meta.outputs.COMMIT_SHA }}
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
|
||||||
|
notify:
|
||||||
|
name: Notify Build Complete
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [build-backend, build-frontend]
|
||||||
|
if: always() && github.event_name != 'pull_request'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: 输出构建结果
|
||||||
|
run: |
|
||||||
|
echo "=== 构建结果 ==="
|
||||||
|
echo "后端镜像: ${{ needs.build-backend.result }}"
|
||||||
|
echo "前端镜像: ${{ needs.build-frontend.result }}"
|
||||||
|
if [[ "${{ needs.build-backend.result }}" == "success" && "${{ needs.build-frontend.result }}" == "success" ]]; then
|
||||||
|
echo "所有镜像构建成功"
|
||||||
|
else
|
||||||
|
echo "部分镜像构建失败"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# ============================================
|
||||||
|
# 后端 Docker 构建忽略文件
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
# 依赖
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# 环境配置(运行时挂载)
|
||||||
|
.env
|
||||||
|
|
||||||
|
# 运行时数据(运行时挂载)
|
||||||
|
uploads
|
||||||
|
logs
|
||||||
|
backups
|
||||||
|
temp
|
||||||
|
|
||||||
|
# 数据库文件
|
||||||
|
*.db
|
||||||
|
*.sqlite
|
||||||
|
|
||||||
|
# 版本控制
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
# 测试
|
||||||
|
tests
|
||||||
|
coverage
|
||||||
|
*.test.js
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
||||||
|
# 系统
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# npm
|
||||||
|
npm-debug.log*
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# ============================================
|
||||||
|
# IDC 设备管理系统 - 后端 Dockerfile
|
||||||
|
# 基于 Node.js 20 Alpine 构建
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
FROM node:20-alpine
|
||||||
|
|
||||||
|
# 设置工作目录
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 安装系统依赖(如需编译原生模块)
|
||||||
|
RUN apk add --no-cache tzdata
|
||||||
|
|
||||||
|
# 设置时区
|
||||||
|
ENV TZ=Asia/Shanghai
|
||||||
|
|
||||||
|
# 复制依赖配置文件
|
||||||
|
COPY package.json ./
|
||||||
|
COPY package-lock.json* yarn.lock* ./
|
||||||
|
|
||||||
|
# 安装生产依赖(不安装 devDependencies)
|
||||||
|
RUN npm install --omit=dev --ignore-scripts
|
||||||
|
|
||||||
|
# 复制后端代码
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# 确保日志和上传目录存在
|
||||||
|
RUN mkdir -p logs uploads temp backups
|
||||||
|
|
||||||
|
# 暴露端口
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
# 启动命令
|
||||||
|
CMD ["node", "server.js"]
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
# ============================================
|
||||||
|
# IDC 设备管理系统 - 开发环境配置
|
||||||
|
# 使用 build 指令本地构建镜像
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
# ---------- 后端服务 ----------
|
||||||
|
backend:
|
||||||
|
build:
|
||||||
|
context: ./backend
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: idc-backend-dev
|
||||||
|
volumes:
|
||||||
|
# 配置映射(核心)
|
||||||
|
- ./backend/.env:/app/.env
|
||||||
|
# 数据持久化
|
||||||
|
- ./backend/uploads:/app/uploads
|
||||||
|
- ./backend/logs:/app/logs
|
||||||
|
- ./backend/backups:/app/backups
|
||||||
|
- ./backend/temp:/app/temp
|
||||||
|
env_file:
|
||||||
|
- ./backend/.env
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=development
|
||||||
|
# 连接 docker-compose 内的 MySQL 时,注释掉下方 MYSQL_HOST 的注释
|
||||||
|
# 连接远程 MySQL 时,保持 .env 中的配置即可
|
||||||
|
networks:
|
||||||
|
- idc-network
|
||||||
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8000/health"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 30s
|
||||||
|
|
||||||
|
# ---------- 前端(Nginx)服务 ----------
|
||||||
|
frontend:
|
||||||
|
build:
|
||||||
|
context: ./frontend
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: idc-frontend-dev
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
networks:
|
||||||
|
- idc-network
|
||||||
|
depends_on:
|
||||||
|
- backend
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
# ---------- MySQL 数据库 ----------
|
||||||
|
# 如需使用本地 MySQL 容器,取消下方注释
|
||||||
|
# mysql:
|
||||||
|
# image: mysql:8.0
|
||||||
|
# container_name: idc-mysql-dev
|
||||||
|
# volumes:
|
||||||
|
# - mysql_data:/var/lib/mysql
|
||||||
|
# environment:
|
||||||
|
# MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-root123}
|
||||||
|
# MYSQL_DATABASE: idc_management
|
||||||
|
# MYSQL_USER: idc_user
|
||||||
|
# MYSQL_PASSWORD: ${MYSQL_PASSWORD:-idc_pass}
|
||||||
|
# ports:
|
||||||
|
# - "3306:3306"
|
||||||
|
# networks:
|
||||||
|
# - idc-network
|
||||||
|
# restart: unless-stopped
|
||||||
|
# healthcheck:
|
||||||
|
# test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
|
||||||
|
# interval: 10s
|
||||||
|
# timeout: 5s
|
||||||
|
# retries: 5
|
||||||
|
|
||||||
|
networks:
|
||||||
|
idc-network:
|
||||||
|
driver: bridge
|
||||||
|
|
||||||
|
# volumes:
|
||||||
|
# mysql_data:
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
# ============================================
|
||||||
|
# IDC 设备管理系统 - 生产环境配置
|
||||||
|
# 从镜像仓库拉取预构建镜像,无 build 指令
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
# ---------- 后端服务 ----------
|
||||||
|
backend:
|
||||||
|
# 从镜像仓库拉取,需替换为实际的镜像名
|
||||||
|
image: ${BACKEND_IMAGE:-your-registry/idc-backend:latest}
|
||||||
|
container_name: idc-backend
|
||||||
|
volumes:
|
||||||
|
# 配置映射(核心)
|
||||||
|
- ./backend/.env:/app/.env
|
||||||
|
# 数据持久化
|
||||||
|
- ./backend/uploads:/app/uploads
|
||||||
|
- ./backend/logs:/app/logs
|
||||||
|
- ./backend/backups:/app/backups
|
||||||
|
- ./backend/temp:/app/temp
|
||||||
|
env_file:
|
||||||
|
- ./backend/.env
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=production
|
||||||
|
# 生产环境使用 Docker 内 MySQL 时,MYSQL_HOST 需设为 mysql
|
||||||
|
# 使用远程 MySQL 时,保持 .env 中的配置
|
||||||
|
networks:
|
||||||
|
- idc-network
|
||||||
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8000/health"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 30s
|
||||||
|
|
||||||
|
# ---------- 前端(Nginx)服务 ----------
|
||||||
|
frontend:
|
||||||
|
image: ${FRONTEND_IMAGE:-your-registry/idc-frontend:latest}
|
||||||
|
pull_policy: always
|
||||||
|
container_name: idc-frontend
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
networks:
|
||||||
|
- idc-network
|
||||||
|
depends_on:
|
||||||
|
- backend
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
# ---------- MySQL 数据库 ----------
|
||||||
|
# 如需使用本地 MySQL 容器,取消下方注释
|
||||||
|
# mysql:
|
||||||
|
# image: mysql:8.0
|
||||||
|
# container_name: idc-mysql
|
||||||
|
# volumes:
|
||||||
|
# - mysql_data:/var/lib/mysql
|
||||||
|
# environment:
|
||||||
|
# MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
|
||||||
|
# MYSQL_DATABASE: ${MYSQL_DATABASE:-idc_management}
|
||||||
|
# MYSQL_USER: ${MYSQL_USER:-idc_user}
|
||||||
|
# MYSQL_PASSWORD: ${MYSQL_PASSWORD}
|
||||||
|
# networks:
|
||||||
|
# - idc-network
|
||||||
|
# restart: unless-stopped
|
||||||
|
# healthcheck:
|
||||||
|
# test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
|
||||||
|
# interval: 10s
|
||||||
|
# timeout: 5s
|
||||||
|
# retries: 5
|
||||||
|
|
||||||
|
networks:
|
||||||
|
idc-network:
|
||||||
|
driver: bridge
|
||||||
|
|
||||||
|
# volumes:
|
||||||
|
# mysql_data:
|
||||||
@@ -0,0 +1,182 @@
|
|||||||
|
# Docker 化部署方案
|
||||||
|
|
||||||
|
> 讨论时间: 2026-06-12
|
||||||
|
|
||||||
|
## 一、架构设计
|
||||||
|
|
||||||
|
### 3 个容器
|
||||||
|
|
||||||
|
| 容器 | 角色 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| **Nginx(含前端)** | Web 服务器 + 前端静态文件 | serve 前端构建产物,反向代理 `/api` 到后端 |
|
||||||
|
| **Backend** | Node.js 后端服务 | Express API 服务 |
|
||||||
|
| **MySQL** | 数据库 | MySQL 8.0 |
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
||||||
|
│ Nginx 容器 │ │ Backend 容器 │ │ MySQL 容器 │
|
||||||
|
│ │ │ │ │ │
|
||||||
|
│ / ──────▶ │ │ Node.js │ │ 数据库 │
|
||||||
|
│ 前端静态文件 │ │ :8000 │ │ :3306 │
|
||||||
|
│ │────▶│ │────▶│ │
|
||||||
|
│ /api ────▶ │ │ │ │ │
|
||||||
|
│ 反向代理 │ │ │ │ │
|
||||||
|
│ :80 │ │ │ │ │
|
||||||
|
└──────────────┘ └──────────────┘ └──────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
> 注意:前端不单独占一个容器。前端代码构建成静态文件后,直接打包进 Nginx 镜像中。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、主机需要映射的目录
|
||||||
|
|
||||||
|
| 路径 | 说明 | 必要性 |
|
||||||
|
|------|------|--------|
|
||||||
|
| `backend/.env` | 核心配置(数据库、JWT、日志等) | **必选** |
|
||||||
|
| `backend/uploads/` | 用户上传文件持久化 | **必选** |
|
||||||
|
| `backend/logs/` | 运行日志持久化 | **必选** |
|
||||||
|
| `backend/backups/` | 数据库备份文件持久化 | **必选** |
|
||||||
|
| `backend/temp/` | 临时文件 | 可选 |
|
||||||
|
| `mysql_data/` | MySQL 数据库文件持久化(Docker 数据卷) | **必选** |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、构建与部署方案
|
||||||
|
|
||||||
|
### 3.1 CI/CD 自动化流程
|
||||||
|
|
||||||
|
```
|
||||||
|
开发 → git push
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
GitHub Actions
|
||||||
|
│
|
||||||
|
├── 构建 idc-backend:latest 镜像
|
||||||
|
├── 构建 idc-frontend:latest 镜像(Nginx + 前端静态文件)
|
||||||
|
├── 推送到镜像仓库(Docker Hub / 阿里云 / GitHub Container Registry)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
生产服务器
|
||||||
|
docker compose pull # 拉取最新镜像
|
||||||
|
docker compose up -d # 重启容器
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 环境区分
|
||||||
|
|
||||||
|
| 环境 | docker-compose 文件 | 镜像来源 |
|
||||||
|
|------|--------------------|----------|
|
||||||
|
| 开发环境 | `docker-compose.dev.yml`(含 `build:` 指令) | 本地构建 |
|
||||||
|
| 生产环境 | `docker-compose.prod.yml`(用 `image:` 指令) | 从镜像仓库拉取 |
|
||||||
|
|
||||||
|
生产环境的 `docker-compose.yml` 不写 `build:`,直接拉取远程镜像:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
backend:
|
||||||
|
image: yourname/idc-backend:latest
|
||||||
|
# 没有 build 指令,直接从仓库拉取
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
image: yourname/idc-frontend:latest
|
||||||
|
# 同上,服务器不需要装 Node.js
|
||||||
|
|
||||||
|
mysql:
|
||||||
|
image: mysql:8.0
|
||||||
|
```
|
||||||
|
|
||||||
|
### 版本回滚
|
||||||
|
|
||||||
|
构建时打版本标签,支持快速回滚:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 部署新版本
|
||||||
|
docker compose pull && docker compose up -d
|
||||||
|
|
||||||
|
# 回滚到旧版本
|
||||||
|
BACKEND_VERSION=v1.2.0 FRONTEND_VERSION=v1.2.0 docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、后端关键配置变化
|
||||||
|
|
||||||
|
| 配置项 | 开发环境(远程 MySQL) | Docker 生产环境 |
|
||||||
|
|--------|----------------------|----------------|
|
||||||
|
| `MYSQL_HOST` | `1p.debian.zhaoyi520.tk` | `mysql`(Docker 服务名) |
|
||||||
|
| `MYSQL_PORT` | `33060` | `3306` |
|
||||||
|
| `JWT_SECRET` | 自动生成 | 部署前必须手动配置 |
|
||||||
|
|
||||||
|
### 注意点
|
||||||
|
|
||||||
|
1. **JWT_SECRET**:生产环境必须在 `.env` 中预先配置好,因为容器内无法写回只读挂载的 `.env`
|
||||||
|
2. **数据库初始化**:MySQL 容器首次启动时,后端 `sequelize.sync()` 会自动建表,无需手动初始化
|
||||||
|
3. **Nginx 反向代理**:Nginx 容器内配置 `proxy_pass http://backend:8000`,用 Docker 服务名通信
|
||||||
|
|
||||||
|
### 已创建的文件(2026-06-12)
|
||||||
|
|
||||||
|
| 文件 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `backend/Dockerfile` | 后端镜像构建(Node.js 20 Alpine) |
|
||||||
|
| `backend/.dockerignore` | 后端构建忽略配置 |
|
||||||
|
| `frontend/Dockerfile` | 前端镜像构建(多阶段构建:先 npm build,再 Nginx) |
|
||||||
|
| `frontend/nginx.conf` | Nginx 配置(serve 静态文件 + 反向代理 /api) |
|
||||||
|
| `frontend/.dockerignore` | 前端构建忽略配置 |
|
||||||
|
| `docker-compose.dev.yml` | 开发环境 Compose(含 `build:` 指令,本地构建) |
|
||||||
|
| `docker-compose.prod.yml` | 生产环境 Compose(用 `image:`,从仓库拉取) |
|
||||||
|
|
||||||
|
### 待实现
|
||||||
|
|
||||||
|
- ~~`.github/workflows/deploy.yml` — CI/CD 自动构建推送~~ ✅ 已完成
|
||||||
|
|
||||||
|
## 五、CI/CD 配置
|
||||||
|
|
||||||
|
### 5.1 镜像仓库:阿里云容器镜像服务
|
||||||
|
|
||||||
|
阿里云控制台开通容器镜像服务后:
|
||||||
|
1. 创建命名空间(如 `idc-assest`)
|
||||||
|
2. 创建镜像仓库:`idc-backend`、`idc-frontend`
|
||||||
|
3. 设置访问凭证(用户名 + 密码)
|
||||||
|
|
||||||
|
### 5.2 GitHub Secrets 配置
|
||||||
|
|
||||||
|
在 GitHub 仓库 `Settings → Secrets and variables → Actions` 中配置:
|
||||||
|
|
||||||
|
| Secret 名称 | 说明 |
|
||||||
|
|------------|------|
|
||||||
|
| `ALIYUN_USERNAME` | 阿里云 Docker Registry 用户名 |
|
||||||
|
| `ALIYUN_PASSWORD` | 阿里云 Docker Registry 密码 |
|
||||||
|
| `ALIYUN_NAMESPACE` | 阿里云命名空间 |
|
||||||
|
|
||||||
|
### 5.3 触发条件
|
||||||
|
|
||||||
|
- 推送到 `master` 分支 → 构建 `latest` 镜像
|
||||||
|
- 创建 `v*` 标签(如 `v1.0.0`)→ 构建对应版本镜像
|
||||||
|
- 手动触发(Actions 页面 `Run workflow`)
|
||||||
|
|
||||||
|
### 5.4 镜像标签策略
|
||||||
|
|
||||||
|
每次构建会同时打两个标签:
|
||||||
|
- `latest`(或 tag 名称):最新版本
|
||||||
|
- `<commit-sha>`:精确回溯到某次提交
|
||||||
|
|
||||||
|
### 5.5 部署流程
|
||||||
|
|
||||||
|
```
|
||||||
|
开发者:git push origin master
|
||||||
|
↓
|
||||||
|
GitHub Actions 自动构建
|
||||||
|
↓
|
||||||
|
推送镜像到阿里云仓库
|
||||||
|
↓
|
||||||
|
管理员 SSH 到生产服务器
|
||||||
|
↓
|
||||||
|
cd /opt/idc_assest
|
||||||
|
docker compose -f docker-compose.prod.yml pull
|
||||||
|
docker compose -f docker-compose.prod.yml up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.6 已创建文件
|
||||||
|
|
||||||
|
- `.github/workflows/docker-build.yml` — CI/CD 工作流定义
|
||||||
|
- `.env.example` — 生产环境 docker-compose 环境变量示例
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# ============================================
|
||||||
|
# 前端 Docker 构建忽略文件
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
# 依赖
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# 构建产物(由构建阶段生成)
|
||||||
|
dist
|
||||||
|
|
||||||
|
# 本地环境
|
||||||
|
.env
|
||||||
|
.frontend-port
|
||||||
|
|
||||||
|
# 版本控制
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
||||||
|
# 系统
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# npm
|
||||||
|
npm-debug.log*
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# ============================================
|
||||||
|
# IDC 设备管理系统 - 前端(Nginx)Dockerfile
|
||||||
|
# 第一阶段:构建前端静态文件
|
||||||
|
# 第二阶段:Nginx 运行
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
# ---------- 第一阶段:构建 ----------
|
||||||
|
FROM node:20-alpine AS build
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 复制依赖配置
|
||||||
|
COPY package.json package-lock.json* yarn.lock* ./
|
||||||
|
|
||||||
|
# 安装所有依赖(含 devDependencies,vite build 需要)
|
||||||
|
RUN npm install --ignore-scripts
|
||||||
|
|
||||||
|
# 复制前端源码
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# 构建生产版本
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# ---------- 第二阶段:Nginx 运行 ----------
|
||||||
|
FROM nginx:alpine
|
||||||
|
|
||||||
|
# 设置时区
|
||||||
|
RUN apk add --no-cache tzdata
|
||||||
|
ENV TZ=Asia/Shanghai
|
||||||
|
|
||||||
|
# 复制 Nginx 配置
|
||||||
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
|
# 复制前端构建产物到 Nginx 静态目录
|
||||||
|
COPY --from=build /app/dist /usr/share/nginx/html
|
||||||
|
|
||||||
|
# 暴露端口
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
# Nginx 前台运行
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
# ============================================
|
||||||
|
# IDC 设备管理系统 - Nginx 配置
|
||||||
|
# 功能:serve 前端静态文件 + 反向代理 API
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name localhost;
|
||||||
|
|
||||||
|
# 前端静态文件
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
# Gzip 压缩
|
||||||
|
gzip on;
|
||||||
|
gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript image/svg+xml;
|
||||||
|
gzip_min_length 1024;
|
||||||
|
gzip_vary on;
|
||||||
|
|
||||||
|
# 前端路由(SPA 支持)
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
# API 反向代理到后端
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://backend:8000;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection 'upgrade';
|
||||||
|
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_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_cache_bypass $http_upgrade;
|
||||||
|
|
||||||
|
# 超时设置
|
||||||
|
proxy_read_timeout 120s;
|
||||||
|
proxy_send_timeout 120s;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 上传文件访问
|
||||||
|
location /uploads/ {
|
||||||
|
proxy_pass http://backend:8000;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 健康检查端点
|
||||||
|
location /health {
|
||||||
|
proxy_pass http://backend:8000;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
}
|
||||||
|
|
||||||
|
# API 文档
|
||||||
|
location /api-docs {
|
||||||
|
proxy_pass http://backend:8000;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 禁止访问隐藏文件
|
||||||
|
location ~ /\. {
|
||||||
|
deny all;
|
||||||
|
access_log off;
|
||||||
|
log_not_found off;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 静态资源缓存策略
|
||||||
|
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?|ttf|eot)$ {
|
||||||
|
expires 30d;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
access_log off;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user