Files
aiworker/docs/04-kubernetes/networking.md
Hector Ros db71705842 Complete documentation for future sessions
- CLAUDE.md for AI agents to understand the codebase
- GITEA-GUIDE.md centralizes all Gitea operations (API, Registry, Auth)
- DEVELOPMENT-WORKFLOW.md explains complete dev process
- ROADMAP.md, NEXT-SESSION.md for planning
- QUICK-REFERENCE.md, TROUBLESHOOTING.md for daily use
- 40+ detailed docs in /docs folder
- Backend as submodule from Gitea

Everything documented for autonomous operation.

Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
2026-01-20 00:37:19 +01:00

9.8 KiB

Networking e Ingress

Arquitectura de Red

Internet
   │
   ▼
[LoadBalancer] (Cloud Provider)
   │
   ▼
[Nginx Ingress Controller]
   │
   ├──► api.aiworker.dev ──► Backend (control-plane)
   ├──► git.aiworker.dev ──► Gitea (gitea)
   ├──► app.aiworker.dev ──► Frontend (control-plane)
   ├──► *.preview.aiworker.dev ──► Preview Deployments
   ├──► staging-*.aiworker.dev ──► Staging Envs
   └──► *.aiworker.dev ──► Production Apps

Ingress Configuration

Wildcard Certificate

# k8s/ingress/wildcard-certificate.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: wildcard-aiworker
  namespace: ingress-nginx
spec:
  secretName: wildcard-aiworker-tls
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  commonName: "*.aiworker.dev"
  dnsNames:
  - "aiworker.dev"
  - "*.aiworker.dev"
  - "*.preview.aiworker.dev"

Backend Ingress

# k8s/ingress/backend-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: backend-ingress
  namespace: control-plane
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
    nginx.ingress.kubernetes.io/websocket-services: "aiworker-backend"
    nginx.ingress.kubernetes.io/proxy-body-size: "50m"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
    nginx.ingress.kubernetes.io/cors-allow-origin: "https://app.aiworker.dev"
    nginx.ingress.kubernetes.io/cors-allow-methods: "GET, POST, PUT, PATCH, DELETE, OPTIONS"
    nginx.ingress.kubernetes.io/cors-allow-credentials: "true"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - api.aiworker.dev
    secretName: backend-tls
  rules:
  - host: api.aiworker.dev
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: aiworker-backend
            port:
              number: 3000

Frontend Ingress

# k8s/ingress/frontend-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: frontend-ingress
  namespace: control-plane
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
    nginx.ingress.kubernetes.io/configuration-snippet: |
      more_set_headers "X-Frame-Options: DENY";
      more_set_headers "X-Content-Type-Options: nosniff";
      more_set_headers "X-XSS-Protection: 1; mode=block";
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - app.aiworker.dev
    secretName: frontend-tls
  rules:
  - host: app.aiworker.dev
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: aiworker-frontend
            port:
              number: 80

Preview Deployments Ingress Template

// services/kubernetes/ingress.ts
export function generatePreviewIngress(params: {
  taskId: string
  projectName: string
  namespace: string
}) {
  const shortId = params.taskId.slice(0, 8)
  const host = `task-${shortId}.preview.aiworker.dev`

  return {
    apiVersion: 'networking.k8s.io/v1',
    kind: 'Ingress',
    metadata: {
      name: `${params.projectName}-preview`,
      namespace: params.namespace,
      annotations: {
        'cert-manager.io/cluster-issuer': 'letsencrypt-prod',
        'nginx.ingress.kubernetes.io/ssl-redirect': 'true',
        'nginx.ingress.kubernetes.io/auth-type': 'basic',
        'nginx.ingress.kubernetes.io/auth-secret': 'preview-basic-auth',
        'nginx.ingress.kubernetes.io/auth-realm': 'Preview Environment',
      },
      labels: {
        environment: 'preview',
        task: params.taskId,
        project: params.projectName,
      },
    },
    spec: {
      ingressClassName: 'nginx',
      tls: [
        {
          hosts: [host],
          secretName: `${params.projectName}-preview-tls`,
        },
      ],
      rules: [
        {
          host,
          http: {
            paths: [
              {
                path: '/',
                pathType: 'Prefix',
                backend: {
                  service: {
                    name: `${params.projectName}-preview`,
                    port: {
                      number: 80,
                    },
                  },
                },
              },
            ],
          },
        },
      ],
    },
  }
}

Service Mesh (Opcional)

Si necesitas más control sobre el tráfico, considera usar Istio o Linkerd:

Istio Gateway

apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: aiworker-gateway
  namespace: istio-system
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 443
      name: https
      protocol: HTTPS
    tls:
      mode: SIMPLE
      credentialName: wildcard-aiworker-tls
    hosts:
    - "*.aiworker.dev"
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: backend-vs
  namespace: control-plane
