Depurar Contenedores Distroless en Kubernetes: kubectl debug, Ephemeral Containers y Cuándo Usar Cada Técnica
El contenedor funciona perfectamente en CI. Se despliega sin problemas en staging. Luego algo falla en producción y escribes el comando de siempre: kubectl exec -it my-pod -- /bin/bash. La respuesta es inmediata: OCI runtime exec failed: exec failed: unable to start container process: exec: "/bin/bash": stat /bin/bash: no such file or directory.
Pruebas con /bin/sh. Mismo error. Pruebas con ls. Mismo error. La imagen del contenedor es distroless — contiene únicamente el binario de tu aplicación y sus dependencias de runtime, sin shell, sin gestor de paquetes, sin ningún tipo de herramienta de diagnóstico. Esto es intencionado y correcto desde el punto de vista de seguridad. También es un desafío operativo significativo la primera vez que te lo encuentras en producción.
Este artículo cubre todas las técnicas prácticas para depurar contenedores distroless en Kubernetes: kubectl debug con ephemeral containers (el enfoque estándar), estrategia pod copy (para versiones de Kubernetes sin soporte de ephemeral containers, o cuando necesitas modificar el spec del pod en ejecución), variantes de imagen debug (el atajo pragmático para desarrolladores), cdebug (una herramienta específica que simplifica el proceso), y depuración a nivel de nodo (el último recurso, con mayor poder). Para cada técnica explicaré qué puede y qué no puede hacer, qué versión de Kubernetes o permisos RBAC requiere, y en qué escenario — desarrollador en local, platform engineer en staging, ops en producción — es la opción más adecuada.
Por Qué los Contenedores Distroless Rompen el Flujo Normal de Depuración
La depuración tradicional de contenedores asume que puedes hacer exec dentro del contenedor y usar herramientas de shell: ps, netstat, strace, curl, un editor de texto. Las imágenes distroless eliminan todo esto por diseño. El proyecto Google distroless, las imágenes basadas en Wolfi de Chainguard, y el ecosistema de imágenes minimalistas en general excluyen deliberadamente todo lo que no es necesario para ejecutar la aplicación. El resultado es una superficie de ataque drásticamente reducida: sin shell no hay RCE mediante shell injection, sin gestor de paquetes no hay vía de escalada fácil, con menos binarios hay menos CVEs en el escaneo de la imagen.
El tradeoff es operativo: cuando algo falla, no puedes usar las herramientas que el propio proceso no tiene permitido ejecutar. Una aplicación Java en gcr.io/distroless/java17-debian12 tiene el JRE y nada más. Un binario de Go compilado con CGO deshabilitado y empaquetado en gcr.io/distroless/static-debian12 tiene literalmente solo el binario y los certificados CA y datos de zona horaria necesarios. No hay wget para descargar un binario de depuración, no hay apt para instalar uno, no hay bash para ejecutar un script.
Kubernetes resuelve esto a nivel de plataforma con los ephemeral containers, añadidos como estables en Kubernetes 1.25. El principio es que un contenedor de depuración — que puede tener una shell completa y cualquier herramienta que necesites — puede inyectarse en un pod en ejecución y compartir su process namespace, network namespace y montajes del sistema de ficheros sin modificar el contenedor original ni reiniciar el pod.
Opción 1: kubectl debug con Ephemeral Containers
Los ephemeral containers son la solución canónica. Desde Kubernetes 1.25 (estable), kubectl debug puede inyectar un contenedor temporal en un pod en ejecución. El contenedor comparte el network namespace del pod destino por defecto, y con --target también puede compartir el process namespace de un contenedor específico, lo que permite inspeccionar sus procesos en ejecución y file descriptors abiertos.
La invocación básica es:
kubectl debug -it my-pod \
--image=busybox:latest \
--target=my-container
El flag --target es la pieza crítica. Sin él, el ephemeral container tiene su propio process namespace. Con él, comparte el process namespace del contenedor especificado — lo que significa que puedes ejecutar ps aux y ver los procesos de la aplicación, usar ls -la /proc/<pid>/fd para inspeccionar file descriptors abiertos, y leer el entorno de la aplicación mediante cat /proc/<pid>/environ.
Para un entorno de depuración más potente, reemplaza busybox con una imagen más completa:
kubectl debug -it my-pod \
--image=nicolaka/netshoot \
--target=my-container
nicolaka/netshoot incluye tcpdump, curl, dig, nmap, ss, iperf3 y docenas de otras herramientas de diagnóstico de red, lo que la convierte en la opción estándar para escenarios de depuración de red.
Qué Puedes y Qué No Puedes Hacer
Los ephemeral containers comparten el network namespace del pod y, cuando se usa --target, el process namespace. Esto te da:
- Visibilidad completa sobre el tráfico de red de la aplicación desde dentro del pod (
tcpdump,ss,netstat) - Inspección de procesos mediante
/proc/<pid>— ficheros abiertos, memory maps, variables de entorno, uso de CPU y memoria - Acceso al contexto de resolución DNS del pod — exactamente el mismo
/etc/resolv.confque ve la aplicación - Capacidad de realizar llamadas de red salientes desde el mismo network namespace (probar endpoints de servicios, resolución DNS)
Lo que no obtienes con ephemeral containers:
- Acceso al sistema de ficheros del contenedor de aplicación. El ephemeral container tiene su propio root filesystem. No puedes hacer
cat /app/config.yamldel sistema de ficheros del contenedor de aplicación a menos que accedas a través de/proc/<pid>/root/. - Capacidad de eliminar el contenedor una vez añadido. Los ephemeral containers son permanentes hasta que se elimina el pod. Esto es por diseño — la API de Kubernetes no permite eliminarlos tras su creación.
- Modificaciones de montajes de volúmenes mediante CLI. No puedes añadir montajes de volúmenes a un ephemeral container mediante
kubectl debug(aunque el spec de la API lo soporta, la CLI no expone esta opción). - Límites de recursos. Los ephemeral containers no soportan resource requests y limits en la CLI de
kubectl debug, aunque esto está evolucionando.
Acceder al Sistema de Ficheros de la Aplicación
La sorpresa más común para los desarrolladores que se inician con ephemeral containers es que no pueden explorar directamente el sistema de ficheros del contenedor de aplicación. La solución es el sistema de ficheros /proc:
# Encontrar el PID de la aplicación
ps aux
# Explorar su sistema de ficheros mediante /proc
ls /proc/1/root/app/
cat /proc/1/root/etc/config.yaml
# O establecer el root al del sistema de ficheros de la aplicación
chroot /proc/1/root /bin/sh # solo si /bin/sh existe en la imagen de la aplicación
La ruta /proc/<pid>/root es un enlace simbólico al root filesystem del contenedor tal como lo ve el process namespace. Dado que el ephemeral container comparte el process namespace con --target, el PID de la aplicación es normalmente 1, y /proc/1/root te da acceso completo de lectura a su sistema de ficheros.
Requisitos RBAC
Los ephemeral containers requieren el permiso de subrecurso pods/ephemeralcontainers. Esto es independiente de pods/exec, que controla kubectl exec. Un error frecuente es conceder pods/exec para propósitos de depuración sin darse cuenta de que los ephemeral containers requieren un permiso adicional:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: ephemeral-debugger
rules:
- apiGroups: [""]
resources: ["pods/ephemeralcontainers"]
verbs: ["update", "patch"]
- apiGroups: [""]
resources: ["pods/attach"]
verbs: ["create", "get"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
En entornos de producción, este permiso debe estar estrictamente acotado: con límite de tiempo mediante RoleBinding en lugar de ClusterRoleBinding permanente, restringido a namespaces específicos, e idealmente protegido por un flujo de aprobación. El contenedor de depuración se ejecuta como root por defecto, lo que puede crear vías de escalada de privilegios si el contenedor de aplicación se ejecuta como un usuario no-root con process namespace compartido — el contenedor de debug puede adjuntarse a los procesos de la aplicación con privilegios más elevados.
Opción 2: kubectl debug –copy-to (Estrategia Pod Copy)
Cuando necesitas modificar el spec del contenedor del pod — reemplazar la imagen, cambiar variables de entorno, añadir un sidecar con un sistema de ficheros compartido — el flag --copy-to crea una copia completa del pod con tus modificaciones aplicadas:
kubectl debug my-pod \
-it \
--copy-to=my-pod-debug \
--image=my-app:debug \
--share-processes
Esto crea un nuevo pod llamado my-pod-debug que es una copia de my-pod pero con la imagen del contenedor reemplazada por my-app:debug. Si my-app:debug es tu imagen de aplicación construida con herramientas de depuración incluidas (o una variante debug de tu registro), esto te permite interactuar con el mismo binario exacto en la misma configuración que el pod original.
Un uso más habitual de --copy-to es adjuntar un contenedor de debug junto al contenedor de aplicación existente manteniendo la imagen original sin cambios:
kubectl debug my-pod \
-it \
--copy-to=my-pod-debug \
--image=busybox \
--share-processes \
--container=debugger
Esto crea el pod copia con los contenedores originales más un nuevo contenedor debugger compartiendo el process namespace. A diferencia de los ephemeral containers, este enfoque soporta montajes de volúmenes y límites de recursos, y el pod de debug puede eliminarse de forma limpia cuando termines.
Limitaciones de la Estrategia Copy
La estrategia de copia de pods tiene una limitación crítica: no estás depurando el pod original. Crea un nuevo pod que puede comportarse de manera diferente porque:
- No comparte el estado en memoria del pod original — si el problema es una goroutine leak o corrupción de heap que ha estado acumulándose durante horas, la copia reciente no lo exhibirá inmediatamente
- Crea un nuevo Pod UID, lo que significa que cualquier admission webhook, network policy, o security context a nivel de pod que dependa de la identidad del pod puede aplicarse de forma diferente
- Si el pod original está crasheando (CrashLoopBackOff), la copia también lo hará — esta técnica no sirve para depurar crashes a menos que también cambies el entrypoint
Para depurar crashes específicamente, combina --copy-to con un entrypoint modificado para mantener el contenedor activo:
kubectl debug my-crashing-pod \
-it \
--copy-to=my-pod-debug \
--image=busybox \
--share-processes \
-- sleep 3600
Opción 3: Variantes de Imagen Debug
El enfoque más pragmático — y el más adecuado para flujos de trabajo de desarrolladores — es mantener una variante debug de tu imagen de aplicación que incluya herramientas de shell. Tanto el proyecto Google distroless como Chainguard ofrecen este patrón de forma oficial.
Las imágenes Google distroless tienen un tag :debug que añade BusyBox a la imagen:
# Imagen de producción
FROM gcr.io/distroless/java17-debian12
# Variante debug — idéntica pero con shell BusyBox
FROM gcr.io/distroless/java17-debian12:debug
Las imágenes de Chainguard siguen una convención similar con variantes :latest-dev que incluyen apk, una shell y utilidades comunes:
# Producción (sin shell, footprint mínimo)
FROM cgr.dev/chainguard/go:latest
# Variante de desarrollo/debug
FROM cgr.dev/chainguard/go:latest-dev
Si construyes tus propias imágenes base, el enfoque recomendado es usar multi-stage builds y mantener build targets separados:
FROM golang:1.22 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp .
# Producción: imagen estática distroless
FROM gcr.io/distroless/static-debian12 AS production
COPY --from=builder /app/myapp /myapp
ENTRYPOINT ["/myapp"]
# Variante debug: mismo binario, con herramientas de shell
FROM gcr.io/distroless/static-debian12:debug AS debug
COPY --from=builder /app/myapp /myapp
ENTRYPOINT ["/myapp"]
En tu pipeline de CI/CD, construye ambos targets y publica my-app:${VERSION} (producción) y my-app:${VERSION}-debug (variante debug) en tu registro. La imagen debug nunca se despliega en producción por defecto, pero existe y está lista para usarse con kubectl debug --copy-to cuando sea necesario.
Consideraciones de Seguridad para las Variantes Debug
Las variantes de imagen debug anulan gran parte del beneficio de seguridad de distroless si se usan en producción, aunque sea temporalmente. Lleva un control del uso: registra cuándo se despliegan imágenes debug, requiere aprobación explícita y asegúrate de que se eliminan tras la sesión de depuración. En entornos regulados, considera si desplegar una variante debug en namespaces de producción está permitido por tu política de seguridad — en muchos casos no lo está, y debes usar ephemeral containers (que añaden un proceso de debug al pod sin modificar la imagen de la aplicación) en su lugar.
Opción 4: cdebug
cdebug es una herramienta CLI de código abierto que simplifica la depuración de contenedores distroless envolviendo kubectl debug con defaults más ergonómicos y capacidades adicionales. Su valor principal está en hacer que la depuración con ephemeral containers se sienta como una experiencia nativa de shell:
# Instalación
brew install cdebug
# o: go install github.com/iximiuz/cdebug@latest
# Depurar un pod en ejecución
cdebug exec -it my-pod
# Especificar un namespace y contenedor
cdebug exec -it -n production my-pod -c my-container
# Usar una imagen de debug específica
cdebug exec -it my-pod --image=nicolaka/netshoot
Lo que cdebug añade sobre el raw kubectl debug:
- Chroot automático del sistema de ficheros.
cdebug execestablece automáticamente el root del sistema de ficheros del contenedor de debug al del contenedor destino, de modo que navegas por/y ves los ficheros de la aplicación — no los del debug image. Esto resuelve el principal punto de fricción conkubectl debug. - Integración con Docker.
cdebug execfunciona de forma idéntica para contenedores Docker (cdebug exec -it <container-id>), haciendo que sea el mismo flujo de trabajo para depuración local y en cluster. - Sin complicaciones RBAC para desarrollo local basado en Docker — útil para flujos de trabajo de desarrolladores antes de que el código llegue a Kubernetes.
El tradeoff: cdebug es una dependencia de terceros y requiere instalación. En entornos con políticas estrictas de tooling (industrias reguladas, clusters air-gapped), puede que no sea una opción. En esos casos, el flujo de trabajo raw de kubectl debug con navegación del sistema de ficheros mediante /proc/1/root es la línea base.
Opción 5: Depuración a Nivel de Nodo
Cuando todo lo demás falla — el pod está en CrashLoopBackOff demasiado rápido para adjuntarse, el problema es a nivel de kernel, o necesitas herramientas como strace que requieren privilegios elevados — la depuración a nivel de nodo te da acceso directo a los procesos del contenedor desde el nodo host.
kubectl debug node/ crea un pod privilegiado en el nodo destino que monta el root filesystem del nodo bajo /host:
kubectl debug node/my-node-name \
-it \
--image=nicolaka/netshoot
Desde este pod privilegiado, puedes usar nsenter para entrar en los namespaces de cualquier contenedor que se ejecute en el nodo:
# Encontrar el PID del contenedor en el nodo
# (desde dentro del pod de debug del nodo)
crictl ps | grep my-container
crictl inspect <container-id> | grep pid
# Entrar en los namespaces del contenedor
nsenter -t <pid> -m -u -i -n -p -- /bin/sh
# O solo el network namespace (para depuración de red)
nsenter -t <pid> -n -- ip a
El enfoque con nsenter te permite ejecutar herramientas del toolset del nodo o del contenedor de debug mientras operas en los namespaces del contenedor destino. Así es como ejecutas strace contra un proceso distroless: strace no está en el contenedor de aplicación, pero puedes ejecutarlo desde el nivel de nodo apuntando al PID de la aplicación:
# Trazar todas las syscalls del proceso de la aplicación
nsenter -t <pid> -- strace -p <pid> -f -e trace=network
RBAC y Seguridad para la Depuración a Nivel de Nodo
La depuración a nivel de nodo requiere nodes/proxy y la capacidad de crear pods privilegiados, lo que en la mayoría de clusters de producción está restringido a administradores del cluster. El pod de debug se ejecuta con hostPID: true y hostNetwork: true, dándole visibilidad sobre todos los procesos y el tráfico de red del nodo — no solo del contenedor destino. Esto es significativo: todos los procesos que se ejecutan en el nodo, incluidos los de namespaces de otros tenants, son visibles.
Esta técnica debe tratarse como un procedimiento de break-glass: registra el acceso, requiere doble aprobación en entornos de producción, y limpia inmediatamente tras la sesión de depuración con kubectl delete pod --selector=app=node-debugger.
Cómo Elegir el Enfoque Correcto: Matriz de Perfil de Acceso y Entorno
La técnica que debes usar depende de dos ejes: quién eres (desarrollador, platform engineer, ops/SRE) y dónde está el problema (desarrollo local, staging, producción). Los requisitos y restricciones difieren significativamente entre estas combinaciones.
Desarrollador — Cluster Local o de Desarrollo
Objetivo: Reproducir y entender un bug, inspeccionar la configuración, verificar la conectividad de red a los servicios.
Restricciones: Ninguna significativa — admin completo del cluster en el entorno local o en el namespace de desarrollo personal.
Enfoque recomendado: Variantes de imagen debug o cdebug.
En desarrollo local (Minikube, Kind, Docker Desktop), el camino más rápido es construir la variante debug de tu imagen y desplegarla directamente. Si trabajas con el servicio de otro equipo, cdebug exec te da una shell en el contenedor con el root del sistema de ficheros automático sin necesidad de RBAC especial. El objetivo es velocidad e iteración — reserva los enfoques más estructurados para entornos superiores.
Desarrollador — Cluster de Staging
Objetivo: Depurar problemas de integración, inspeccionar configuración en vivo, verificar el comportamiento específico del entorno.
Restricciones: Cluster compartido — no se pueden desplegar workloads arbitrarios en los namespaces de otros equipos, pero se tiene pods/ephemeralcontainers en el propio namespace.
Enfoque recomendado: kubectl debug con ephemeral containers (--target), acotado al propio namespace.
Staging es donde los ephemeral containers demuestran su valor. Puedes adjuntarte a un pod en ejecución sin reiniciarlo, sin modificar el spec del deployment, y sin afectar a otros usuarios del mismo cluster. Concede a los desarrolladores pods/ephemeralcontainers en los namespaces de su equipo y podrán depurar de forma autónoma sin necesitar intervención de ops.
Platform Engineer / SRE — Producción
Objetivo: Diagnosticar un incidente de producción en vivo. El pod se comporta de forma inesperada — alta latencia, crecimiento de memoria, conexiones inesperadas, respuestas incorrectas.
Restricciones: Los cambios en pods en ejecución son de alto riesgo. Cualquier despliegue de imagen debug debe estar controlado. El problema está activo y afectando a usuarios.
Enfoque recomendado: kubectl debug con ephemeral containers (los ephemeral containers no reinician el pod, no modifican el deployment y son auditables mediante los audit logs de la API).
Los requisitos clave en producción son la auditabilidad y el blast radius mínimo. Los ephemeral containers satisfacen ambos: quedan registrados en el audit log de la API de Kubernetes (quién se adjuntó, cuándo, a qué pod), no modifican el contenedor de aplicación en ejecución, y están limitados al network y process namespaces del propio pod. Documenta la sesión de depuración en tu ticket de incidencia: nombre del pod, hora, qué se observó, quién ejecutó el contenedor de debug.
La estrategia --copy-to generalmente no es apropiada para respuesta a incidentes de producción: crea un nuevo pod que puede o no exhibir el problema, añade carga al cluster durante un incidente, y si está conectado a los mismos servicios (bases de datos, APIs downstream), produce tráfico adicional que complica la investigación forense.
Platform Engineer — Producción, Problema a Nivel de Nodo
Objetivo: Diagnosticar un problema a nivel de kernel, un problema del container runtime, un problema de red que abarca múltiples pods, o una situación en la que el pod crashea demasiado rápido para adjuntarse.
Restricciones: Se requiere el máximo privilegio. Alto riesgo operativo.
Enfoque recomendado: Pod de debug a nivel de nodo con nsenter. Tratar como break-glass.
Para este escenario, crea un rol RBAC dedicado que conceda acceso a nodes/proxy y la capacidad de crear pods con hostPID: true en un namespace de debug dedicado. Vincúlalo solo a usuarios específicos, requiere un paso de autenticación separado (por ejemplo, comprobar kubectl auth can-i contra un binding de tiempo limitado), y registra todos los accesos. Este nivel de acceso debe generar una alerta estilo PagerDuty para que el equipo de seguridad sepa que hay una sesión de debug privilegiada activa en producción.
Errores Comunes y Soluciones
| Error | Causa | Solución |
|---|---|---|
| «ephemeral containers are disabled for this cluster» | Kubernetes < 1.25 o feature gate deshabilitado | Actualiza el cluster o habilita el feature gate EphemeralContainers |
| «cannot update ephemeralcontainers» | Falta permiso RBAC | Concede el rol pods/ephemeralcontainers descrito arriba |
«container not found» con --target | Nombre de contenedor incorrecto | Verifica con kubectl get pod -o jsonpath='{.spec.containers[*].name}' |
No se puede leer /proc/1/root | Falta CAP_SYS_PTRACE | Usa el perfil PSS Baseline o añade explícitamente la capability |
| tcpdump no muestra tráfico | Capturando en la interfaz equivocada | Usa tcpdump -i any para capturar en todas las interfaces |
«ephemeral containers are disabled for this cluster»
Los ephemeral containers requieren Kubernetes 1.16+ (alpha, tras feature gate) y son estables desde la versión 1.25. Si estás en la versión 1.16–1.22, necesitas habilitar el feature gate EphemeralContainers en el API server y en kubelet. Desde la versión 1.23 era beta y estaba habilitado por defecto. Desde la versión 1.25 es estable y siempre está activo. En servicios de Kubernetes gestionados (EKS, GKE, AKS), comprueba la versión del cluster — las versiones anteriores a la 1.25 pueden tenerlo deshabilitado según tu configuración.
«cannot update ephemeralcontainers» (RBAC)
Tienes pods/exec pero no pods/ephemeralcontainers. Añade el permiso mostrado en la sección RBAC anterior. Ten en cuenta que pods/exec y pods/ephemeralcontainers son subrecursos separados — tener uno no implica el otro.
«container not found» con –target
El nombre de contenedor en --target debe coincidir exactamente con el nombre del contenedor tal como está definido en el spec del Pod — no el nombre de la imagen. Compruébalo con kubectl get pod my-pod -o jsonpath='{.spec.containers[*].name}' para obtener los nombres exactos de los contenedores.
Puedo ver procesos pero no puedo leer /proc/1/root
El contenedor de aplicación se ejecuta como usuario no-root (por ejemplo, UID 1000) y el ephemeral container se ejecuta como root. El sistema de ficheros de la aplicación puede tener ficheros propiedad de UID 1000 que no son legibles por otros UIDs según los permisos. La propia ruta /proc/<pid>/root requiere la capability CAP_SYS_PTRACE. Si los PodSecurityStandards (PSS) de tu cluster están configurados como restricted, el contenedor de debug puede no tener esta capability. Usa el perfil PSS Baseline para namespaces de debug o añade explícitamente SYS_PTRACE al securityContext del ephemeral container.
tcpdump no muestra tráfico
Cuando uses nicolaka/netshoot para depuración de red, asegúrate de que el ephemeral container se crea sin --target si tu objetivo es capturar todo el tráfico en la interfaz de red del pod (no solo el del proceso del contenedor específico). Con --target, compartes el process namespace pero el network namespace se comparte a nivel de pod independientemente. Ejecuta tcpdump -i any para capturar en todas las interfaces incluyendo loopback, que es por donde viaja el tráfico entre contenedores dentro de un pod.
Marco de Decisión
Usa esto como punto de partida para seleccionar la técnica adecuada a tu situación:
| Escenario | Técnica | Requisito |
|---|---|---|
| Incidente de producción activo, pod en ejecución | kubectl debug + ephemeral container | RBAC pods/ephemeralcontainers, k8s 1.25+ |
| Pod crasheando demasiado rápido para adjuntarse | kubectl debug --copy-to + entrypoint modificado | Capacidad de crear pods en el namespace |
| Desarrollador depurando en dev/staging | cdebug exec o kubectl debug | pods/ephemeralcontainers o pod create |
| Necesitas acceso completo al sistema de ficheros | kubectl debug --copy-to + variante debug | Imagen debug en registro, pod create |
| Necesitas strace o kernel tracing | Debug a nivel de nodo con nsenter | nodes/proxy, equivalente a cluster admin |
| Captura de paquetes de red | kubectl debug + nicolaka/netshoot | pods/ephemeralcontainers |
| Depuración local en Docker | cdebug exec <container-id> | Acceso al Docker socket |
| Entorno de debug reproducible en CI | Variante debug en build target separado | Tag de imagen separado en registro |
Diseño RBAC para Producción
Un diseño RBAC limpio para la depuración de contenedores distroless en producción separa tres roles con diferentes niveles de privilegio:
# Tier 1: Autoservicio del desarrollador en namespaces de equipo
# Permite adjuntar ephemeral containers, sin acceso a nodo
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: distroless-debugger
namespace: team-namespace
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
- apiGroups: [""]
resources: ["pods/ephemeralcontainers"]
verbs: ["update", "patch"]
- apiGroups: [""]
resources: ["pods/attach"]
verbs: ["create", "get"]
---
# Tier 2: Acceso SRE a incidentes de producción
# Ephemeral containers en todos los namespaces
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: sre-distroless-debugger
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
- apiGroups: [""]
resources: ["pods/ephemeralcontainers"]
verbs: ["update", "patch"]
- apiGroups: [""]
resources: ["pods/attach"]
verbs: ["create", "get"]
---
# Tier 3: Acceso break-glass a nodo
# Solo para el equipo de plataforma, binding de tiempo limitado recomendado
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: node-debugger
rules:
- apiGroups: [""]
resources: ["nodes/proxy"]
verbs: ["get"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["create", "get", "list", "delete"]
# Restringir al namespace de debug mediante RoleBinding, no ClusterRoleBinding
Vincula el Tier 1 permanentemente a tus desarrolladores. Vincula el Tier 2 a los SREs de forma permanente pero con alertas de auditoría sobre su uso. Vincula el Tier 3 solo bajo demanda (mediante un operador de Kubernetes que cree RoleBinding de tiempo limitado) y nunca como ClusterRoleBinding permanente.
La clave de este modelo de tres niveles es que cada escalada de privilegios es visible y tiene coste operativo. Un desarrollador puede depurar su propio namespace sin coordinación. Un SRE que necesita acceso a producción usa el Tier 2, que queda registrado en los audit logs. Un platform engineer que necesita acceso a nivel de nodo activa el Tier 3, que genera alertas y requiere un binding temporal — haciendo que el acceso privilegiado sea auditable y difícil de mantener silenciosamente durante más tiempo del necesario.
Conclusión
Los contenedores distroless son la elección correcta para workloads de producción. Reducen la superficie de ataque, eliminan CVEs innecesarios y obligan a una separación más limpia entre aplicación y tooling. El coste operativo es que tu flujo de trabajo habitual de depuración — exec dentro del contenedor, ejecutar algunos comandos — ya no funciona por defecto.
Kubernetes ofrece una respuesta clara con los ephemeral containers y kubectl debug: inyecta un contenedor de debug con las herramientas que necesites en el pod en ejecución, compartiendo sus namespaces de red y de procesos, sin reiniciar ni modificar la aplicación. Para escenarios donde los ephemeral containers son insuficientes — acceso al sistema de ficheros, depuración de crashes, investigación a nivel de kernel — la estrategia copy y el debug a nivel de nodo cubren los huecos restantes.
La clave para que esto funcione a escala no es la técnica en sí, sino el modelo de acceso: los desarrolladores tienen acceso de autoservicio a ephemeral containers en sus propios namespaces, los SREs tienen acceso a ephemeral containers en todo el cluster para incidentes de producción, y el acceso a nivel de nodo es un procedimiento de break-glass con trail de auditoría y límites de tiempo. Con ese modelo en su lugar, los contenedores distroless se convierten en una cuestión operativa resuelta, no en un obstáculo.