DevTools

Cheatsheet Kubernetes

Orquestração de contentores para deploy, escala e gestão automatizada

Volver a los lenguajes
Kubernetes
114 tarjetas encontradas
Categorías:
Versiones:

Cluster e Contexto


12 cards
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


12 cards
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: 8080

livenessProbe 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: 80

Un 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


12 cards
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.25

spec.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: Recreate

RollingUpdate 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.0

minReadySeconds 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


12 cards
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: 80

Un 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: 3600

sessionAffinity: 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: ClusterIP

El 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 -A

describe 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: 9090

Un 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: 8080

NetworkPolicy 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


12 cards
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.0

imagePullSecrets 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.0

data 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-config

configMapKeyRef 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-creds

secretKeyRef 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: true

Montar 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: true

Montar 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


10 cards
PersistentVolumeClaim
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: data-pvc
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
  storageClassName: standard

Un 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/data

El 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: 256Mi

emptyDir 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/data

Un 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: DirectoryOrCreate

hostPath 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


12 cards
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: 16Gi

ResourceQuota 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: web

El 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: Container

LimitRange 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: web

topologySpreadConstraints 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: 75

autoscaling/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/hostname

podAntiAffinity 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


12 cards
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: 80

Ingress 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.0

Un 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: 10Gi

Un 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: 80

tls 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: Never

Un 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: 5432

policyTypes: 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: OnFailure

Un 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: restricted

Labels 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:latest

Un 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


12 cards
Estructura básica
apiVersion: v1
kind: Pod
metadata:
  name: my-pod
  labels:
    app: web
  annotations:
    team: backend
spec:
  containers:
  - name: app
    image: nginx

Todo 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.hostIP

env 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


8 cards
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).