spec:
  hosts:
  - "api.aiworker.dev"
  gateways:
  - istio-system/aiworker-gateway
  http:
  - match:
    - uri:
        prefix: /api
    route:
    - destination:
        host: aiworker-backend
        port:
          number: 3000

DNS Configuration

Cloudflare DNS Records

# A records
api.aiworker.dev     A     <loadbalancer-ip>
git.aiworker.dev     A     <loadbalancer-ip>
app.aiworker.dev     A     <loadbalancer-ip>

# Wildcard for preview and dynamic environments
*.preview.aiworker.dev  A  <loadbalancer-ip>
*.aiworker.dev          A  <loadbalancer-ip>

External DNS (Automated)

# k8s/external-dns/external-dns-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: external-dns
  namespace: kube-system
spec:
  replicas: 1
  selector:
    matchLabels:
      app: external-dns
  template:
    metadata:
      labels:
        app: external-dns
    spec:
      serviceAccountName: external-dns
      containers:
      - name: external-dns
        image: registry.k8s.io/external-dns/external-dns:v0.14.0
        args:
        - --source=ingress
        - --domain-filter=aiworker.dev
        - --provider=cloudflare
        env:
        - name: CF_API_TOKEN
          valueFrom:
            secretKeyRef:
              name: cloudflare-api-token
              key: token

Network Policies

Isolate Preview Environments

# k8s/network-policies/preview-isolation.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: preview-isolation
  namespace: agents
spec:
  podSelector:
    matchLabels:
      environment: preview
  policyTypes:
  - Ingress
  - Egress
  ingress:
  # Allow from ingress controller
  - from:
    - namespaceSelector:
        matchLabels:
          name: ingress-nginx
  # Allow from control-plane
  - from:
    - namespaceSelector:
        matchLabels:
          name: control-plane
  egress:
  # Allow to gitea
  - to:
    - namespaceSelector:
        matchLabels:
          name: gitea
  # Allow to external HTTPS (npm, apt, etc)
  - to:
    - namespaceSelector: {}
    ports:
    - protocol: TCP
      port: 443
  # Allow DNS
  - to:
    - namespaceSelector: {}
      podSelector:
        matchLabels:
          k8s-app: kube-dns
    ports:
    - protocol: UDP
      port: 53

Allow Backend to All

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: backend-egress
  namespace: control-plane
spec:
  podSelector:
    matchLabels:
      app: aiworker-backend
  policyTypes:
  - Egress
  egress:
  - {}  # Allow all egress

Load Balancing

Session Affinity for WebSocket

apiVersion: v1
kind: Service
metadata:
  name: aiworker-backend
  namespace: control-plane
  annotations:
    service.beta.kubernetes.io/external-traffic: OnlyLocal
spec:
  selector:
    app: aiworker-backend
  ports:
  - name: http
    port: 3000
    targetPort: 3000
  sessionAffinity: ClientIP
  sessionAffinityConfig:
    clientIP:
      timeoutSeconds: 3600
  type: ClusterIP

Rate Limiting

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: backend-ingress
  namespace: control-plane
  annotations:
    nginx.ingress.kubernetes.io/rate-limit: "100"
    nginx.ingress.kubernetes.io/rate-limit-burst: "200"
    nginx.ingress.kubernetes.io/rate-limit-key: "$binary_remote_addr"
spec:
  # ... spec

Health Checks

Liveness and Readiness Probes

livenessProbe:
  httpGet:
    path: /api/health
    port: 3000
    httpHeaders:
    - name: X-Health-Check
      value: liveness
  initialDelaySeconds: 30
  periodSeconds: 10
  timeoutSeconds: 5
  failureThreshold: 3

readinessProbe:
  httpGet:
    path: /api/health/ready
    port: 3000
  initialDelaySeconds: 10
  periodSeconds: 5
  timeoutSeconds: 3
  failureThreshold: 3

Health Endpoint Implementation

// api/routes/health.ts
import { Router } from 'express'
import { getDatabase } from '../../config/database'
import { getRedis } from '../../config/redis'

const router = Router()

router.get('/health', async (req, res) => {
  res.json({
    status: 'ok',
    timestamp: new Date().toISOString(),
  })
})

router.get('/health/ready', async (req, res) => {
  try {
    // Check DB
    const db = getDatabase()
    await db.execute('SELECT 1')

    // Check Redis
    const redis = getRedis()
    await redis.ping()

    res.json({
      status: 'ready',
      services: {
        database: 'connected',
        redis: 'connected',
      },
    })
  } catch (error) {
    res.status(503).json({
      status: 'not ready',
      error: error.message,
    })
  }
})

export default router

Monitoring Traffic

# Ver logs de Nginx Ingress
kubectl logs -n ingress-nginx deployment/ingress-nginx-controller --tail=100 -f

# Ver métricas
kubectl top pods -n ingress-nginx

# Ver configuración generada
kubectl exec -n ingress-nginx <pod> -- cat /etc/nginx/nginx.conf