# Estructura de Namespaces ## Arquitectura de Namespaces ``` aiworker-cluster/ ├── control-plane/ # Backend, API, MCP Server ├── agents/ # Claude Code agent pods ├── gitea/ # Gitea server ├── projects/ │ └── / │ ├── 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 ```yaml 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 ```yaml 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 ```yaml 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 ```yaml 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: ```typescript // 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 ```yaml 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 ```yaml 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**: ```typescript // 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 ```yaml 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 ```yaml 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 ```yaml 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 ```typescript // 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 = {}) { 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 { try { await this.k8sApi.readNamespace(name) return true } catch { return false } } } ``` ## Dashboard de Namespaces En el frontend, mostrar todos los namespaces con sus recursos: ```typescript // 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