# 权益小助手 - CI/CD 流程规范 ## 项目概述 - **项目名称**: 权益小助手 (QuanYiXiaoZhuShou) - **项目类型**: 微信小程序 + Node.js 后端 - **当前版本**: v0.1.0 - **Git 仓库**: https://git.dxz99wyr.cn/Superuser/miniapp-api ## 项目结构 ``` 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 | 运行中 | ```bash # 查看容器状态 docker ps # 查看后端日志 docker logs quanyixiaozhushou-app # 查看 MongoDB 日志 docker logs quanyixiaozhushou-mongo ``` ### 环境变量 (生产环境) ```env 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 流程 ```mermaid 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) ```yaml # .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: 依赖安装与测试 ```yaml - 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 构建 ```yaml - 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: 部署到服务器 ```yaml - 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. 初始化脚本 (首次部署) ```bash #!/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. 部署脚本 (日常更新) ```bash #!/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" ``` ## 健康检查端点 确保后端有以下健康检查端点: ```javascript // 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 ### 自动上传脚本 ```yaml # .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. 日志监控 ```bash # 查看实时日志 docker logs -f quanyi-backend # 查看错误日志 docker logs quanyi-backend 2>&1 | grep ERROR ``` ### 进程监控 ```bash # Docker 容器监控 docker stats quanyixiaozhushou-app # 进入容器调试 docker exec -it quanyixiaozhushou-app sh ``` ### 3. 告警规则 - 服务 5xx 错误率 > 1% - 响应时间 > 2s - 内存使用 > 80% - CPU 使用 > 80% - 磁盘使用 > 85% ## 回滚策略 ### 自动回滚条件 - 健康检查失败 - 错误率突增 - 部署后 5 分钟内服务不可用 ### 回滚脚本 ```bash #!/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 迁移脚本 ```javascript // 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. 常用命令 ```bash # 查看容器状态 docker ps # 查看容器日志 docker logs quanyi-backend # 进入容器 docker exec -it quanyi-backend sh # 重启容器 docker restart quanyi-backend # 查看资源使用 docker stats quanyi-backend ``` ### B. 故障排查 ```bash # 服务无法启动 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. 联系方式与仓库信息 - **Git 仓库**: https://git.dxz99wyr.cn/Superuser/miniapp-api - **服务器**: 8.136.137.59 - **域名**: api-miniapp.dxz99wyr.cn - **SSH 密钥**: D:\003_Project\小程序连接.pem ### D. Git 仓库配置命令 ```bash # 添加远程仓库 git remote add origin https://git.dxz99wyr.cn/Superuser/miniapp-api # 推送代码 git push -u origin master # 拉取最新代码 git pull origin master ```