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>
This commit is contained in:
481
docs/04-kubernetes/namespaces.md
Normal file
481
docs/04-kubernetes/namespaces.md
Normal file
@@ -0,0 +1,481 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user