# Kubernetes 部署

在 Kubernetes 上进行 Monetarie PIX 的生产级部署，具备高可用、自动扩缩容和监控能力。

## 前提条件

- Kubernetes 1.28+ 集群，至少 3 个节点
- `kubectl` 已配置集群访问权限
- 容器仓库中已有 Monetarie PIX 镜像
- PostgreSQL、Redis 和 NATS 可从集群访问
- 目标域名的 TLS 证书

## 命名空间设置

```bash
kubectl create namespace pix
```

## Secrets

```yaml
# db-credentials.yaml
apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
  namespace: pix
type: Opaque
stringData:
  database-url: "postgres://monetarie_prod:PASSWORD@10.140.241.2:5432/monetarie"
  secret-key-base: "GENERATE_WITH_mix_phx.gen.secret"
---
# shared-jwt-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: monetarie-shared-jwt-secret
  namespace: pix
type: Opaque
stringData:
  jwt-secret: "shared-secret-with-core-banking"
```

```bash
kubectl apply -f db-credentials.yaml
kubectl apply -f shared-jwt-secret.yaml
```

::: warning KEBAB-CASE 键名
Kubernetes Secret 键使用 **kebab-case** (`database-url`、`secret-key-base`)，不是 SCREAMING_SNAKE_CASE。`backend.yaml` 会将这些映射到正确的环境变量。
:::

## 后端 Deployment

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: pix-backend
  namespace: pix
spec:
  replicas: 2
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app: pix-backend
  template:
    metadata:
      labels:
        app: pix-backend
    spec:
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 100
              podAffinityTerm:
                labelSelector:
                  matchExpressions:
                    - key: app
                      operator: In
                      values: [pix-backend]
                topologyKey: kubernetes.io/hostname
      terminationGracePeriodSeconds: 30
      containers:
        - name: pix-backend
          image: REGISTRY/pix-backend:TAG
          ports:
            - containerPort: 4001
            - containerPort: 4002
            - containerPort: 4003
          resources:
            requests:
              cpu: "2"
              memory: 2Gi
            limits:
              cpu: "4"
              memory: 4Gi
          env:
            - name: DB_HOST
              value: "10.140.241.2"
            - name: REDIS_HOST
              value: "10.140.240.4"
            - name: NATS_HOST
              value: "10.10.40.5"
            - name: NATS_ENABLED
              value: "true"
            - name: POOL_SIZE
              value: "200"
            - name: SECRET_KEY_BASE
              valueFrom:
                secretKeyRef:
                  name: db-credentials
                  key: secret-key-base
          livenessProbe:
            httpGet:
              path: /health
              port: 4003
            initialDelaySeconds: 30
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /health
              port: 4003
            initialDelaySeconds: 10
            periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
  name: pix-backend
  namespace: pix
spec:
  selector:
    app: pix-backend
  ports:
    - name: dict
      port: 4001
    - name: spi
      port: 4002
    - name: settlement
      port: 4003
```

## PodDisruptionBudget

```yaml
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: pix-backend-pdb
  namespace: pix
spec:
  minAvailable: 1
  selector:
    matchLabels:
      app: pix-backend
```

## Ingress (Traefik)

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: pix-backend-ingress
  namespace: pix
  annotations:
    traefik.ingress.kubernetes.io/router.tls: "true"
spec:
  tls:
    - hosts:
        - pixapi-dev.fluxiq.com.br
      secretName: monetarie-dev-tls
  rules:
    - host: pixapi-dev.fluxiq.com.br
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: pix-backend
                port:
                  number: 4003
```

## 数据库迁移

```bash
kubectl exec -n pix deployment/pix-backend -- bin/monetarie_pix eval "Shared.Release.migrate()"
```

## NetworkPolicy

```yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: pix-backend-netpol
  namespace: pix
spec:
  podSelector:
    matchLabels:
      app: pix-backend
  policyTypes: [Ingress, Egress]
  ingress:
    - from:
        - podSelector:
            matchLabels:
              purpose: ingress-controller
      ports:
        - port: 4003
  egress:
    - to:
        - ipBlock:
            cidr: 10.0.0.0/8
      ports:
        - port: 5432  # PostgreSQL
        - port: 6379  # Redis
        - port: 4222  # NATS
```

## 扩缩容

```bash
# 手动扩缩容
kubectl scale deployment pix-backend -n pix --replicas=4

# HPA (自动扩缩容)
kubectl autoscale deployment pix-backend -n pix \
  --min=2 --max=8 --cpu-percent=70
```

## 验证

```bash
# 检查 pods
kubectl get pods -n pix

# 查看日志
kubectl logs -n pix deployment/pix-backend --tail=50

# 检查健康
kubectl exec -n pix deployment/pix-backend -- curl -s localhost:4003/health

# 检查 NATS workers
kubectl logs -n pix deployment/pix-backend | grep "Worker started"
```

## 预期结果

完成此部署后：

- 2+ 后端 pods 运行，跨节点反亲和性
- PodDisruptionBudget 确保维护期间至少 1 个 pod 可用
- 在配置域名上的 TLS 终止 ingress
- NetworkPolicy 限制流量仅来自授权来源
- 健康检查和就绪探针活跃
- NATS workers 在所有 pods 间处理消息
