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:
474
docs/04-kubernetes/networking.md
Normal file
474
docs/04-kubernetes/networking.md
Normal file
@@ -0,0 +1,474 @@
|
||||
# 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
|
||||
|
||||
```yaml
|
||||
# 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
|
||||
|
||||
```yaml
|
||||
# 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
|
||||
|
||||
```yaml
|
||||
# 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
|
||||
|
||||
```typescript
|
||||
// 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
|
||||
|
||||
```yaml
|
||||
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
|
||||
|
||||
```bash
|
||||
# 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)
|
||||
|
||||
```yaml
|
||||
# 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
|
||||
|
||||
```yaml
|
||||
# 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
|
||||
|
||||
```yaml
|
||||
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
|
||||
|
||||
```yaml
|
||||
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
|
||||
|
||||
```yaml
|
||||
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
|
||||
|
||||
```yaml
|
||||
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
|
||||
|
||||
```typescript
|
||||
// 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
|
||||
|
||||
```bash
|
||||
# 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
|
||||
```
|
||||
Reference in New Issue
Block a user