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

482 lines
9.3 KiB
Markdown

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