# Authorization Service 部署文档 ## 目录 1. [部署架构](#部署架构) 2. [环境配置](#环境配置) 3. [Docker 部署](#docker-部署) 4. [Kubernetes 部署](#kubernetes-部署) 5. [数据库迁移](#数据库迁移) 6. [监控与日志](#监控与日志) 7. [故障排除](#故障排除) --- ## 部署架构 ### 整体架构 ``` ┌─────────────────┐ │ Load Balancer │ │ (Nginx/ALB) │ └────────┬────────┘ │ ┌───────────────────┼───────────────────┐ │ │ │ ┌─────────▼─────────┐ ┌──────▼──────┐ ┌─────────▼─────────┐ │ Authorization │ │ Identity │ │ Other │ │ Service │ │ Service │ │ Services │ │ (Port 3002) │ │ (Port 3001) │ │ │ └─────────┬─────────┘ └─────────────┘ └──────────────────┘ │ ┌─────────┴─────────────────────────────────┐ │ │ ┌───▼───┐ ┌────────┐ ┌────────┐ ┌──────────┐ │ DB │ │ Redis │ │ Kafka │ │ External │ │(PG 15)│ │ (7.x) │ │(3.7.x) │ │ Services │ └───────┘ └────────┘ └────────┘ └──────────┘ ``` ### 服务依赖 | 依赖 | 版本 | 用途 | |------|------|------| | PostgreSQL | 15.x | 主数据库 | | Redis | 7.x | 缓存、会话 | | Kafka | 3.7.x | 事件消息队列 | | Identity Service | - | JWT 验证 | | Referral Service | - | 推荐关系查询 | | Statistics Service | - | 团队统计查询 | --- ## 环境配置 ### 环境变量 ```bash # .env.production # 应用配置 NODE_ENV=production PORT=3002 # 数据库配置 DATABASE_URL=postgresql://user:password@db-host:5432/authorization_prod # Redis 配置 REDIS_HOST=redis-host REDIS_PORT=6379 REDIS_PASSWORD=redis-password # Kafka 配置 KAFKA_BROKERS=kafka-1:9092,kafka-2:9092,kafka-3:9092 KAFKA_CLIENT_ID=authorization-service KAFKA_CONSUMER_GROUP=authorization-service-group # JWT 配置 JWT_SECRET=your-production-jwt-secret-key JWT_EXPIRES_IN=1h # 外部服务 IDENTITY_SERVICE_URL=http://identity-service:3001 REFERRAL_SERVICE_URL=http://referral-service:3003 STATISTICS_SERVICE_URL=http://statistics-service:3004 # 日志 LOG_LEVEL=info ``` ### 配置说明 | 配置项 | 说明 | 默认值 | |--------|------|--------| | NODE_ENV | 运行环境 | production | | PORT | 服务端口 | 3002 | | DATABASE_URL | PostgreSQL 连接字符串 | - | | REDIS_HOST | Redis 主机地址 | localhost | | REDIS_PORT | Redis 端口 | 6379 | | KAFKA_BROKERS | Kafka broker 地址列表 | - | | JWT_SECRET | JWT 签名密钥 | - | | LOG_LEVEL | 日志级别 | info | ### 密钥管理 生产环境建议使用密钥管理服务: - **AWS**: AWS Secrets Manager / Parameter Store - **阿里云**: KMS / 密钥管理服务 - **Kubernetes**: Secrets --- ## Docker 部署 ### 生产 Dockerfile ```dockerfile # Dockerfile # 构建阶段 FROM node:20-alpine AS builder WORKDIR /app # 安装 OpenSSL RUN apk add --no-cache openssl openssl-dev libc6-compat # 复制依赖文件 COPY package*.json ./ COPY prisma ./prisma/ # 安装依赖 RUN npm ci --only=production # 生成 Prisma 客户端 RUN npx prisma generate # 复制源代码 COPY . . # 构建 RUN npm run build # 生产阶段 FROM node:20-alpine AS production WORKDIR /app # 安装运行时依赖 RUN apk add --no-cache openssl libc6-compat # 复制构建产物 COPY --from=builder /app/dist ./dist COPY --from=builder /app/node_modules ./node_modules COPY --from=builder /app/package*.json ./ COPY --from=builder /app/prisma ./prisma # 创建非 root 用户 RUN addgroup -g 1001 -S nodejs && \ adduser -S nestjs -u 1001 && \ chown -R nestjs:nodejs /app USER nestjs # 健康检查 HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ CMD wget --no-verbose --tries=1 --spider http://localhost:3002/health || exit 1 EXPOSE 3002 CMD ["node", "dist/main.js"] ``` ### Docker Compose (生产) ```yaml # docker-compose.prod.yml version: '3.8' services: authorization-service: build: context: . dockerfile: Dockerfile ports: - "3002:3002" environment: - NODE_ENV=production - DATABASE_URL=postgresql://postgres:password@db:5432/authorization - REDIS_HOST=redis - REDIS_PORT=6379 - KAFKA_BROKERS=kafka:9092 - JWT_SECRET=${JWT_SECRET} depends_on: db: condition: service_healthy redis: condition: service_healthy kafka: condition: service_healthy restart: unless-stopped deploy: resources: limits: cpus: '1' memory: 1G reservations: cpus: '0.5' memory: 512M db: image: postgres:15-alpine environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: password POSTGRES_DB: authorization volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 10s timeout: 5s retries: 5 restart: unless-stopped redis: image: redis:7-alpine command: redis-server --requirepass ${REDIS_PASSWORD} volumes: - redis_data:/data healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 5 restart: unless-stopped kafka: image: apache/kafka:3.7.0 environment: KAFKA_NODE_ID: 1 KAFKA_PROCESS_ROLES: broker,controller KAFKA_LISTENERS: PLAINTEXT://:9092,CONTROLLER://:9093 KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092 KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:9093 KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 CLUSTER_ID: MkU3OEVBNTcwNTJENDM2Qk volumes: - kafka_data:/var/lib/kafka/data healthcheck: test: ["CMD-SHELL", "/opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --list"] interval: 30s timeout: 10s retries: 5 start_period: 60s restart: unless-stopped volumes: postgres_data: redis_data: kafka_data: networks: default: driver: bridge ``` ### 构建和部署 ```bash # 构建镜像 docker build -t authorization-service:latest . # 推送到镜像仓库 docker tag authorization-service:latest your-registry/authorization-service:latest docker push your-registry/authorization-service:latest # 使用 Docker Compose 部署 docker compose -f docker-compose.prod.yml up -d # 查看日志 docker compose -f docker-compose.prod.yml logs -f authorization-service # 扩容 docker compose -f docker-compose.prod.yml up -d --scale authorization-service=3 ``` --- ## Kubernetes 部署 ### Deployment ```yaml # k8s/deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: authorization-service namespace: rwadurian labels: app: authorization-service spec: replicas: 3 selector: matchLabels: app: authorization-service template: metadata: labels: app: authorization-service spec: containers: - name: authorization-service image: your-registry/authorization-service:latest ports: - containerPort: 3002 env: - name: NODE_ENV value: "production" - name: PORT value: "3002" - name: DATABASE_URL valueFrom: secretKeyRef: name: authorization-secrets key: database-url - name: REDIS_HOST valueFrom: configMapKeyRef: name: authorization-config key: redis-host - name: REDIS_PORT valueFrom: configMapKeyRef: name: authorization-config key: redis-port - name: KAFKA_BROKERS valueFrom: configMapKeyRef: name: authorization-config key: kafka-brokers - name: JWT_SECRET valueFrom: secretKeyRef: name: authorization-secrets key: jwt-secret resources: requests: memory: "512Mi" cpu: "250m" limits: memory: "1Gi" cpu: "1000m" readinessProbe: httpGet: path: /health port: 3002 initialDelaySeconds: 10 periodSeconds: 5 livenessProbe: httpGet: path: /health port: 3002 initialDelaySeconds: 30 periodSeconds: 10 imagePullSecrets: - name: registry-credentials ``` ### Service ```yaml # k8s/service.yaml apiVersion: v1 kind: Service metadata: name: authorization-service namespace: rwadurian spec: selector: app: authorization-service ports: - port: 3002 targetPort: 3002 type: ClusterIP ``` ### ConfigMap ```yaml # k8s/configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: authorization-config namespace: rwadurian data: redis-host: "redis-master.redis.svc.cluster.local" redis-port: "6379" kafka-brokers: "kafka-0.kafka.svc.cluster.local:9092,kafka-1.kafka.svc.cluster.local:9092" ``` ### Secret ```yaml # k8s/secret.yaml apiVersion: v1 kind: Secret metadata: name: authorization-secrets namespace: rwadurian type: Opaque stringData: database-url: "postgresql://user:password@postgres:5432/authorization" jwt-secret: "your-production-jwt-secret" ``` ### Ingress ```yaml # k8s/ingress.yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: authorization-ingress namespace: rwadurian annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: ingressClassName: nginx rules: - host: api.rwadurian.com http: paths: - path: /authorization pathType: Prefix backend: service: name: authorization-service port: number: 3002 ``` ### HPA (自动扩缩容) ```yaml # k8s/hpa.yaml apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: authorization-service-hpa namespace: rwadurian spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: authorization-service minReplicas: 2 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 - type: Resource resource: name: memory target: type: Utilization averageUtilization: 80 ``` ### 部署命令 ```bash # 创建命名空间 kubectl create namespace rwadurian # 应用配置 kubectl apply -f k8s/ # 查看部署状态 kubectl get pods -n rwadurian -l app=authorization-service # 查看日志 kubectl logs -f -n rwadurian -l app=authorization-service # 扩缩容 kubectl scale deployment authorization-service -n rwadurian --replicas=5 ``` --- ## 数据库迁移 ### 迁移策略 1. **新部署**: 自动运行所有迁移 2. **升级部署**: 先迁移数据库,再部署新版本 3. **回滚**: 支持向下迁移 ### 迁移命令 ```bash # 创建新迁移 npx prisma migrate dev --name add_new_field # 应用迁移(生产) npx prisma migrate deploy # 重置数据库(仅开发) npx prisma migrate reset # 查看迁移状态 npx prisma migrate status ``` ### 迁移脚本 ```bash #!/bin/bash # scripts/migrate.sh set -e echo "Running database migrations..." # 等待数据库就绪 until npx prisma migrate status > /dev/null 2>&1; do echo "Waiting for database..." sleep 2 done # 运行迁移 npx prisma migrate deploy echo "Migrations completed successfully!" ``` ### Kubernetes Job (迁移) ```yaml # k8s/migration-job.yaml apiVersion: batch/v1 kind: Job metadata: name: authorization-migration namespace: rwadurian spec: template: spec: containers: - name: migration image: your-registry/authorization-service:latest command: ["npx", "prisma", "migrate", "deploy"] env: - name: DATABASE_URL valueFrom: secretKeyRef: name: authorization-secrets key: database-url restartPolicy: Never backoffLimit: 3 ``` --- ## 监控与日志 ### 健康检查端点 ```typescript // src/health/health.controller.ts @Controller('health') export class HealthController { constructor( private health: HealthCheckService, private db: PrismaHealthIndicator, private redis: RedisHealthIndicator, ) {} @Get() @HealthCheck() check() { return this.health.check([ () => this.db.pingCheck('database'), () => this.redis.pingCheck('redis'), ]) } @Get('live') live() { return { status: 'ok' } } @Get('ready') @HealthCheck() ready() { return this.health.check([ () => this.db.pingCheck('database'), ]) } } ``` ### Prometheus 指标 ```typescript // src/metrics/metrics.module.ts import { PrometheusModule } from '@willsoto/nestjs-prometheus' @Module({ imports: [ PrometheusModule.register({ defaultMetrics: { enabled: true, }, path: '/metrics', }), ], }) export class MetricsModule {} ``` ### 日志配置 ```typescript // src/main.ts import { WinstonModule } from 'nest-winston' import * as winston from 'winston' const app = await NestFactory.create(AppModule, { logger: WinstonModule.createLogger({ transports: [ new winston.transports.Console({ format: winston.format.combine( winston.format.timestamp(), winston.format.json(), ), }), ], }), }) ``` ### 结构化日志格式 ```json { "timestamp": "2024-01-20T10:30:00.000Z", "level": "info", "message": "Authorization created", "service": "authorization-service", "traceId": "abc-123", "userId": "user-001", "authorizationId": "auth-456", "roleType": "AUTH_PROVINCE_COMPANY" } ``` --- ## 故障排除 ### 常见问题 #### 1. 数据库连接失败 ```bash # 检查数据库连接 npx prisma db pull # 查看连接字符串 echo $DATABASE_URL # 测试网络连通性 nc -zv db-host 5432 ``` #### 2. Redis 连接失败 ```bash # 测试 Redis 连接 redis-cli -h redis-host -p 6379 -a password ping # 查看 Redis 状态 redis-cli -h redis-host info ``` #### 3. Kafka 连接失败 ```bash # 列出 topics kafka-topics.sh --bootstrap-server kafka:9092 --list # 查看 consumer groups kafka-consumer-groups.sh --bootstrap-server kafka:9092 --list ``` #### 4. Pod 启动失败 ```bash # 查看 Pod 状态 kubectl describe pod -n rwadurian # 查看容器日志 kubectl logs -n rwadurian # 进入容器调试 kubectl exec -it -n rwadurian -- sh ``` ### 性能调优 #### 数据库连接池 ```typescript // prisma/schema.prisma datasource db { provider = "postgresql" url = env("DATABASE_URL") // 连接池配置 // ?connection_limit=20&pool_timeout=30 } ``` #### Redis 连接池 ```typescript // src/infrastructure/cache/redis.service.ts const redis = new Redis({ host: process.env.REDIS_HOST, port: parseInt(process.env.REDIS_PORT), password: process.env.REDIS_PASSWORD, maxRetriesPerRequest: 3, enableReadyCheck: true, // 连接池大小 lazyConnect: true, }) ``` ### 运维命令 ```bash # 查看服务状态 kubectl get all -n rwadurian # 查看资源使用 kubectl top pods -n rwadurian # 滚动更新 kubectl rollout restart deployment/authorization-service -n rwadurian # 回滚 kubectl rollout undo deployment/authorization-service -n rwadurian # 查看回滚历史 kubectl rollout history deployment/authorization-service -n rwadurian ``` --- ## 部署检查清单 ### 部署前 - [ ] 环境变量配置完成 - [ ] 数据库迁移已准备 - [ ] 镜像已构建并推送 - [ ] 配置文件已验证 - [ ] 密钥已配置 ### 部署中 - [ ] 数据库迁移成功 - [ ] Pod 启动正常 - [ ] 健康检查通过 - [ ] 服务可访问 ### 部署后 - [ ] 功能测试通过 - [ ] 监控指标正常 - [ ] 日志无异常 - [ ] 通知相关人员 --- ## 参考资源 - [Docker 官方文档](https://docs.docker.com/) - [Kubernetes 官方文档](https://kubernetes.io/docs/) - [Prisma 部署指南](https://www.prisma.io/docs/guides/deployment) - [NestJS 部署指南](https://docs.nestjs.com/faq/common-errors)