561 lines
13 KiB
Markdown
561 lines
13 KiB
Markdown
# 权益小助手 - 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
|
|
```
|