Files
miniapp-api/CI_CD_SPEC.md
T
2026-05-17 11:58:37 +08:00

13 KiB

权益小助手 - CI/CD 流程规范

项目概述

项目结构

QuanYiXiaoZhuShou/
├── backend/          # Node.js 后端服务
│   ├── src/          # 源代码
│   ├── Dockerfile    # Docker 构建文件
│   ├── docker-compose.yml  # Docker Compose 配置
│   ├── nginx-api.conf      # Nginx 反向代理配置
│   └── package.json        # 依赖管理
├── miniapp/          # 微信小程序前端
│   ├── pages/        # 页面代码
│   ├── utils/        # 工具函数
│   └── app.js        # 小程序入口
└── test/             # 测试相关

技术栈

后端 (Backend)

  • Runtime: Node.js >= 18.0.0
  • Framework: Express.js 4.18.2
  • Database: MongoDB 7.0
  • Process Manager: PM2
  • Container: Docker + Docker Compose
  • Web Server: Nginx (反向代理)

前端 (Miniapp)

  • Platform: 微信小程序
  • Language: JavaScript
  • Build Tool: 微信开发者工具

服务器环境

  • OS: Ubuntu/Debian (推测)
  • Server IP: 8.136.137.59
  • Domain: api-miniapp.dxz99wyr.cn
  • SSL: 已配置
  • Deployment Path: /var/www/quanyixiaozhushou-app

当前部署状态

服务器部署方式

当前使用 Docker 容器部署,包含两个容器:

容器名称 服务 端口 状态
quanyixiaozhushou-app Node.js 后端 3000 运行中
quanyixiaozhushou-mongo MongoDB 7.0 27018 运行中
# 查看容器状态
docker ps

# 查看后端日志
docker logs quanyixiaozhushou-app

# 查看 MongoDB 日志
docker logs quanyixiaozhushou-mongo

环境变量 (生产环境)

PORT=3000
NODE_ENV=production
WECHAT_APPID=wxa83262674846ca1a
WECHAT_APPSECRET=c40e9d356438f92d10091a115ee50172
MONGODB_URI=mongodb://localhost:27017/quanyixiaozhushou
JWT_SECRET=your_jwt_secret_key_here_change_in_production
JWT_EXPIRES_IN=7d
LOG_LEVEL=info

CI/CD 流程设计

1. Git 工作流

[Developer] → feature branch → Pull Request → [Code Review] → merge to master → [CI/CD Pipeline] → Deploy

2. 分支策略

