# Kubernetes Deployment

Production-grade deployment of Monetarie PIX on Kubernetes with high availability, auto-scaling, and monitoring.

## Prerequisites

- Kubernetes 1.28+ cluster with at least 3 nodes
- `kubectl` configured with cluster access
- Container registry with Monetarie PIX images
- PostgreSQL, Redis, and NATS accessible from the cluster
- TLS certificate for the target domain

## Namespace Setup

```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 KEYS
Kubernetes Secret keys use **kebab-case** (`database-url`, `secret-key-base`), NOT SCREAMING_SNAKE_CASE. The `backend.yaml` maps these to the correct environment variables.
:::

## Backend Deployment

```yaml
# backend.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
```

## Database Migration

```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
```

## Scaling

```bash
# Manual scaling
kubectl scale deployment pix-backend -n pix --replicas=4

# HPA (auto-scaling)
kubectl autoscale deployment pix-backend -n pix \
  --min=2 --max=8 --cpu-percent=70
```

## Verification

```bash
# Check pods
kubectl get pods -n pix

# Check logs
kubectl logs -n pix deployment/pix-backend --tail=50

# Check health
kubectl exec -n pix deployment/pix-backend -- curl -s localhost:4003/health

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

## Expected Outcome

After completing this deployment:

- 2+ backend pods running with anti-affinity across nodes
- PodDisruptionBudget ensures at least 1 pod during maintenance
- TLS-terminated ingress on the configured domain
- NetworkPolicy restricts traffic to authorized sources
- Health checks and readiness probes active
- NATS workers processing messages across all pods
