13 KiB
13 KiB
权益小助手 - 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 | 运行中 |
# 查看容器状态
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. 联系方式与仓库信息
- Git 仓库: https://git.dxz99wyr.cn/Superuser/miniapp-api
- 服务器: 8.136.137.59
- 域名: api-miniapp.dxz99wyr.cn
- SSH 密钥: D:\003_Project\小程序连接.pem
D. Git 仓库配置命令
# 添加远程仓库
git remote add origin https://git.dxz99wyr.cn/Superuser/miniapp-api
# 推送代码
git push -u origin master
# 拉取最新代码
git pull origin master