Files
aiworker/docs/04-kubernetes/namespaces.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.3 KiB

Estructura de Namespaces

Arquitectura de Namespaces

aiworker-cluster/
├── control-plane/          # Backend, API, MCP Server
├── agents/                 # Claude Code agent pods
├── gitea/                  # Gitea server
├── projects/
│   └── <project-name>/
│       ├── dev/            # Desarrollo continuo
│       ├── preview-*/      # Preview deployments por tarea
│       ├── staging/        # Staging environment
│       └── production/     # Production environment
└── monitoring/             # Prometheus, Grafana

Namespace: control-plane

Propósito: Backend API, MCP Server, servicios core

apiVersion: v1
kind: Namespace
metadata:
  name: control-plane
  labels:
    name: control-plane
    environment: production
    managed-by: aiworker
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: control-plane-quota
  namespace: control-plane
spec:
  hard:
    requests.cpu: "4"
    requests.memory: 8Gi
    limits.cpu: "8"
    limits.memory: 16Gi
    persistentvolumeclaims: "5"
---
apiVersion: v1
kind: LimitRange
metadata:
  name: control-plane-limits
  namespace: control-plane
spec:
  limits:
  - max:
      cpu: "2"
      memory: 4Gi
    min:
      cpu: "100m"
      memory: 128Mi
    default:
      cpu: "500m"
      memory: 512Mi
    defaultRequest:
      cpu: "250m"
      memory: 256Mi
    type: Container

Servicios en control-plane

  • Backend API: Express + Bun
  • MCP Server: Comunicación con agentes
  • MySQL: Base de datos
  • Redis: Cache y colas
  • BullMQ Workers: Procesamiento de jobs

Namespace: agents

Propósito: Pods de Claude Code agents

apiVersion: v1
kind: Namespace
metadata:
  name: agents
  labels:
    name: agents
    environment: production
    managed-by: aiworker
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: agents-quota
  namespace: agents
spec:
  hard:
    requests.cpu: "20"
    requests.memory: 40Gi
    limits.cpu: "40"
    limits.memory: 80Gi
    pods: "50"
---
apiVersion: v1
kind: LimitRange
metadata:
  name: agents-limits
  namespace: agents
spec:
  limits:
  - max:
      cpu: "2"
      memory: 4Gi
    min:
      cpu: "500m"
      memory: 1Gi
    default:
      cpu: "1"
      memory: 2Gi
    defaultRequest:
      cpu: "500m"
      memory: 1Gi
    type: Container

Network Policy para Agents

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: agents-network-policy
  namespace: agents
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress
  ingress:
  # Permitir tráfico desde control-plane
  - from:
    - namespaceSelector:
        matchLabels:
          name: control-plane
  egress:
  # Permitir salida a control-plane (MCP Server)
  - to:
    - namespaceSelector:
        matchLabels:
          name: control-plane
  # Permitir salida a gitea
  - to:
    - namespaceSelector:
        matchLabels:
          name: gitea
  # Permitir DNS
  - to:
    - namespaceSelector: {}
      podSelector:
        matchLabels:
          k8s-app: kube-dns
    ports:
    - protocol: UDP
      port: 53
  # Permitir HTTPS externo (para Claude API)
  - to:
    - namespaceSelector: {}
    ports:
    - protocol: TCP
      port: 443

Namespace: gitea

Propósito: Servidor Git auto-alojado

apiVersion: v1
kind: Namespace
metadata:
  name: gitea
  labels:
    name: gitea
    environment: production
    managed-by: aiworker
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: gitea-quota
  namespace: gitea
spec:
  hard:
    requests.cpu: "2"
    requests.memory: 4Gi
    limits.cpu: "4"
    limits.memory: 8Gi
    persistentvolumeclaims: "2"

Namespaces por Proyecto

Estructura Dinámica

Para cada proyecto creado, se generan automáticamente 4 namespaces:

// services/kubernetes/namespaces.ts
export async function createProjectNamespaces(projectName: string) {
  const baseName = projectName.toLowerCase().replace(/[^a-z0-9-]/g, '-')

  const namespaces = [
    `${baseName}-dev`,
    `${baseName}-staging`,
    `${baseName}-production`,
  ]

  for (const ns of namespaces) {
    await k8sClient.createNamespace({
      name: ns,
      labels: {
        project: baseName,
        'managed-by': 'aiworker',
      },
    })

    // Aplicar resource quotas
    await k8sClient.applyResourceQuota(ns, {
      requests: { cpu: '2', memory: '4Gi' },
      limits: { cpu: '4', memory: '8Gi' },
    })
  }
}

Namespace: project-dev

Propósito: Desarrollo continuo, deploy automático de main/develop

