# 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 <pod-name> -n rwadurian

# 查看容器日志
kubectl logs <pod-name> -n rwadurian

# 进入容器调试
kubectl exec -it <pod-name> -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)
