Patrones de Kubernetes
Patrones de Kubernetes de grado de producción para desplegar, gestionar y depurar cargas de trabajo de forma fiable.
Cuándo activar
- Escribir manifiestos de Kubernetes (Deployments, Services, Ingress, Jobs)
- Configurar solicitudes/límites de recursos, probes de liveness/readiness
- Configurar RBAC, namespaces o ServiceAccounts
- Gestionar configuración y secretos en K8s
- Depurar CrashLoopBackOff, OOMKilled, pods pendientes o errores de extracción de imágenes
- Configurar HPA (Horizontal Pod Autoscaler) o PodDisruptionBudgets
- Revisar YAML de K8s por seguridad o corrección
Cuándo usar
Igual que Cuándo activar arriba. Este alias cumple con las convenciones de formato de skill del repositorio. Use este skill cada vez que escriba, revise o depure YAML y cargas de trabajo de Kubernetes.
Cómo funciona
Este skill proporciona patrones YAML de grado de producción listos para copiar y pegar y comandos de depuración de kubectl organizados por tarea:
- Plantilla de Deployment — Un
Deploymentde producción completamente configurado con contexto de seguridad, estrategia de actualización, los tres tipos de probes, límites de recursos e inyección de entorno desde ConfigMap/Secret. - Probes — Tabla de decisión para startup vs liveness vs readiness, con la matemática correcta de
failureThreshold × periodSeconds. - Services e Ingress — Patrones de ClusterIP, LoadBalancer e Ingress con TLS y anotaciones de cert-manager.
- ConfigMaps y Secrets — Guía de
envFrom, montaje de archivos y secretos externos. - Gestión de recursos — Reglas generales de requests vs limits por tipo de carga de trabajo (API web, JVM, worker, sidecar).
- RBAC — Cadena de ServiceAccount → Role → RoleBinding con mínimo privilegio.
- HPA y PDB — Configuraciones de autoescalado y seguridad ante drenaje de nodos.
- Jobs y CronJobs — Patrones de cargas de trabajo únicas y programadas con
restartPolicycorrecta. - Cheatsheet de kubectl — Comandos de logs, exec, rollback, port-forward, dry-run y diagnóstico de errores comunes.
- Antipatrones y checklist — Lo que NO se debe hacer, y un checklist de seguridad, fiabilidad y observabilidad.
Ejemplos
Vea las secciones a continuación para ejemplos completos y ejecutables. Referencias rápidas:
| Tarea | Ir a |
|---|---|
| YAML de Deployment de producción completo | Patrones de Carga de Trabajo Principales |
| Configuración de probes | Probes |
| Configuración RBAC de mínimo privilegio | RBAC |
| Depurar un CrashLoopBackOff | Cheatsheet de Depuración de kubectl |
| Autoescalado | HPA |
Patrones de Carga de Trabajo Principales
Deployment — Plantilla de Producción
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: my-namespace
labels:
app: my-app
version: "1.0.0"
spec:
replicas: 3
selector:
matchLabels:
app: my-app
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # Permitir 1 pod extra durante la actualización
maxUnavailable: 0 # Nunca reducir por debajo del número deseado
template:
metadata:
labels:
app: my-app
version: "1.0.0"
spec:
# Contexto de seguridad a nivel de pod
securityContext:
runAsNonRoot: true
runAsUser: 1001
fsGroup: 1001
# Apagado gradual
terminationGracePeriodSeconds: 30
containers:
- name: my-app
image: ghcr.io/org/my-app:1.0.0 # Nunca usar :latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
protocol: TCP
# Tanto requests como limits son obligatorios
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
# Contexto de seguridad del contenedor
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
# Probes (ver sección de Probes a continuación)
startupProbe:
httpGet:
path: /health
port: 8080
failureThreshold: 30
periodSeconds: 5
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 0
periodSeconds: 30
failureThreshold: 3
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
failureThreshold: 2
# Entorno desde ConfigMap y Secret
envFrom:
- configMapRef:
name: my-app-config
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: my-app-secrets
key: db-password
# Directorio tmp escribible cuando readOnlyRootFilesystem: true
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir: {}
Probes — Liveness, Readiness, Startup
Entender cuándo usar cada probe es crítico:
| Probe | Acción en caso de fallo | Usar para |
|---|---|---|
startupProbe | Mata el contenedor si es lento al arrancar | Apps de arranque lento (JVM, Python) |
livenessProbe | Reinicia el contenedor | Detección de bloqueos / procesos colgados |
readinessProbe | Elimina del Service endpoints | Indisponibilidad temporal (reconexión de DB) |
# Patrón correcto: startupProbe cubre el arranque lento,
# luego liveness/readiness toman el control
startupProbe:
httpGet:
path: /health
port: 8080
failureThreshold: 30 # 30 * 5s = 150s de tiempo máximo de arranque
periodSeconds: 5
livenessProbe:
httpGet:
path: /health
port: 8080
periodSeconds: 30
failureThreshold: 3 # 3 * 30s = 90s antes de reiniciar
readinessProbe:
httpGet:
path: /ready # Endpoint separado: verifica DB, caché, etc.
port: 8080
periodSeconds: 10
failureThreshold: 2
# INCORRECTO: initialDelaySeconds sin startupProbe
# Si la app tarda 60s en arrancar, use un startupProbe en su lugar
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 60 # MAL: Espera arbitraria, condición de carrera
Services e Ingress
Tipos de Service
# ClusterIP (por defecto) — solo interno
apiVersion: v1
kind: Service
metadata:
name: my-app
namespace: my-namespace
spec:
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
protocol: TCP
type: ClusterIP
# LoadBalancer — tráfico externo (proveedores cloud)
spec:
type: LoadBalancer
ports:
- port: 443
targetPort: 8080
Ingress con TLS
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
namespace: my-namespace
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- myapp.example.com
secretName: my-app-tls
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app
port:
number: 80
ConfigMaps y Secrets
ConfigMap — Configuración no sensible
apiVersion: v1
kind: ConfigMap
metadata:
name: my-app-config
namespace: my-namespace
data:
LOG_LEVEL: "info"
APP_ENV: "production"
MAX_CONNECTIONS: "100"
# Montar como archivo para configuración compleja
app.yaml: |
server:
port: 8080
timeout: 30s
# Montar ConfigMap como archivo
volumes:
- name: config
configMap:
name: my-app-config
items:
- key: app.yaml
path: app.yaml
volumeMounts:
- name: config
mountPath: /etc/app
readOnly: true
Secrets — Datos sensibles
# Crear secret desde literales (CLI, luego almacenar en Vault/SOPS)
kubectl create secret generic my-app-secrets \
--from-literal=db-password='s3cr3t' \
--namespace=my-namespace \
--dry-run=client -o yaml | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
name: my-app-secrets
namespace: my-namespace
type: Opaque
# Los valores están codificados en base64 (NO cifrados — use Sealed Secrets o ESO para cifrado real)
data:
db-password: czNjcjN0 # base64 de 's3cr3t'
Importante: Los Secrets sin procesar de Kubernetes solo están codificados en base64, no cifrados en reposo a menos que su clúster tenga cifrado configurado. Use Sealed Secrets o External Secrets Operator para producción.
Solicitudes y Límites de Recursos
resources:
requests: # El scheduler usa esto para colocar el pod
cpu: "100m" # 100 milinúcleos = 0.1 CPU
memory: "128Mi"
limits: # El contenedor se mata/acelera por encima de esto
cpu: "500m"
memory: "256Mi"
Reglas generales:
| Tipo de Carga de Trabajo | CPU Request | Memory Request | Notas |
|---|---|---|---|
| API web | 100–250m | 128–256Mi | Establecer límites 2-4x requests |
| Worker/consumer | 250–500m | 256–512Mi | Memory limit = request para previsibilidad |
| App JVM | 500m–1 | 512Mi–2Gi | Permitir espacio adicional por encima de -Xmx para overhead de JVM |
| Sidecar | 10–50m | 32–64Mi | Mantener mínimo |
# INCORRECTO: Sin requests ni limits — programación impredecible, expulsiones OOM
containers:
- name: app
image: myapp:latest
# Falta resources: {} — esto es peligroso en producción
# INCORRECTO: Limits sin requests — los requests toman el valor de los limits, sobre-reserva capacidad
resources:
limits:
cpu: "2"
memory: "1Gi"
# Falta requests — se incumplirán los valores de limits
RBAC — Roles y ServiceAccounts
Principio de Mínimo Privilegio
Dos patrones dependiendo de si la app llama a la API de Kubernetes:
Patrón A — La app NO necesita la API de Kubernetes (la mayoría de apps)
Deshabilitar el montaje automático del token en el ServiceAccount. La Role/RoleBinding no son necesarias.
# ServiceAccount con token deshabilitado — opción más segura por defecto
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app-sa
namespace: my-namespace
automountServiceAccountToken: false # Sin token de API de K8s inyectado en pods
# Referencia en Deployment — sin token, sin acceso a la API
spec:
template:
spec:
serviceAccountName: my-app-sa
automountServiceAccountToken: false # Doble seguridad: también se establece a nivel de pod
Patrón B — La app SÍ necesita la API de Kubernetes (operadores, controladores, observadores de configuración)
Habilitar el token y conceder solo los permisos realmente necesarios.
# 1. ServiceAccount — habilitar token para esta SA
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app-sa
namespace: my-namespace
automountServiceAccountToken: true # Token requerido: la app llama a la API de K8s
# 2. Role — conceder solo lo que la app necesita (ámbito de namespace)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: my-app-role
namespace: my-namespace
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "watch"] # Solo lectura, recurso específico
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["my-app-secrets"] # Restringir a un secreto específico por nombre
verbs: ["get"]
# 3. Enlazar Role al ServiceAccount
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: my-app-rolebinding
namespace: my-namespace
subjects:
- kind: ServiceAccount
name: my-app-sa
namespace: my-namespace
roleRef:
kind: Role
apiGroup: rbac.authorization.k8s.io
name: my-app-role
# 4. Referenciar SA en Deployment
spec:
template:
spec:
serviceAccountName: my-app-sa
# automountServiceAccountToken por defecto es true desde SA — el token se inyecta
Horizontal Pod Autoscaler (HPA)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: my-app-hpa
namespace: my-namespace
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
minReplicas: 2 # Siempre al menos 2 para alta disponibilidad
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # Escalar cuando CPU media > 70%
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
HPA requiere que
resources.requestsesté establecido en todos los contenedores — calcula la utilización comoactual / request.
PodDisruptionBudget (PDB)
Evitar que demasiados pods se caigan durante drenajes de nodos o actualizaciones:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: my-app-pdb
namespace: my-namespace
spec:
minAvailable: 2 # O use maxUnavailable: 1
selector:
matchLabels:
app: my-app
Namespaces y Multitenencia
# Crear namespace con cuotas de recursos
kubectl create namespace my-namespace
# Aplicar ResourceQuota para limitar el consumo del namespace
kubectl apply -f - <<EOF
apiVersion: v1
kind: ResourceQuota
metadata:
name: my-namespace-quota
namespace: my-namespace
spec:
hard:
requests.cpu: "4"
requests.memory: 4Gi
limits.cpu: "8"
limits.memory: 8Gi
pods: "20"
EOF
Jobs y CronJobs
# Job único (migración de DB, procesamiento de datos)
apiVersion: batch/v1
kind: Job
metadata:
name: db-migrate
namespace: my-namespace
spec:
backoffLimit: 3 # Reintentar hasta 3 veces en caso de fallo
ttlSecondsAfterFinished: 3600 # Auto-eliminar después de 1h
template:
spec:
restartPolicy: OnFailure # Never for Jobs (not Always)
containers:
- name: migrate
image: ghcr.io/org/my-app:1.0.0
command: ["python", "manage.py", "migrate"]
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
# CronJob — ejecución programada (copia de seguridad nocturna, limpieza)
apiVersion: batch/v1
kind: CronJob
metadata:
name: nightly-cleanup
namespace: my-namespace
spec:
schedule: "0 2 * * *" # Diariamente a las 2:00 AM
concurrencyPolicy: Forbid # No ejecutar si el anterior aún se está ejecutando
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: cleanup
image: ghcr.io/org/cleanup:1.0.0
command: ["/bin/cleanup.sh"]
Cheatsheet de Depuración de kubectl
Registros y Ejecución
# Seguir logs en tiempo real
kubectl logs -f deployment/my-app -n my-namespace
# Versión anterior del pod (útil si se reinició)
kubectl logs -f deployment/my-app -n my-namespace --previous
# Todos los contenedores en un pod
kubectl logs -f pod/my-app-xxx -n my-namespace --all-containers
# Shell interactiva
kubectl exec -it pod/my-app-xxx -n my-namespace -- /bin/sh
# Shell en un contenedor específico
kubectl exec -it pod/my-app-xxx -c sidecar -n my-namespace -- /bin/sh
Diagnóstico de Errores
# Finalizar un pod atascado
kubectl delete pod <pod-name> -n my-namespace --grace-period=0 --force
# Obtener eventos del namespace (primera parada para depurar)
kubectl get events -n my-namespace --sort-by='.lastTimestamp'
# Ver logs del kubelet
# SSH al nodo y ejecutar:
journalctl -u kubelet -f
# Inspeccionar YAML en ejecución (útil para ver el estado actual)
kubectl get pod my-app-xxx -n my-namespace -o yaml
# Describir el pod (resume eventos, condiciones, contenedores)
kubectl describe pod my-app-xxx -n my-namespace
# Revisar estado de NodePort (todas las IPs/hechos de los nodos)
kubectl get endpoints -n my-namespace
Rollback y Cambios
# Retroceder Deployment a la revisión anterior
kubectl rollout undo deployment/my-app -n my-namespace
# Ver historial de revisiones
kubectl rollout history deployment/my-app -n my-namespace
# Retroceder a una revisión específica
kubectl rollout undo deployment/my-app --to-revision=2 -n my-namespace
Port-Forwarding y Depuración de Red
# Reenviar puerto local al pod
kubectl port-forward pod/my-app-xxx 8080:8080 -n my-namespace
# Reenviar a un Service (nombre del Service, no del Deployment)
kubectl port-forward service/my-app 8080:80 -n my-namespace
Antipatrones y Checklist
Antipatrones (Lo que NO se debe hacer)
- Imágenes
:latest— Nunca. Use tags semánticas o hashes de commit. - Sin probes — Sin probes significa sin recuperación automática.
- initialDelaySeconds en lugar de startupProbe — Causa condiciones de carrera en apps de arranque lento.
- Sin requests/limits — Lleva a programación impredecible y expulsiones OOM.
- Secrets no cifrados en reposo — Los Secrets de Kubernetes no están cifrados por defecto.
- Contenedor root — Siempre configurar
runAsNonRoot: truey descartar capacidades. - RBAC demasiado permisivo — Otorgar
*a roles de namespace o clúster cuando no es necesario. - Sin PDB — Las operaciones de drenaje de nodos pueden dejar caer réplicas críticas.
Checklist de Revisión de Producción
Seguridad:
-
runAsNonRoot: true(pod y contenedor) -
readOnlyRootFilesystem: true(a menos que se necesite escritura) -
capabilities.drop: ["ALL"] -
allowPrivilegeEscalation: false - ServiceAccount con
automountServiceAccountToken: false(a menos que la app necesite la API de K8s) - Secrets: cifrados en reposo (Sealed Secrets / ESO)
- Imagen: tag semántico, no
:latest - Anotaciones de Ingress para HTTPS y certificados gestionados por cert-manager
Fiabilidad:
- Liveness probe configurada
- Readiness probe configurada (endpoint de readiness separado)
-
failureThreshold × periodSecondscalculados para el comportamiento esperado de la app - Límites de recursos establecidos (no solo requests)
-
maxSurgeymaxUnavailablerazonables para la actualización -
terminationGracePeriodSecondsestablecido (cubrir la señal SIGTERM y la limpieza) - PodDisruptionBudget configurado
- HPA configurado (si aplica) con CPU y memoria
- Múltiples réplicas (>= 2) para HA
Observabilidad:
- Endpoint de health (liveness) expuesto
- Endpoint de readiness expuesto (verifica dependencias: DB, caché, etc.)
- Métricas expuestas (Prometheus / OpenTelemetry)
- Logs estructurados (JSON), nivel configurable
- Logs de solicitudes con ID de traza
Limitaciones y Compensaciones
| Limitación | Detalle |
|---|---|
| Cobertura de proveedor cloud | Los patrones de LoadBalancer asumen anotaciones estándar; es posible que sean necesarios ajustes para EKS, AKS, GKE para servicios específicos como NLBs o ILBs. |
| CertManager | Las anotaciones de TLS asumen que cert-manager está instalado en el clúster; no es un componente nativo de K8s. |
| Límites de recursos | Los valores en resources son puntos de partida; la puesta a punto requiere supervisión real de la carga de trabajo. |
| RBAC | Los ejemplos son para roles de namespace; los roles de clúster (ClusterRole, ClusterRoleBinding) siguen un patrón similar pero no están incluidos. |
| Zonas de disponibilidad | El topología spread constraints no se tratan; consulte la documentación de K8s para configuraciones multi-AZ. |
| Helm / GitOps | Este skill se basa en YAML; considere helm charts, kustomize o GitOps flujos (ArgoCD, Flux) para la gestión del ciclo de vida. |
| Redis / Kafka | Los patrones StatefulSet (Redis, Kafka) no están incluidos; este skill se centra en cargas de trabajo stateless. |
Documentación Relacionada
- Kubernetes Documentation — Configure Liveness, Readiness and Startup Probes
- Kubernetes — Resource Management for Pods and Containers
- Kubernetes — PodDisruptionBudget
- Kubernetes — Horizontal Pod Autoscaling
- cert-manager Documentation
- External Secrets Operator
- Sealed Secrets
- Kubernetes — RBAC Authorization