Cheatsheet Kubernetes
Orquestração de contentores para deploy, escala e gestão automatizada
Kubernetes
Cluster e Contexto
Info del clúster
kubectl cluster-info kubectl cluster-info dump kubectl get nodes kubectl get nodes -o wide kubectl top nodes kubectl version --short
cluster-info muestra la dirección del API server y los servicios. get nodes lista los nodos del clúster con su estado (Ready, NotReady). top nodes muestra el uso de CPU/memoria (requiere metrics-server).
Estado de los nodes
kubectl get nodes -o wide kubectl describe node worker-1 kubectl get nodes --show-labels kubectl get nodes -l kubernetes.io/os=linux kubectl cordon worker-1 kubectl uncordon worker-1
describe node muestra capacidad, condiciones y pods programados. cordon marca el nodo como no disponible para nuevos pods (mantenimiento). uncordon lo revierte. Labels como kubernetes.io/os permiten filtrar nodos.
Labels y Selectors
kubectl get pods -l app=web kubectl get pods -l 'env in (prod,staging)' kubectl get pods -l app=web,tier=frontend kubectl label pod my-pod tier=frontend kubectl label pod my-pod tier- kubectl get pods --show-labels
Los Labels son pares clave/valor que identifican recursos. -l filtra por selector. label pod tier=frontend añade; tier- elimina. --show-labels muestra todas las labels de cada recurso.
Namespaces
kubectl get namespaces kubectl get ns kubectl create namespace dev kubectl create namespace staging kubectl get pods -n dev kubectl get pods --all-namespaces kubectl get pods -A
Los Namespaces aíslan recursos lógicamente dentro del mismo clúster. -n o --namespace especifica el namespace objetivo. -A o --all-namespaces lista los recursos de todos los namespaces.
Drenar un node
kubectl drain worker-1 \ --ignore-daemonsets \ --delete-emptydir-data kubectl drain worker-1 --force kubectl uncordon worker-1
drain evacúa un nodo: termina los pods gracefully e impide nuevas programaciones. --ignore-daemonsets es necesario porque los DaemonSets no pueden evacuarse. Tras el mantenimiento, uncordon reactiva el nodo.
Annotations
kubectl annotate pod my-pod \
description="Pod de prueba"
kubectl annotate pod my-pod description-
kubectl get pod my-pod -o jsonpath=\
'{.metadata.annotations}'Las Annotations guardan metadatos no identificadores (descripciones, URLs, contactos). A diferencia de las labels, no se usan para selección. annotate clave- elimina la annotation.
Contextos y kubeconfig
kubectl config get-contexts kubectl config current-context kubectl config use-context prod-cluster kubectl config set-context --current \ --namespace=dev kubectl config view
Un context combina clúster + usuario + namespace. use-context cambia de clúster/entorno. set-context --current --namespace define el namespace por defecto del contexto actual. El archivo ~/.kube/config lo guarda todo.
Componentes del sistema
kubectl get componentstatuses kubectl get pods -n kube-system kubectl get pods -n kube-system -l \ k8s-app=kube-dns kubectl logs -n kube-system \ -l k8s-app=kube-dns
El namespace kube-system contiene los componentes core: API server, etcd, scheduler, controller-manager y CoreDNS. Verificar estos pods es el primer paso al diagnosticar problemas del clúster.
Eliminar recursos
kubectl delete pod my-pod kubectl delete pods --all -n dev kubectl delete deployment web kubectl delete -f manifest.yaml kubectl delete pods -l app=legacy kubectl delete pod my-pod \ --grace-period=0 --force
delete elimina recursos por nombre, label (-l) o archivo (-f). --grace-period=0 --force fuerza la eliminación inmediata (útil para pods atascados en Terminating). Cuidado con --all.
Explorar la API
kubectl api-resources kubectl api-resources --namespaced=true kubectl api-versions kubectl explain pod kubectl explain pod.spec.containers kubectl explain deployment.spec --recursive
api-resources lista todos los tipos de recurso disponibles con sus shortnames. explain muestra la documentación de un campo directamente en el terminal — útil para explorar la estructura de cualquier objeto sin salir del CLI.
Output formateado
kubectl get pods -o wide
kubectl get pods -o yaml
kubectl get pods -o json
kubectl get pod my-pod -o jsonpath=\
'{.status.podIP}'
kubectl get pods -o custom-columns=\
NAME:.metadata.name,STATUS:.status.phase-o controla el formato de salida: wide (más columnas), yaml, json. jsonpath extrae campos específicos. custom-columns crea tablas personalizadas con los campos que quieras.
Dry-run y generar YAML
kubectl run nginx --image=nginx \ --dry-run=client -o yaml > pod.yaml kubectl create deployment web \ --image=nginx --replicas=3 \ --dry-run=client -o yaml > deploy.yaml kubectl apply -f pod.yaml --dry-run=server
--dry-run=client simula sin crear y -o yaml genera el manifiesto — ideal para crear plantillas. --dry-run=server valida contra el API server sin persistir. Ahorra tiempo y evita errores de sintaxis YAML.
Pods
Listar pods
kubectl get pods kubectl get pods -o wide kubectl get pods -A kubectl get pods -w kubectl get pods --field-selector \ status.phase=Running kubectl get pods --sort-by=.status.startTime
get pods lista los pods del namespace actual. -w (watch) actualiza en tiempo real. --field-selector filtra por campos como status.phase. -o wide añade la IP y el nodo de cada pod.
Ejecutar comandos
kubectl exec -it my-pod -- sh kubectl exec -it my-pod -- bash kubectl exec my-pod -- ls /app kubectl exec my-pod -- cat /etc/config/app.yaml kubectl exec -it my-pod -c sidecar -- sh
exec ejecuta comandos dentro de un contenedor en ejecución. -it da un terminal interactivo. Todo lo que va tras -- es el comando. -c selecciona el contenedor en pods con varios. Esencial para inspeccionar el interior de los pods.
Pods multi-container
spec:
containers:
- name: app
image: myapp:1.0
- name: sidecar
image: fluentd:latest
initContainers:
- name: init-db
image: busybox
command: ['sh', '-c', 'sleep 5']Un pod puede tener varios contenedores que comparten red y volúmenes. Los initContainers se ejecutan secuencialmente antes de los principales (setup, migraciones). Patrones comunes: sidecar (logging, proxy) y ambassador (proxy de red).
Crear pod imperativo
kubectl run nginx --image=nginx kubectl run app --image=myapp:1.0 \ --port=8080 kubectl run test --image=busybox \ --rm -it -- sh kubectl run debug --image=nicolaka/netshoot \ --rm -it -- bash
run crea un pod directamente. --rm -it crea un pod efímero con terminal interactivo que se elimina al salir — perfecto para debug. --port expone el puerto del contenedor (no crea un Service).
Port-forward
kubectl port-forward my-pod 8080:80 kubectl port-forward svc/web 8080:80 kubectl port-forward deployment/web 8080:80 kubectl port-forward my-pod 8080:80 \ --address=0.0.0.0
port-forward crea un túnel local hacia un pod, Service o Deployment. 8080:80 mapea el puerto local 8080 al puerto 80 del pod. Ideal para probar sin crear un Service. --address=0.0.0.0 permite acceso externo.
Probes (health checks)
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
readinessProbe:
httpGet:
path: /ready
port: 8080livenessProbe reinicia el contenedor si falla (app colgada). readinessProbe quita el pod del Service si no está listo (aún arrancando). Tipos: httpGet, tcpSocket, exec. initialDelaySeconds da tiempo al arranque.
Crear pod declarativo
apiVersion: v1
kind: Pod
metadata:
name: my-pod
labels:
app: web
spec:
containers:
- name: app
image: nginx:1.25
ports:
- containerPort: 80Un Pod es la unidad programable más pequeña. spec.containers define los contenedores (puede haber varios). containerPort es informativo — la exposición real se hace vía Service. Aplica con kubectl apply -f pod.yaml.
Describir un pod
kubectl describe pod my-pod kubectl get pod my-pod -o yaml kubectl get events --field-selector \ involvedObject.name=my-pod
describe muestra detalles completos: spec, estado, condiciones y Events al final. Los eventos revelan errores de programación, pull de imagen o probes fallidos. Es el primer comando para diagnosticar un pod con problemas.
Copiar archivos
kubectl cp my-pod:/app/log.txt ./log.txt kubectl cp ./config.yaml my-pod:/etc/config/ kubectl cp my-pod:/data ./backup \ -c sidecar
cp copia archivos entre el sistema local y un pod (como docker cp). Formato: pod:/ruta para remoto. -c especifica el contenedor. Útil para extraer logs, configs o inyectar archivos de debug.
Logs
kubectl logs my-pod kubectl logs -f my-pod kubectl logs my-pod --tail=100 kubectl logs my-pod --since=1h kubectl logs my-pod -c sidecar kubectl logs my-pod --previous
logs muestra la salida del contenedor. -f sigue en tiempo real (como tail -f). -c especifica el contenedor en pods multi-container. --previous muestra los logs del contenedor anterior (tras crash/restart).
Estados del pod
Pending # espera programación/pull Running # en ejecución Succeeded # terminó con éxito Failed # terminó con error Unknown # estado indeterminado CrashLoopBackOff # reinicia en bucle ImagePullBackOff # fallo al descargar la imagen
Pending indica falta de recursos o imagen descargándose. CrashLoopBackOff significa que el contenedor termina repetidamente. ImagePullBackOff indica error de imagen (nombre erróneo, registry privado, sin credenciales).
Eliminar pods
kubectl delete pod my-pod kubectl delete pods --all kubectl delete pods -l app=legacy kubectl delete pod my-pod \ --grace-period=0 --force kubectl delete pod my-pod --wait=false
Los pods gestionados por Deployments se recrean automáticamente al eliminarlos. --grace-period=0 --force fuerza la eliminación inmediata (pods atascados). --wait=false no espera la terminación. Para pararlo del todo, elimina el Deployment.
Deployments e Rollouts
Crear un deployment
kubectl create deployment web \ --image=nginx --replicas=3 kubectl apply -f deployment.yaml kubectl get deployments kubectl get rs
Un Deployment gestiona ReplicaSets que mantienen el número deseado de pods. create deployment es imperativo; apply -f es declarativo (preferido). get rs muestra los ReplicaSets creados.
Pausar y reanudar un rollout
kubectl rollout pause deployment/web kubectl set image deployment/web \ nginx=nginx:1.26 kubectl set resources deployment/web \ --limits=cpu=500m,memory=256Mi kubectl rollout resume deployment/web
pause congela el rollout para aplicar múltiples cambios de una vez. Al hacer resume, Kubernetes realiza un único rollout con todos los cambios — evita múltiples rollouts intermedios y reduce el downtime.
Restart de un deployment
kubectl rollout restart deployment/web kubectl rollout status deployment/web
rollout restart reinicia todos los pods de forma controlada (rolling update) sin cambiar la imagen. Útil tras actualizar un ConfigMap o Secret montado, o para limpiar el estado en memoria de los contenedores.
Manifiesto de Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.25spec.selector.matchLabels debe corresponder a las template.metadata.labels — el Deployment lo usa para saber qué pods gestionar. template es la plantilla de pod que el ReplicaSet replica.
Estrategia RollingUpdate
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
spec:
strategy:
type: RecreateRollingUpdate sustituye pods gradualmente (por defecto). maxSurge = pods extra durante el update; maxUnavailable: 0 garantiza zero-downtime. Recreate termina todos antes de crear nuevos (hay downtime).
Revision history limit
spec: revisionHistoryLimit: 5 progressDeadlineSeconds: 300
revisionHistoryLimit controla cuántas revisiones antiguas guardar (por defecto: 10). progressDeadlineSeconds define el timeout para considerar fallido el rollout si no progresa. Ajústalo según la velocidad de tus deploys.
Actualizar la imagen
kubectl set image deployment/web \ nginx=nginx:1.26 kubectl apply -f deployment.yaml kubectl rollout status deployment/web kubectl rollout status deployment/web \ --timeout=60s
set image actualiza la imagen de un contenedor sin editar YAML. El formato es contenedor=nueva-imagen. rollout status sigue el progreso de la actualización hasta que todos los pods estén listos.
ReplicaSet
kubectl get rs kubectl get rs -l app=web kubectl describe rs web-abc123 kubectl scale rs web-abc123 --replicas=5 kubectl delete rs web-abc123
El ReplicaSet garantiza que N pods con las labels correctas estén siempre en ejecución. Lo crea automáticamente el Deployment. Rara vez se crea directamente — usa siempre Deployment para tener rollouts e historial.
MinReadySeconds
spec:
minReadySeconds: 10
template:
spec:
containers:
- name: app
image: myapp:1.0minReadySeconds define cuánto tiempo debe estar un pod en Ready antes de considerarse disponible. Evita que el rollout avance demasiado rápido si la app tarda en estabilizarse tras pasar el readiness probe.
Rollback
kubectl rollout history deployment/web kubectl rollout history deployment/web \ --revision=2 kubectl rollout undo deployment/web kubectl rollout undo deployment/web \ --to-revision=2
rollout undo revierte a la revisión anterior. --to-revision=N revierte a una revisión específica. rollout history lista las revisiones. Por defecto, Kubernetes guarda 10 revisiones (revisionHistoryLimit).
Escalar un deployment
kubectl scale deployment web --replicas=5
kubectl scale deployment web --replicas=0
kubectl get deployment web -o jsonpath=\
'{.spec.replicas}'scale ajusta el número de réplicas inmediatamente. --replicas=0 pausa la app sin eliminar el Deployment (todos los pods terminan). Útil para entornos de staging que no necesitan estar siempre activos.
Inspeccionar un deployment
kubectl get deployment web -o yaml
kubectl describe deployment web
kubectl get deployment web -o jsonpath=\
'{.status.readyReplicas}'
kubectl get deployment web -o jsonpath=\
'{.status.conditions[*].type}'describe muestra réplicas, condiciones y eventos. status.readyReplicas indica cuántos pods están listos. conditions revela Available, Progressing y ReplicaFailure para un diagnóstico rápido.
Services e Networking
Tipos de Service
ClusterIP # interno (por defecto) NodePort # puerto en cada node LoadBalancer # LB externo (cloud) ExternalName # CNAME DNS kubectl get svc kubectl get svc -o wide
ClusterIP expone solo dentro del clúster (por defecto). NodePort abre un puerto (30000-32767) en todos los nodos. LoadBalancer aprovisiona un balanceador externo (AWS, GCP, Azure). ExternalName hace CNAME a un DNS externo.
DNS interno
# Formato: # <svc>.<namespace>.svc.cluster.local curl http://web.default.svc.cluster.local nslookup web.default # Mismo namespace: curl http://web
Cada Service recibe un registro DNS: <nombre>.<namespace>.svc.cluster.local. Dentro del mismo namespace, basta el nombre (http://web). El CoreDNS en kube-system resuelve estos nombres automáticamente.
Service sin selector
apiVersion: v1 kind: Service metadata: name: external-db spec: ports: - port: 5432 --- apiVersion: v1 kind: Endpoints metadata: name: external-db subsets: - addresses: - ip: 10.0.0.50 ports: - port: 5432
Un Service sin selector no crea Endpoints automáticamente. Puedes crear Endpoints manuales apuntando a IPs externas — útil para bases de datos fuera del clúster, manteniendo el mismo patrón de DNS interno.
Crear Service imperativo
kubectl expose deployment web \ --port=80 --target-port=8080 kubectl expose deployment web \ --port=80 --type=NodePort kubectl expose pod my-pod --port=8080
expose crea un Service a partir de un Deployment o Pod. --port es el puerto del Service; --target-port es el puerto del contenedor. El selector se hereda automáticamente de las labels del recurso expuesto.
Headless Service
spec:
clusterIP: None
selector:
app: web
ports:
- port: 80Un Headless Service (clusterIP: None) no tiene IP virtual. El DNS devuelve directamente las IPs de los pods. Se usa con StatefulSets para dar DNS estable a cada pod (pod-0.svc.ns) o para service discovery manual.
Session affinity
spec:
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 3600sessionAffinity: ClientIP enruta las peticiones del mismo IP siempre al mismo pod (sticky sessions). El valor por defecto es None (round-robin). Útil para apps con estado en memoria, pero prefiere stateless + caché externa.
Manifiesto de Service
apiVersion: v1
kind: Service
metadata:
name: web
spec:
selector:
app: web
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: ClusterIPEl selector del Service debe corresponder a las labels de los pods objetivo. port es el puerto del Service; targetPort es el puerto del contenedor. El Service distribuye el tráfico entre los pods que coinciden con el selector (load balancing interno).
Endpoints
kubectl get endpoints web kubectl describe endpoints web kubectl get endpointslice
Endpoints lista las IPs de los pods a los que el Service está enrutando. Si está vacío, el selector no coincide con ningún pod o los pods no están Ready. EndpointSlice es la versión escalable para clústeres grandes.
Inspeccionar Services
kubectl get svc -o wide
kubectl describe svc web
kubectl get svc web -o jsonpath=\
'{.spec.clusterIP}'
kubectl get svc -Adescribe svc muestra selector, puertos, endpoints y tipo. clusterIP es la IP virtual interna. Si los Endpoints están vacíos, verifica que las labels de los pods correspondan al selector del Service.
NodePort
spec:
type: NodePort
ports:
- port: 80
targetPort: 8080
nodePort: 30080
kubectl get svc web -o jsonpath=\
'{.spec.ports[0].nodePort}'NodePort expone el Service en un puerto fijo (30000-32767) de todos los nodos. Accesible vía IP-del-node:nodePort. Si no se especifica nodePort, Kubernetes lo asigna automáticamente. Bueno para dev/pruebas.
Multi-port Service
spec:
ports:
- name: http
port: 80
targetPort: 8080
- name: metrics
port: 9090
targetPort: 9090Un Service puede exponer múltiples puertos. Cada puerto necesita un name cuando hay más de uno. Útil para exponer la app (80) y las métricas (9090) en el mismo Service. El Ingress lo referencia por el nombre del puerto.
NetworkPolicy básica
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-web
spec:
podSelector:
matchLabels:
app: web
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- port: 8080NetworkPolicy controla el tráfico entre pods (firewall L3/L4). podSelector define los pods afectados. ingress.from especifica quién puede comunicarse. Requiere un CNI que soporte NetworkPolicy (Calico, Cilium).
ConfigMaps e Secrets
Crear un ConfigMap
kubectl create configmap app-config \ --from-file=config.yaml kubectl create configmap vars \ --from-literal=ENV=prod \ --from-literal=DEBUG=false kubectl get configmaps
ConfigMap almacena configuración no sensible en pares clave/valor. --from-file carga archivos; --from-literal define valores inline. Cada clave queda disponible como variable de entorno o archivo montado.
Crear un Secret
kubectl create secret generic db-pass \ --from-literal=password=s3cret kubectl create secret docker-registry \ regcred --docker-server=registry.io \ --docker-user=admin --docker-password=pass kubectl get secrets
Secret guarda datos sensibles (passwords, tokens, claves). Los valores se codifican en base64 (no cifrados por defecto). docker-registry crea credenciales para registries privados. Activa el cifrado del etcd en producción.
ImagePullSecrets
spec:
imagePullSecrets:
- name: regcred
containers:
- name: app
image: registry.io/myapp:1.0imagePullSecrets proporciona credenciales para descargar imágenes de registries privados. Créalo con kubectl create secret docker-registry. Puedes configurarlo en el ServiceAccount para aplicarlo a todos los pods del namespace.
Manifiesto de ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
ENV: "production"
LOG_LEVEL: "info"
app.properties: |
server.port=8080
server.host=0.0.0.0data acepta pares clave/valor simples o archivos completos (con | para multilínea). Las claves con punto (p. ej. app.properties) se tratan como nombres de archivo cuando se montan como volumen.
Manifiesto de Secret
apiVersion: v1 kind: Secret metadata: name: db-creds type: Opaque stringData: username: admin password: s3cret # o con base64: data: password: czNjcmV0
stringData acepta valores en texto plano (Kubernetes los codifica). data exige valores ya en base64. type: Opaque es el por defecto; tipos especiales: kubernetes.io/tls, kubernetes.io/dockerconfigjson.
Editar ConfigMap/Secret
kubectl edit configmap app-config kubectl edit secret db-creds kubectl create configmap vars \ --from-literal=ENV=staging \ --dry-run=client -o yaml | kubectl apply -f -
edit abre el editor para modificar directamente. El patrón --dry-run=client -o yaml | kubectl apply -f - recrea el recurso con nuevos valores (útil en CI/CD). Los volúmenes montados se actualizan; las env vars requieren reiniciar el pod.
ConfigMap como env
env:
- name: ENV
valueFrom:
configMapKeyRef:
name: app-config
key: ENV
envFrom:
- configMapRef:
name: app-configconfigMapKeyRef inyecta una clave específica como variable. envFrom.configMapRef inyecta todas las claves del ConfigMap como variables de entorno (nombre de la clave = nombre de la variable). Más simple para configs con muchas claves.
Secret como env
env:
- name: DB_PASS
valueFrom:
secretKeyRef:
name: db-creds
key: password
envFrom:
- secretRef:
name: db-credssecretKeyRef inyecta una clave del Secret como variable. envFrom.secretRef inyecta todas las claves. El valor se decodifica automáticamente. La app ve texto plano — el base64 es solo transporte/almacenamiento.
ConfigMap vs Secret
# ConfigMap: datos no sensibles # - configs, URLs, flags # - texto plano en etcd # Secret: datos sensibles # - passwords, tokens, claves # - base64 en etcd (activa el cifrado) kubectl get cm app-config -o yaml kubectl get secret db-creds -o yaml
Usa ConfigMap para configuración no sensible y Secret para credenciales. Ambos tienen un límite de 1 MiB. Los Secrets son base64 (no cifrados por defecto) — activa EncryptionConfiguration en etcd en producción.
ConfigMap como volumen
volumes:
- name: config-vol
configMap:
name: app-config
volumeMounts:
- name: config-vol
mountPath: /etc/config
readOnly: trueMontar un ConfigMap como volumen crea un archivo por clave en mountPath. Las actualizaciones del ConfigMap se propagan automáticamente (con retraso). readOnly: true evita escrituras accidentales.
Secret como volumen
volumes:
- name: secret-vol
secret:
secretName: db-creds
defaultMode: 0400
volumeMounts:
- name: secret-vol
mountPath: /etc/secrets
readOnly: trueMontar un Secret como volumen crea archivos con permisos restringidos. defaultMode: 0400 garantiza lectura solo por el owner. Las actualizaciones se propagan automáticamente. Prefiere volúmenes a env vars para secrets grandes.
ConfigMap/Secret immutable
apiVersion: v1 kind: ConfigMap metadata: name: app-config immutable: true data: ENV: "production"
immutable: true impide cambios tras la creación (mejora el rendimiento del clúster — el kubelet no hace watch). Para actualizar, elimina y recrea. Ideal para configs versionadas que cambian mediante un nuevo Deployment.
Storage e Volumes
PersistentVolumeClaim
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: standardUn PVC solicita almacenamiento al clúster. accessModes: ReadWriteOnce (1 nodo), ReadOnlyMany (varios nodos, lectura), ReadWriteMany (varios nodos, escritura). El StorageClass define el aprovisionador.
Ver volúmenes y clases
kubectl get pv kubectl get pvc kubectl get storageclass kubectl get sc kubectl describe pvc data-pvc kubectl get pvc -o wide
get pv muestra los volúmenes del clúster (globales). get pvc muestra los claims por namespace. Estados: Bound (enlazado), Pending (en espera). describe pvc revela eventos de aprovisionamiento y errores de bind.
Expandir un volumen
kubectl edit pvc data-pvc # Cambiar: storage: 10Gi -> 20Gi kubectl get pvc data-pvc
La expansión requiere allowVolumeExpansion: true en el StorageClass. Edita el PVC para aumentar el tamaño. Algunos aprovisionadores expanden online; otros requieren reiniciar el pod. Reducir no está soportado.
Usar un PVC en un Pod
volumes:
- name: data
persistentVolumeClaim:
claimName: data-pvc
containers:
- name: app
volumeMounts:
- name: data
mountPath: /var/dataEl pod referencia el PVC por nombre en volumes y lo monta en mountPath. Los datos persisten aunque el pod se elimine o se recree. El PVC debe estar en el mismo namespace que el pod.
emptyDir
volumes:
- name: cache
emptyDir: {}
- name: shared
emptyDir:
medium: Memory
sizeLimit: 256MiemptyDir crea un volumen temporal que vive mientras exista el pod. Ideal para caché compartida entre contenedores del mismo pod. medium: Memory usa RAM (tmpfs) para rendimiento. Los datos se pierden al eliminar el pod.
Volume subPath
volumeMounts: - name: config-vol mountPath: /etc/app/config.yaml subPath: config.yaml - name: data mountPath: /data/user1 subPath: user1
subPath monta solo un archivo/subdirectorio del volumen en vez del volumen entero. Útil para montar un único archivo de un ConfigMap sin sustituir todo el directorio. No se actualiza automáticamente con ConfigMaps.
PersistentVolume
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs
spec:
capacity:
storage: 50Gi
accessModes:
- ReadWriteMany
nfs:
server: 10.0.0.10
path: /exports/dataUn PV es el recurso de almacenamiento real (NFS, iSCSI, disco cloud). Puede aprovisionarse estáticamente (lo crea el admin) o dinámicamente (vía StorageClass). El PVC se enlaza a un PV compatible automáticamente.
hostPath
volumes:
- name: logs
hostPath:
path: /var/log/app
type: DirectoryOrCreatehostPath monta un directorio del nodo en el pod. El dato persiste en el nodo pero no migra con el pod. type: DirectoryOrCreate lo crea si no existe. Úsalo con cautela — acopla el pod a un nodo específico (evítalo en producción).
StorageClass
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: fast-ssd provisioner: kubernetes.io/gce-pd parameters: type: pd-ssd reclaimPolicy: Delete volumeBindingMode: WaitForFirstConsumer
StorageClass permite el aprovisionamiento dinámico: al crear un PVC, el PV se crea automáticamente. provisioner define el plugin (cloud, NFS, local). WaitForFirstConsumer retrasa la creación hasta que un pod use el PVC.
Reclaim policies
# Retain: el PV mantiene los datos tras eliminar el PVC
# Delete: el PV y los datos se eliminan
# Recycle: datos eliminados, PV reutilizable
kubectl patch pv pv-nfs -p \
'{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}'reclaimPolicy define qué pasa con el PV cuando se elimina el PVC. Retain preserva los datos (seguro). Delete lo elimina todo (por defecto en cloud). Recycle está deprecated. Configúralo en el StorageClass o el PV.
Escala e Recursos
Escalar manualmente
kubectl scale deployment web --replicas=5 kubectl scale deployment web --replicas=0 kubectl get deployment web kubectl get pods -l app=web
scale ajusta las réplicas inmediatamente. El ReplicaSet crea o termina pods para alcanzar lo deseado. --replicas=0 para la app sin eliminar el Deployment. Escalar hacia abajo termina pods (graceful shutdown).
ResourceQuota
apiVersion: v1
kind: ResourceQuota
metadata:
name: quota-dev
namespace: dev
spec:
hard:
pods: "20"
requests.cpu: "4"
requests.memory: 8Gi
limits.cpu: "8"
limits.memory: 16GiResourceQuota limita el total de recursos por namespace. Impide que un equipo agote el clúster. Si se alcanza la cuota, los nuevos pods se rechazan. Combínalo con LimitRange para defaults por pod.
Pod Disruption Budget
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: web-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: webEl PDB garantiza disponibilidad mínima durante disrupciones voluntarias (drain, upgrade). minAvailable: 2 garantiza al menos 2 pods activos. Alternativa: maxUnavailable: 1. Protege contra downtime en mantenimientos.
HPA (Horizontal Pod Autoscaler)
kubectl autoscale deployment web \ --min=2 --max=10 --cpu-percent=80 kubectl get hpa kubectl describe hpa web kubectl delete hpa web
El HPA ajusta las réplicas automáticamente según métricas. --cpu-percent=80 escala cuando la media de CPU supera el 80%. Requiere metrics-server y requests definidos en los pods para calcular porcentajes.
LimitRange
apiVersion: v1
kind: LimitRange
metadata:
name: defaults
spec:
limits:
- default:
cpu: "500m"
memory: "256Mi"
defaultRequest:
cpu: "100m"
memory: "64Mi"
type: ContainerLimitRange define defaults de requests/limits para contenedores sin valores explícitos. También puede imponer min/max por contenedor. Se aplica por namespace. Garantiza que ningún pod quede sin límites definidos.
Topology spread
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: webtopologySpreadConstraints distribuye los pods uniformemente por nodos, zonas o regiones. maxSkew: 1 permite una diferencia máxima de 1 pod entre topologías. DoNotSchedule rechaza si no puede distribuir. Garantiza alta disponibilidad.
HPA declarativo
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 75autoscaling/v2 soporta múltiples métricas (CPU, memoria, custom). scaleTargetRef apunta al Deployment. averageUtilization es un porcentaje del request. El HPA verifica las métricas cada 15-30 segundos.
VPA (Vertical Pod Autoscaler)
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: web-vpa
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: web
updatePolicy:
updateMode: "Auto"El VPA ajusta requests/limits automáticamente según el uso real. updateMode: Auto reinicia los pods con nuevos valores. Off solo recomienda. No uses VPA y HPA por CPU al mismo tiempo (conflicto). Requiere instalación extra.
Node affinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disktype
operator: In
values: [ssd]nodeAffinity controla en qué nodos se programan los pods. required es obligatorio; preferred es soft (peso). Más flexible que un nodeSelector simple. Úsalo para SSDs, GPUs o zonas específicas.
Requests y Limits
resources:
requests:
cpu: "250m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"requests = recursos garantizados (usados en la programación). limits = máximo permitido. CPU en millicores (250m = 0.25 core). Memoria: Mi (mebibytes). Superar el limit de memoria causa OOMKilled; la CPU se limita (throttling).
Priority y Preemption
apiVersion: scheduling.k8s.io/v1 kind: PriorityClass metadata: name: high-priority value: 1000000 globalDefault: false description: "Pods críticos" --- spec: priorityClassName: high-priority
PriorityClass define una prioridad numérica. Los pods con mayor prioridad pueden hacer preempt (terminar) pods de menor prioridad para obtener recursos. Esencial para workloads críticos en clústeres compartidos.
Pod affinity/anti-affinity
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: web
topologyKey: kubernetes.io/hostnamepodAntiAffinity reparte los pods de la misma app por nodos diferentes (HA). podAffinity agrupa pods relacionados en el mismo nodo (caché local). topologyKey define el dominio: hostname, zona o región.
Avançado
Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80Ingress enruta el tráfico HTTP/HTTPS externo hacia Services internos. host define el dominio; path define la ruta. Requiere un Ingress Controller (nginx, traefik, AWS ALB). Soporta TLS, rewrites y rate limiting.
ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-sa
---
spec:
serviceAccountName: app-sa
containers:
- name: app
image: myapp:1.0Un ServiceAccount da identidad a los pods para comunicarse con la API. Cada pod usa el SA default si no se especifica. Crea SAs dedicados con RBAC mínimo. El token se monta automáticamente en /var/run/secrets/.
StatefulSet
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: postgres-headless
replicas: 3
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: db
image: postgres:15
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10GiUn StatefulSet es para apps con estado (BDs, colas). Garantiza: nombres estables (postgres-0, postgres-1), DNS individual, almacenamiento persistente por pod y orden de arranque/parada. Requiere un Headless Service.
Ingress con TLS
spec:
tls:
- hosts:
- app.example.com
secretName: tls-secret
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80tls activa HTTPS en el Ingress. secretName referencia un Secret del tipo kubernetes.io/tls con tls.crt y tls.key. Combínalo con cert-manager para certificados automáticos vía Let's Encrypt.
Jobs
apiVersion: batch/v1
kind: Job
metadata:
name: backup
spec:
completions: 1
backoffLimit: 3
template:
spec:
containers:
- name: backup
image: busybox
command: ["sh", "-c", "echo backup done"]
restartPolicy: NeverUn Job ejecuta una tarea hasta completarla. completions = ejecuciones con éxito necesarias. backoffLimit = intentos antes de fallar. restartPolicy: Never u OnFailure (nunca Always). Ideal para migraciones y backups.
NetworkPolicy (egress)
spec:
podSelector:
matchLabels:
app: web
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
name: database
ports:
- port: 5432policyTypes: Egress controla el tráfico de salida. Este ejemplo solo permite al pod web comunicarse con el puerto 5432 en el namespace database. Sin NetworkPolicy, todo el tráfico está permitido por defecto (zero-trust).
RBAC (Role y RoleBinding)
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: reader namespace: dev rules: - apiGroups: [""] resources: ["pods", "services"] verbs: ["get", "list", "watch"] --- kind: RoleBinding metadata: name: reader-binding subjects: - kind: User name: dev roleRef: kind: Role name: reader
Un Role define permisos (recursos + verbs) en un namespace. Un RoleBinding enlaza el Role a usuarios/grupos/ServiceAccounts. ClusterRole/ClusterRoleBinding son globales. Principio de mínimo privilegio.
CronJobs
apiVersion: batch/v1
kind: CronJob
metadata:
name: daily-backup
spec:
schedule: "0 2 * * *"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
template:
spec:
containers:
- name: backup
image: busybox
command: ["sh", "-c", "echo backup"]
restartPolicy: OnFailureUn CronJob crea Jobs según un horario (formato cron). concurrencyPolicy: Allow, Forbid (no solapa), Replace (cancela el anterior). successfulJobsHistoryLimit controla cuántos Jobs completados guardar.
Pod security standards
metadata:
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restrictedLabels de Pod Security Standards en el namespace: privileged (sin restricciones), baseline (mínimo), restricted (máximo). enforce bloquea, audit registra, warn avisa. Sustituye al deprecated PodSecurityPolicy.
RBAC imperativo
kubectl create role reader \ --verb=get,list,watch \ --resource=pods,services -n dev kubectl create rolebinding reader-bind \ --role=reader --user=dev -n dev kubectl auth can-i get pods -n dev kubectl auth can-i delete pods --as=dev
create role y create rolebinding crean permisos vía CLI. auth can-i verifica si una acción está permitida. --as=dev simula otro usuario. Esencial para validar permisos antes de dar acceso.
DaemonSet
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: log-agent
spec:
selector:
matchLabels:
app: log-agent
template:
metadata:
labels:
app: log-agent
spec:
containers:
- name: fluentd
image: fluentd:latestUn DaemonSet ejecuta exactamente 1 pod en cada nodo (o un subconjunto vía nodeSelector). Cuando entra un nodo nuevo, el pod se crea automáticamente. Usado para logging (fluentd), monitoring (prometheus-node-exporter) y networking (CNI).
kubectl debug
kubectl debug my-pod -it --image=busybox kubectl debug my-pod --image=nicolaka/netshoot \ --target=app kubectl debug node/worker-1 -it \ --image=ubuntu kubectl debug deployment/web \ --image=curlimages/curl
debug crea un contenedor efímero en un pod existente (sin restart). --target comparte el namespace de red del contenedor objetivo. debug node/ crea un pod privilegiado en el nodo. Ideal para diagnosticar pods sin herramientas de debug.
Manifests YAML
Estructura básica
apiVersion: v1
kind: Pod
metadata:
name: my-pod
labels:
app: web
annotations:
team: backend
spec:
containers:
- name: app
image: nginxTodo manifiesto tiene 4 campos obligatorios: apiVersion (versión de la API), kind (tipo de recurso), metadata (nombre, labels) y spec (estado deseado). metadata.name debe ser único en el namespace.
Labels y selectors en manifests
metadata:
labels:
app: web
version: v1
tier: frontend
spec:
selector:
matchLabels:
app: web
matchExpressions:
- key: version
operator: In
values: [v1, v2]matchLabels exige correspondencia exacta. matchExpressions soporta operadores: In, NotIn, Exists, DoesNotExist. El selector del Deployment es inmutable tras la creación — planifica bien las labels.
Security context
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 2000
containers:
- name: app
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]securityContext define privilegios: runAsNonRoot impide root, readOnlyRootFilesystem impide escritura, drop: ALL elimina capabilities de Linux. Esencial para hardening en producción.
YAML multi-documento
apiVersion: v1 kind: ConfigMap metadata: name: config data: ENV: prod --- apiVersion: v1 kind: Service metadata: name: web spec: ports: - port: 80
--- separa múltiples recursos en un único archivo. kubectl apply -f los procesa todos secuencialmente. Ideal para agrupar recursos relacionados (Deployment + Service + ConfigMap) en un solo archivo versionado.
Variables de entorno
spec:
containers:
- name: app
env:
- name: DB_HOST
value: "postgres.default"
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: NODE_IP
valueFrom:
fieldRef:
fieldPath: status.hostIPenv define variables de entorno. fieldRef inyecta metadatos del pod (nombre, IP, namespace) — llamado Downward API. Útil para logging, service discovery e identificación del pod sin hardcoding.
Tolerations y taints
# Taint en el nodo:
kubectl taint nodes worker-1 gpu=true:NoSchedule
# Toleration en el pod:
spec:
tolerations:
- key: "gpu"
operator: "Equal"
value: "true"
effect: "NoSchedule"Los Taints repelen pods de los nodos. Las Tolerations permiten que un pod tolere el taint. Efectos: NoSchedule (no programa), PreferNoSchedule (evita), NoExecute (termina). Úsalos para nodos dedicados (GPU, mantenimiento).
Apply vs Create
kubectl apply -f manifest.yaml kubectl create -f manifest.yaml kubectl apply -f ./manifests/ kubectl apply -f https://url/manifest.yaml kubectl diff -f manifest.yaml
apply es declarativo e idempotente: crea o actualiza (merge). create falla si el recurso ya existe. apply -f ./dir/ aplica todos los YAML del directorio. diff muestra lo que cambiaría sin aplicar.
Commands y args
spec:
containers:
- name: app
command: ["sh", "-c"]
args: ["echo $ENV && sleep 3600"]
env:
- name: ENV
value: "production"command sustituye el ENTRYPOINT del Dockerfile. args sustituye el CMD. Ambos aceptan arrays. Las variables de entorno definidas en env se expanden en args. Esencial para init scripts y debug.
Owner references y GC
kubectl get rs -o yaml | grep ownerReferences -A5 kubectl delete deployment web \ --cascade=foreground kubectl delete deployment web \ --cascade=orphan
ownerReferences enlazan recursos (Deployment → ReplicaSet → Pod). Eliminar el owner elimina los dependientes (garbage collection). --cascade=orphan elimina el owner pero mantiene los pods. foreground espera a que los dependientes se eliminen.
Kustomize
# kustomization.yaml apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - deployment.yaml - service.yaml namePrefix: prod- replicas: - name: web count: 5 commonLabels: env: production
Kustomize personaliza manifests sin plantillas. namePrefix, replicas, patches y commonLabels transforman la base. Integrado en kubectl: kubectl apply -k ./overlays/prod/. Alternativa a Helm.
Lifecycle hooks
lifecycle:
postStart:
exec:
command: ["sh", "-c", "echo ready"]
preStop:
exec:
command: ["sh", "-c", "sleep 10"]postStart se ejecuta después de iniciar el contenedor (sin garantía de orden con el ENTRYPOINT). preStop se ejecuta antes de terminar — ideal para graceful shutdown (desregistrar del LB, drenar conexiones). El pod espera a que preStop complete.
Validar manifests
kubectl apply -f manifest.yaml --dry-run=client kubectl apply -f manifest.yaml --dry-run=server kubectl apply -f manifest.yaml --validate=strict kubeconform -f manifest.yaml kubeval manifest.yaml
--dry-run=client valida la sintaxis localmente. --dry-run=server valida contra el API server (más completo). --validate=strict rechaza campos desconocidos. Herramientas externas: kubeconform, kubeval, datree.
Dicas e Troubleshooting
Eventos del clúster
kubectl get events --sort-by=\ .metadata.creationTimestamp kubectl get events -n dev --field-selector \ type=Warning kubectl get events -A --watch
get events muestra eventos recientes (creación de pods, fallos de pull, OOM). --sort-by ordena cronológicamente. type=Warning filtra solo errores. Los eventos expiran tras ~1 hora — usa logging centralizado para el histórico.
Backup y restore (etcd)
kubectl get all -A -o yaml > backup-all.yaml kubectl get deployment,svc,cm,secret \ -n prod -o yaml > prod-backup.yaml kubectl apply -f prod-backup.yaml # etcd snapshot (admin): ETCDCTL_API=3 etcdctl snapshot save /backup.db
Exporta recursos con -o yaml para un backup declarativo. get all no incluye todo (faltan PVCs, Secrets, Roles). Para un backup completo del clúster, haz snapshot del etcd. Herramientas: Velero, kube-backup.
JSONPath y output custom
kubectl get pods -o jsonpath=\
'{.items[*].metadata.name}'
kubectl get pods -o jsonpath=\
'{.items[?(@.status.phase=="Running")].metadata.name}'
kubectl get nodes -o jsonpath=\
'{range .items[*]}{.metadata.name}{"\t"}{.status.conditions[-1].type}{"\n"}{end}'jsonpath extrae datos específicos del JSON. .items[*] itera arrays. ?(@.campo=="valor") filtra. range/end crea bucles con formato. Combínalo con xargs para automatizar scripts.
Namespaces como entornos
kubectl create namespace dev kubectl create namespace staging kubectl create namespace prod kubectl config set-context dev-ctx \ --cluster=my-cluster --namespace=dev kubectl get pods -n prod kubectl top pods -n staging
Usa namespaces para aislar entornos (dev, staging, prod) o equipos. Combínalos con ResourceQuota para limitar recursos y NetworkPolicy para aislar tráfico. Los contextos con namespace por defecto evitan el -n constante.
Aliases útiles
alias k='kubectl' alias kgp='kubectl get pods' alias kgs='kubectl get svc' alias kgd='kubectl get deployments' alias kaf='kubectl apply -f' alias kdp='kubectl describe pod' alias kl='kubectl logs -f' alias kex='kubectl exec -it' # Autocomplete: source <(kubectl completion bash) complete -o default -F __start_kubectl k
Los aliases aceleran el uso diario de kubectl. completion bash activa el autocompletado de recursos y nombres. Añádelos al .bashrc o .zshrc. Herramientas como kubectx y kubens agilizan el cambio de contexto/namespace.
Buenas prácticas de producción
# 1. Definir siempre requests/limits # 2. Usar liveness + readiness probes # 3. runAsNonRoot: true # 4. readOnlyRootFilesystem: true # 5. Imágenes con tag específico (no :latest) # 6. PodDisruptionBudget para HA # 7. topologySpreadConstraints # 8. Secrets cifrados (etcd encryption)
Producción exige: requests/limits (evita noisy neighbors), probes (health checks), securityContext restrictivo, tags de imagen fijos (reproducibilidad), PDB (disponibilidad en upgrades) y topologySpread (HA).
Debug de pods con problemas
kubectl get pods kubectl describe pod my-pod kubectl logs my-pod --previous kubectl exec -it my-pod -- sh kubectl get events --field-selector \ involvedObject.name=my-pod kubectl top pod my-pod
Flujo de debug: 1) get pods (estado), 2) describe (eventos), 3) logs --previous (crash anterior), 4) exec (interior), 5) top (recursos). CrashLoopBackOff → ver logs; Pending → verificar recursos/nodos.
Herramientas esenciales
# Gestión: kubectl, kubectx, kubens, k9s # Debug: kubectl debug, stern (logs), ktop # Seguridad: kube-bench, trivy, falco, opa # Deploy: helm, kustomize, argocd, flux # Monitoring: prometheus, grafana, loki
Ecosistema esencial: k9s (TUI), stern (multi-pod logs), helm (package manager), argocd (GitOps), prometheus+grafana (monitoring), trivy (escaneo de imágenes), kube-bench (CIS benchmarks).