分支 用途 保护规则
master 生产环境分支 禁止直接推送,需 PR
develop 开发环境分支 需 PR
feature/* 功能开发 自由推送
hotfix/* 紧急修复 需 PR 到 master

3. CI/CD Pipeline 流程

graph TD
    A[Git Push/PR] --> B[Checkout Code]
    B --> C[Install Dependencies]
    C --> D[Run Tests]
    D --> E[Lint Check]
    E --> F[Build Docker Image]
    F --> G[Push to Registry]
    G --> H[Deploy to Server]
    H --> I[Health Check]
    I --> J[Notify]

详细 CI/CD 配置

Stage 1: 代码检出 (Checkout)

# .github/workflows/ci-cd.yml
name: CI/CD Pipeline

on:
  push:
    branches: [ master, develop ]
  pull_request:
    branches: [ master ]

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
      with:
        repository: https://git.dxz99wyr.cn/Superuser/miniapp-api
        token: ${{ secrets.GIT_ACCESS_TOKEN }}

Stage 2: 依赖安装与测试

    - name: Setup Node.js
      uses: actions/setup-node@v4
      with:
        node-version: '18'
        cache: 'npm'
        cache-dependency-path: backend/package-lock.json

    - name: Install backend dependencies
      working-directory: ./backend
      run: npm ci

    - name: Run backend tests
      working-directory: ./backend
      run: npm test
      continue-on-error: true  # 如果没有测试,不阻塞流程

    - name: Run backend lint
      working-directory: ./backend
      run: npm run lint
      continue-on-error: true

Stage 3: Docker 构建

    - name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v3

    - name: Login to Docker Registry
      uses: docker/login-action@v3
      with:
        registry: ghcr.io  # 或你的私有仓库
        username: ${{ github.actor }}
        password: ${{ secrets.GITHUB_TOKEN }}

    - name: Build and push Docker image
      uses: docker/build-push-action@v5
      with:
        context: ./backend
        push: true
        tags: |
          ghcr.io/${{ github.repository }}/backend:${{ github.sha }}
          ghcr.io/${{ github.repository }}/backend:latest
        cache-from: type=gha
        cache-to: type=gha,mode=max

Stage 4: 部署到服务器

    - name: Deploy to server
      uses: appleboy/ssh-action@v1.0.0
      with:
        host: 8.136.137.59
        username: root
        key: ${{ secrets.SSH_PRIVATE_KEY }}
        script: |
          # 拉取最新镜像
          docker pull ghcr.io/${{ github.repository }}/backend:latest
          
          # 停止旧容器
          docker stop quanyi-backend || true
          docker rm quanyi-backend || true
          
          # 启动新容器
          docker run -d \
            --name quanyi-backend \
            --restart unless-stopped \
            -p 3000:3000 \
            -e PORT=3000 \
            -e NODE_ENV=production \
            -e WECHAT_APPID=${{ secrets.WECHAT_APPID }} \
            -e WECHAT_APPSECRET=${{ secrets.WECHAT_APPSECRET }} \
            -e MONGODB_URI=${{ secrets.MONGODB_URI }} \
            -e JWT_SECRET=${{ secrets.JWT_SECRET }} \
            -v /var/www/quanyixiaozhushou-app/public/uploads:/app/public/uploads \
            ghcr.io/${{ github.repository }}/backend:latest
          
          # 健康检查
          sleep 10
          curl -f http://localhost:3000/health || exit 1

环境变量配置

GitHub Secrets 需要配置

Secret Name Description Example
GIT_ACCESS_TOKEN Git 仓库访问令牌 用于拉取 https://git.dxz99wyr.cn/Superuser/miniapp-api
SSH_PRIVATE_KEY 服务器 SSH 私钥 -----BEGIN OPENSSH PRIVATE KEY-----...
WECHAT_APPID 微信小程序 AppID wxa83262674846ca1a
WECHAT_APPSECRET 微信小程序 AppSecret c40e9d356438f92d10091a115ee50172
MONGODB_URI MongoDB 连接字符串 mongodb://localhost:27017/quanyixiaozhushou
JWT_SECRET JWT 密钥 your_jwt_secret_key

部署脚本 (服务器端)

1. 初始化脚本 (首次部署)

#!/bin/bash
# deploy-init.sh

set -e

PROJECT_DIR="/var/www/quanyixiaozhushou-app"
BACKUP_DIR="/var/backups/quanyixiaozhushou"

# 创建目录
mkdir -p $PROJECT_DIR
mkdir -p $BACKUP_DIR
mkdir -p $PROJECT_DIR/public/uploads
mkdir -p $PROJECT_DIR/public/avatars

# 安装 Docker (如果未安装)
if ! command -v docker &> /dev/null; then
    curl -fsSL https://get.docker.com | sh
    systemctl enable docker
    systemctl start docker
fi

# 安装 Docker Compose
if ! command -v docker-compose &> /dev/null; then
    curl -L "https://github.com/docker/compose/releases/download/v2.23.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
    chmod +x /usr/local/bin/docker-compose
fi

echo "初始化完成"

2. 部署脚本 (日常更新)

#!/bin/bash
# deploy.sh

set -e

IMAGE_TAG=${1:-latest}
PROJECT_DIR="/var/www/quanyixiaozhushou-app"
BACKUP_DIR="/var/backups/quanyixiaozhushou"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)

echo "=== 开始部署 ==="
echo "镜像标签: $IMAGE_TAG"

# 1. 备份当前数据
echo "[1/5] 备份当前数据..."
mkdir -p $BACKUP_DIR/$TIMESTAMP
cp -r $PROJECT_DIR/public/uploads $BACKUP_DIR/$TIMESTAMP/ 2>/dev/null || true
mongodump --db quanyixiaozhushou --out $BACKUP_DIR/$TIMESTAMP/ 2>/dev/null || true

# 2. 拉取最新镜像
echo "[2/5] 拉取最新镜像..."
docker pull ghcr.io/your-repo/quanyixiaozhushou-backend:$IMAGE_TAG

# 3. 停止旧容器
echo "[3/5] 停止旧容器..."
docker stop quanyi-backend 2>/dev/null || true
docker rm quanyi-backend 2>/dev/null || true

# 4. 启动新容器
echo "[4/5] 启动新容器..."
docker run -d \
  --name quanyi-backend \
  --restart unless-stopped \
  -p 3000:3000 \
  -e PORT=3000 \
  -e NODE_ENV=production \
  -e WECHAT_APPID=$WECHAT_APPID \
  -e WECHAT_APPSECRET=$WECHAT_APPSECRET \
  -e MONGODB_URI=$MONGODB_URI \
  -e JWT_SECRET=$JWT_SECRET \
  -v $PROJECT_DIR/public/uploads:/app/public/uploads \
  -v $PROJECT_DIR/public/avatars:/app/public/avatars \
  ghcr.io/your-repo/quanyixiaozhushou-backend:$IMAGE_TAG

# 5. 健康检查
echo "[5/5] 健康检查..."
sleep 5

MAX_RETRIES=10
RETRY_COUNT=0

while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
  if curl -f -s http://localhost:3000/health > /dev/null; then
    echo "✅ 服务健康检查通过"
    break
  fi
  
  RETRY_COUNT=$((RETRY_COUNT + 1))
  echo "等待服务启动... ($RETRY_COUNT/$MAX_RETRIES)"
  sleep 3
done

if [ $RETRY_COUNT -eq $MAX_RETRIES ]; then
  echo "❌ 健康检查失败,执行回滚..."
  # 回滚逻辑
  docker stop quanyi-backend
  docker rm quanyi-backend
  # 启动上一个版本
  exit 1
fi

# 清理旧镜像
docker image prune -f

echo "=== 部署完成 ==="
echo "备份位置: $BACKUP_DIR/$TIMESTAMP"

健康检查端点

确保后端有以下健康检查端点:

// src/app.js
app.get('/health', (req, res) => {
  res.status(200).json({
    status: 'ok',
    timestamp: new Date().toISOString(),
    version: process.env.npm_package_version || '1.0.0'
  });
});

微信小程序 CI/CD

自动上传脚本

# .github/workflows/miniapp-deploy.yml
name: Miniapp Deploy

on:
  push:
    branches: [ master ]
    paths:
      - 'miniapp/**'

jobs:
  deploy-miniapp:
    runs-on: ubuntu-latest
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v4

    - name: Setup Node.js
      uses: actions/setup-node@v4
      with:
        node-version: '18'

    - name: Install miniprogram-ci
      run: npm install -g miniprogram-ci

    - name: Upload to WeChat
      working-directory: ./miniapp
      run: |
        miniprogram-ci upload \
          --pp ./ \
          --pkp ${{ secrets.WECHAT_PRIVATE_KEY }} \
          --appid ${{ secrets.WECHAT_APPID }} \
          --uv ${{ github.run_number }} \
          --enable-es6 true

监控与告警

1. 日志监控

# 查看实时日志
docker logs -f quanyi-backend

# 查看错误日志
docker logs quanyi-backend 2>&1 | grep ERROR

进程监控

# Docker 容器监控
docker stats quanyixiaozhushou-app

# 进入容器调试
docker exec -it quanyixiaozhushou-app sh

3. 告警规则

  • 服务 5xx 错误率 > 1%
  • 响应时间 > 2s
  • 内存使用 > 80%
  • CPU 使用 > 80%
  • 磁盘使用 > 85%

回滚策略

自动回滚条件

  • 健康检查失败
  • 错误率突增
  • 部署后 5 分钟内服务不可用

回滚脚本

#!/bin/bash
# rollback.sh

PREVIOUS_IMAGE=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep quanyixiaozhushou-backend | head -2 | tail -1)

echo "回滚到上一个版本: $PREVIOUS_IMAGE"

docker stop quanyi-backend
docker rm quanyi-backend
docker run -d \
  --name quanyi-backend \
  --restart unless-stopped \
  -p 3000:3000 \
  -e PORT=3000 \
  -e NODE_ENV=production \
  -v /var/www/quanyixiaozhushou-app/public/uploads:/app/public/uploads \
  $PREVIOUS_IMAGE

echo "回滚完成"

数据库迁移

MongoDB 迁移脚本

// migrations/001_add_subscription_type.js
const mongoose = require('mongoose');

async function migrate() {
  await mongoose.connect(process.env.MONGODB_URI);
  
  // 添加 type 字段到现有订阅记录
  await mongoose.connection.collection('usersubscriptions').updateMany(
    { type: { $exists: false } },
    { $set: { type: 'version-update' } }
  );
  
  console.log('Migration completed');
  process.exit(0);
}

migrate().catch(console.error);

安全检查清单

  • 环境变量不提交到 Git
  • 使用 secrets 管理敏感信息
  • Docker 镜像使用非 root 用户运行
  • 启用 HTTPS
  • 配置防火墙规则
  • 定期更新依赖
  • 启用日志审计
  • 配置备份策略

附录

A. 常用命令

# 查看容器状态
docker ps

# 查看容器日志
docker logs quanyi-backend

# 进入容器
docker exec -it quanyi-backend sh

# 重启容器
docker restart quanyi-backend

# 查看资源使用
docker stats quanyi-backend

B. 故障排查

# 服务无法启动
docker logs quanyi-backend

# MongoDB 连接失败
docker exec -it quanyi-backend node -e "require('mongoose').connect(process.env.MONGODB_URI).then(() => console.log('OK')).catch(e => console.error(e))"

# 端口占用
netstat -tlnp | grep 3000

C. 联系方式与仓库信息

D. Git 仓库配置命令

# 添加远程仓库
git remote add origin https://git.dxz99wyr.cn/Superuser/miniapp-api

# 推送代码
git push -u origin master

# 拉取最新代码
git pull origin master