apiVersion: v1
kind: Namespace
metadata:
  name: my-project-dev
  labels:
    project: my-project
    environment: dev
    managed-by: aiworker
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: dev-quota
  namespace: my-project-dev
spec:
  hard:
    requests.cpu: "1"
    requests.memory: 2Gi
    limits.cpu: "2"
    limits.memory: 4Gi
    pods: "5"

Namespace: preview-task-{id}

Propósito: Preview deployment temporal para una tarea específica

apiVersion: v1
kind: Namespace
metadata:
  name: preview-task-abc123
  labels:
    project: my-project
    environment: preview
    task-id: abc123
    managed-by: aiworker
    ttl: "168h"  # 7 days
  annotations:
    created-at: "2026-01-19T12:00:00Z"
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: preview-quota
  namespace: preview-task-abc123
spec:
  hard:
    requests.cpu: "500m"
    requests.memory: 1Gi
    limits.cpu: "1"
    limits.memory: 2Gi
    pods: "3"

Limpieza automática:

// Cleanup job que corre diariamente
export async function cleanupOldPreviewNamespaces() {
  const allNamespaces = await k8sClient.listNamespaces()

  for (const ns of allNamespaces) {
    if (ns.metadata?.labels?.environment === 'preview') {
      const createdAt = new Date(ns.metadata.annotations?.['created-at'])
      const ageHours = (Date.now() - createdAt.getTime()) / (1000 * 60 * 60)

      if (ageHours > 168) { // 7 days
        await k8sClient.deleteNamespace(ns.metadata.name)
        logger.info(`Deleted old preview namespace: ${ns.metadata.name}`)
      }
    }
  }
}

Namespace: project-staging

Propósito: Staging environment, testing antes de producción

apiVersion: v1
kind: Namespace
metadata:
  name: my-project-staging
  labels:
    project: my-project
    environment: staging
    managed-by: aiworker
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: staging-quota
  namespace: my-project-staging
spec:
  hard:
    requests.cpu: "2"
    requests.memory: 4Gi
    limits.cpu: "4"
    limits.memory: 8Gi
    pods: "10"

Namespace: project-production

Propósito: Production environment

apiVersion: v1
kind: Namespace
metadata:
  name: my-project-production
  labels:
    project: my-project
    environment: production
    managed-by: aiworker
    protected: "true"
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: production-quota
  namespace: my-project-production
spec:
  hard:
    requests.cpu: "4"
    requests.memory: 8Gi
    limits.cpu: "8"
    limits.memory: 16Gi
    pods: "20"
---
# Pod Disruption Budget para alta disponibilidad
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: app-pdb
  namespace: my-project-production
spec:
  minAvailable: 1
  selector:
    matchLabels:
      app: my-project

Namespace: monitoring

Propósito: Prometheus, Grafana, logs

apiVersion: v1
kind: Namespace
metadata:
  name: monitoring
  labels:
    name: monitoring
    environment: production
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: monitoring-quota
  namespace: monitoring
spec:
  hard:
    requests.cpu: "4"
    requests.memory: 8Gi
    limits.cpu: "8"
    limits.memory: 16Gi
    persistentvolumeclaims: "10"

Gestión de Namespaces desde el Backend

// services/kubernetes/namespaces.ts
import { KubeConfig, CoreV1Api } from '@kubernetes/client-node'

export class NamespaceManager {
  private k8sApi: CoreV1Api

  constructor() {
    const kc = new KubeConfig()
    kc.loadFromDefault()
    this.k8sApi = kc.makeApiClient(CoreV1Api)
  }

  async createNamespace(name: string, labels: Record<string, string> = {}) {
    await this.k8sApi.createNamespace({
      metadata: {
        name,
        labels: {
          'managed-by': 'aiworker',
          ...labels,
        },
      },
    })
  }

  async deleteNamespace(name: string) {
    await this.k8sApi.deleteNamespace(name)
  }

  async listNamespaces(labelSelector?: string) {
    const response = await this.k8sApi.listNamespace(undefined, undefined, undefined, undefined, labelSelector)
    return response.body.items
  }

  async namespaceExists(name: string): Promise<boolean> {
    try {
      await this.k8sApi.readNamespace(name)
      return true
    } catch {
      return false
    }
  }
}

Dashboard de Namespaces

En el frontend, mostrar todos los namespaces con sus recursos:

// hooks/useNamespaces.ts
export function useNamespaces(projectId?: string) {
  return useQuery({
    queryKey: ['namespaces', projectId],
    queryFn: async () => {
      const { data } = await api.get('/namespaces', {
        params: { projectId },
      })
      return data.namespaces
    },
  })
}

Vista en el dashboard:

  • Mapa de namespaces por proyecto
  • Uso de recursos (CPU, memoria) por namespace
  • Número de pods activos
  • Botón de cleanup para preview namespaces antiguos