rwadurian/backend/services/wallet-service/docs/DEPLOYMENT.md

788 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Wallet Service 部署文档
## 概述
本文档描述 Wallet Service 的部署架构、配置方式、Docker 容器化以及生产环境部署流程。
---
## 部署架构
```
┌─────────────────┐
│ Load Balancer │
│ (Nginx/ALB) │
└────────┬────────┘
┌────────────────────────┼────────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Wallet Service │ │ Wallet Service │ │ Wallet Service │
│ Instance 1 │ │ Instance 2 │ │ Instance N │
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
└──────────────────────┼──────────────────────┘
┌────────┴────────┐
│ PostgreSQL │
│ (Primary) │
└────────┬────────┘
┌────────┴────────┐
│ PostgreSQL │
│ (Replica) │
└─────────────────┘
```
---
## 环境要求
### 系统要求
| 组件 | 最低要求 | 推荐配置 |
|-----|---------|---------|
| CPU | 2 核 | 4+ 核 |
| 内存 | 2 GB | 4+ GB |
| 磁盘 | 20 GB SSD | 50+ GB SSD |
| Node.js | 20.x | 20.x LTS |
| PostgreSQL | 15.x | 15.x |
### 依赖服务
- PostgreSQL 15.x 数据库
- Docker (可选,用于容器化部署)
- Kubernetes (可选,用于编排)
---
## 环境变量配置
### 必需变量
| 变量 | 描述 | 示例 |
|-----|------|-----|
| `DATABASE_URL` | PostgreSQL 连接字符串 | `postgresql://user:pass@host:5432/db` |
| `JWT_SECRET` | JWT 签名密钥 | `your-secret-key-here` |
| `NODE_ENV` | 环境标识 | `production` |
| `PORT` | 服务端口 | `3000` |
### 可选变量
| 变量 | 描述 | 默认值 |
|-----|------|-------|
| `JWT_EXPIRES_IN` | JWT 过期时间 | `24h` |
| `LOG_LEVEL` | 日志级别 | `info` |
| `CORS_ORIGIN` | CORS 允许源 | `*` |
### 环境配置文件
```bash
# .env.production
DATABASE_URL="postgresql://wallet:strong_password@db.example.com:5432/wallet_prod?schema=public&connection_limit=10"
JWT_SECRET="your-production-secret-key-min-32-chars"
NODE_ENV=production
PORT=3000
LOG_LEVEL=info
```
---
## Docker 部署
### Dockerfile
```dockerfile
# Dockerfile
# 构建阶段
FROM node:20-alpine AS builder
WORKDIR /app
# 复制依赖文件
COPY package*.json ./
COPY prisma ./prisma/
# 安装依赖
RUN npm ci --only=production=false
# 复制源代码
COPY . .
# 生成 Prisma Client
RUN npx prisma generate
# 构建应用
RUN npm run build
# 清理开发依赖
RUN npm prune --production
# 生产阶段
FROM node:20-alpine AS production
WORKDIR /app
# 安装 dumb-init 用于信号处理
RUN apk add --no-cache dumb-init
# 创建非 root 用户
RUN addgroup -g 1001 -S nodejs && \
adduser -S nestjs -u 1001
# 复制构建产物
COPY --from=builder --chown=nestjs:nodejs /app/dist ./dist
COPY --from=builder --chown=nestjs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nestjs:nodejs /app/prisma ./prisma
COPY --from=builder --chown=nestjs:nodejs /app/package.json ./
# 切换到非 root 用户
USER nestjs
# 暴露端口
EXPOSE 3000
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/api/v1/health || exit 1
# 启动命令
ENTRYPOINT ["dumb-init", "--"]
CMD ["sh", "-c", "npx prisma migrate deploy && node dist/main"]
```
### .dockerignore
```
# .dockerignore
node_modules
dist
coverage
.git
.gitignore
*.md
*.log
.env*
!.env.example
test
.vscode
.idea
```
### 构建和运行
```bash
# 构建镜像
docker build -t wallet-service:latest .
# 运行容器
docker run -d \
--name wallet-service \
-p 3000:3000 \
-e DATABASE_URL="postgresql://wallet:password@host:5432/wallet" \
-e JWT_SECRET="your-secret-key" \
-e NODE_ENV=production \
wallet-service:latest
# 查看日志
docker logs -f wallet-service
```
---
## Docker Compose 部署
### docker-compose.yml
```yaml
# docker-compose.yml
version: '3.8'
services:
wallet-service:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
DATABASE_URL: postgresql://wallet:wallet123@postgres:5432/wallet?schema=public
JWT_SECRET: ${JWT_SECRET:-development-secret-change-in-production}
NODE_ENV: ${NODE_ENV:-production}
depends_on:
postgres:
condition: service_healthy
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/api/v1/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
postgres:
image: postgres:15-alpine
environment:
POSTGRES_USER: wallet
POSTGRES_PASSWORD: wallet123
POSTGRES_DB: wallet
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U wallet -d wallet"]
interval: 10s
timeout: 5s
retries: 5
volumes:
postgres_data:
```
### docker-compose.prod.yml
```yaml
# docker-compose.prod.yml
version: '3.8'
services:
wallet-service:
image: wallet-service:${VERSION:-latest}
ports:
- "3000:3000"
environment:
DATABASE_URL: ${DATABASE_URL}
JWT_SECRET: ${JWT_SECRET}
NODE_ENV: production
LOG_LEVEL: info
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 10s
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/api/v1/health"]
interval: 30s
timeout: 10s
retries: 3
```
### 运行命令
```bash
# 开发环境
docker-compose up -d
# 生产环境
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
# 扩展副本
docker-compose -f docker-compose.prod.yml up -d --scale wallet-service=3
```
---
## Kubernetes 部署
### Deployment
```yaml
# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: wallet-service
labels:
app: wallet-service
spec:
replicas: 3
selector:
matchLabels:
app: wallet-service
template:
metadata:
labels:
app: wallet-service
spec:
containers:
- name: wallet-service
image: wallet-service:latest
ports:
- containerPort: 3000
env:
- name: NODE_ENV
value: "production"
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: wallet-secrets
key: database-url
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: wallet-secrets
key: jwt-secret
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /api/v1/health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /api/v1/health
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
```
### Service
```yaml
# k8s/service.yaml
apiVersion: v1
kind: Service
metadata:
name: wallet-service
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 3000
selector:
app: wallet-service
```
### Secret
```yaml
# k8s/secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: wallet-secrets
type: Opaque
stringData:
database-url: "postgresql://wallet:password@postgres:5432/wallet"
jwt-secret: "your-production-secret-key"
```
### Ingress
```yaml
# k8s/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: wallet-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: wallet.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: wallet-service
port:
number: 80
```
### 部署命令
```bash
# 创建 Secret
kubectl apply -f k8s/secret.yaml
# 部署服务
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yaml
kubectl apply -f k8s/ingress.yaml
# 查看状态
kubectl get pods -l app=wallet-service
kubectl get svc wallet-service
# 滚动更新
kubectl set image deployment/wallet-service wallet-service=wallet-service:v2.0.0
# 回滚
kubectl rollout undo deployment/wallet-service
```
---
## 数据库迁移
### Prisma 迁移
```bash
# 开发环境 - 创建迁移
npx prisma migrate dev --name add_new_field
# 生产环境 - 应用迁移
npx prisma migrate deploy
# 查看迁移状态
npx prisma migrate status
```
### 迁移脚本
```bash
#!/bin/bash
# scripts/migrate.sh
set -e
echo "Running database migrations..."
# 等待数据库就绪
until pg_isready -h $DB_HOST -p $DB_PORT -U $DB_USER; do
echo "Waiting for database..."
sleep 2
done
# 运行迁移
npx prisma migrate deploy
echo "Migrations completed successfully!"
```
---
## CI/CD 流水线
### GitHub Actions
```yaml
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
release:
types: [published]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15-alpine
env:
POSTGRES_USER: wallet
POSTGRES_PASSWORD: wallet123
POSTGRES_DB: wallet_test
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Generate Prisma Client
run: npx prisma generate
- name: Run migrations
run: npx prisma db push
env:
DATABASE_URL: postgresql://wallet:wallet123@localhost:5432/wallet_test
- name: Run tests
run: npm test
env:
DATABASE_URL: postgresql://wallet:wallet123@localhost:5432/wallet_test
JWT_SECRET: test-secret
- name: Run E2E tests
run: npm run test:e2e
env:
DATABASE_URL: postgresql://wallet:wallet123@localhost:5432/wallet_test
JWT_SECRET: test-secret
build:
needs: test
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v3
- name: Log in to Container Registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=sha
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
deploy:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- name: Deploy to production
run: |
# 触发部署 (例如 ArgoCD, Kubernetes, 或 SSH)
echo "Deploying to production..."
```
---
## 监控和日志
### 健康检查端点
```bash
# 健康检查
curl http://localhost:3000/api/v1/health
# 响应示例
{
"success": true,
"data": {
"status": "ok",
"service": "wallet-service",
"timestamp": "2024-01-01T00:00:00.000Z"
}
}
```
### 日志配置
```typescript
// src/main.ts
import { Logger } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule, {
logger: process.env.NODE_ENV === 'production'
? ['error', 'warn', 'log']
: ['error', 'warn', 'log', 'debug', 'verbose'],
});
// ...
}
```
### 日志格式 (生产环境)
```json
{
"timestamp": "2024-01-01T00:00:00.000Z",
"level": "info",
"context": "WalletController",
"message": "Deposit processed",
"userId": "12345",
"amount": 100,
"requestId": "abc-123"
}
```
---
## 安全配置
### Nginx 反向代理
```nginx
# nginx.conf
upstream wallet_service {
server wallet-service-1:3000;
server wallet-service-2:3000;
server wallet-service-3:3000;
}
server {
listen 80;
server_name wallet.example.com;
# 重定向到 HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name wallet.example.com;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
# 安全头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Strict-Transport-Security "max-age=31536000" always;
# 速率限制
limit_req zone=api burst=20 nodelay;
location /api/ {
proxy_pass http://wallet_service;
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;
}
}
```
### 环境变量安全
```bash
# 不要在版本控制中提交敏感信息
# 使用环境变量或密钥管理服务
# AWS Secrets Manager
aws secretsmanager get-secret-value --secret-id wallet/production
# HashiCorp Vault
vault kv get secret/wallet/production
```
---
## 故障排除
### 常见问题
#### 1. 数据库连接失败
```bash
# 检查连接
psql $DATABASE_URL -c "SELECT 1"
# 检查网络
nc -zv db.example.com 5432
```
#### 2. 容器无法启动
```bash
# 查看日志
docker logs wallet-service
# 进入容器调试
docker exec -it wallet-service sh
```
#### 3. 迁移失败
```bash
# 查看迁移状态
npx prisma migrate status
# 重置数据库 (开发环境)
npx prisma migrate reset
```
### 回滚流程
```bash
# Docker 回滚
docker stop wallet-service
docker run -d --name wallet-service wallet-service:previous-tag
# Kubernetes 回滚
kubectl rollout undo deployment/wallet-service
# 数据库回滚 (手动)
# 需要提前备份Prisma 不支持自动回滚
pg_restore -d wallet wallet_backup.dump
```
---
## 部署检查清单
### 部署前
- [ ] 所有测试通过
- [ ] 代码审查完成
- [ ] 环境变量配置正确
- [ ] 数据库备份完成
- [ ] 迁移脚本测试通过
### 部署中
- [ ] 监控指标正常
- [ ] 健康检查通过
- [ ] 日志无错误
- [ ] API 响应正常
### 部署后
- [ ] 功能验证通过
- [ ] 性能测试通过
- [ ] 安全扫描通过
- [ ] 文档更新
---
## 联系方式
如遇部署问题,请联系:
- 运维团队: devops@example.com
- 开发团队: dev@example.com
- 紧急联系: oncall@example.com