# 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