71 puntos por GN⁺ 2026-01-21 | 9 comentarios | Compartir por WhatsApp
  • Aunque ejecutes docker run ubuntu, comparte el kernel Linux del host y Ubuntu solo aporta herramientas de espacio de usuario
  • El resultado de uname -r muestra la versión del kernel del host, y solo /etc/os-release aparece con información de Ubuntu
  • Las VM tienen su propio kernel y tardan varios minutos en arrancar, pero los contenedores inician en milisegundos y comparten el kernel del host con aislamiento a nivel de sistema operativo, sin virtualización de hardware, por lo que el overhead es bajo
  • Gracias a la estabilidad del ABI de system calls de Linux, contenedores de distintas distribuciones pueden ejecutarse sobre el mismo kernel
  • En un entorno con 16 GB de RAM, un límite práctico es de 50-100 contenedores livianos, 10-30 medianos y 5-10 contenedores grandes
  • Entender esta arquitectura es importante porque una vulnerabilidad del kernel afecta a todos los contenedores, y la elección de la imagen base impacta directamente en la compatibilidad y la seguridad

Qué significa “ejecutar Ubuntu”

  • Al ejecutar docker run ubuntu:22.04, obtienes un prompt de bash que parece Ubuntu y puedes usar apt update e instalar paquetes
  • Pero si ejecutas uname -r dentro del contenedor, verás la versión del kernel del host (por ejemplo, 6.5.0-44-generic)
  • El archivo /etc/os-release muestra Ubuntu 22.04, pero el kernel es el de la máquina host, y la parte “Ubuntu” no es más que el sistema de archivos que compone el espacio de usuario

Contenedores vs máquinas virtuales: comparación de arquitectura

  • Las VM virtualizan el hardware, mientras que los contenedores virtualizan el sistema operativo
  • Diferencias principales:
    • Kernel: cada VM tiene su propio kernel; los contenedores comparten el kernel del host
    • Tiempo de arranque: las VM tardan minutos; los contenedores, milisegundos
    • Overhead de memoria: VM 512 MB-4 GB; contenedores 1-10 MB
    • Uso de disco: VM 10-100 GB; imágenes de contenedor 10-500 MB
    • Nivel de aislamiento: VM a nivel de hardware; contenedores a nivel de proceso
    • Rendimiento: las VM tienen alrededor de 5-10% de overhead; los contenedores ofrecen un rendimiento cercano al nativo

Qué incluye realmente una imagen base

  • Contenido del tarball que se descarga al hacer pull de ubuntu:22.04:
  • 1. Binarios esenciales (/bin, /usr/bin)

    • /bin/bash (shell), /bin/ls (lista archivos), /bin/cat (muestra archivos)
    • /usr/bin/apt (gestor de paquetes), /usr/bin/dpkg (herramienta de paquetes de Debian)
  • 2. Bibliotecas compartidas (/lib, /usr/lib)

    • glibc y otras bibliotecas compartidas contra las que enlazan los programas
    • /lib/x86_64-linux-gnu/libc.so.6 (biblioteca C, la base de todos los programas en C)
    • Bibliotecas esenciales como libpthread.so.0 y libm.so.6
  • 3. Archivos de configuración (/etc)

    • /etc/apt/sources.list (repositorios de paquetes)
    • /etc/passwd (base de datos de usuarios)
    • /etc/resolv.conf (configuración DNS, normalmente montada desde el host)
  • 4. Base de datos de paquetes

    • /var/lib/dpkg/status (paquetes instalados)
    • /var/lib/apt/lists/ (caché de paquetes disponibles)
  • No incluye kernel, bootloader ni drivers

El kernel sigue igual, todo lo demás cambia

  • Funciones que aporta el kernel de Linux: planificación de procesos, gestión de memoria, operaciones de sistema de archivos, stack de red, drivers de dispositivos y system calls
  • Cuando un proceso dentro del contenedor llama a open(), read() o fork(), la llamada va directamente al kernel del host
  • Al kernel ni le importa ni sabe si ese proceso viene de un “contenedor Ubuntu” o de un “contenedor Alpine”
  • Estabilidad de la interfaz de system calls

    • El ABI de syscalls de Linux es muy estable
    • Razones por las que un binario compilado con glibc 2.31 (Ubuntu 20.04) funciona también sobre un kernel de Ubuntu 24.04:
      • El kernel mantiene compatibilidad hacia atrás
      • Los números de system call no cambian
      • Se agregan nuevas funciones, pero rara vez se eliminan las existentes
    • Por eso puede ejecutarse un contenedor de Ubuntu 18.04 en un host con kernel 6.5

Hagamos una prueba: mismo kernel, distinto espacio de usuario

  • Si consultas el mismo dato del kernel en varias imágenes base, puedes ver que todas comparten el kernel del host
  • ubuntu:22.04, debian:12, alpine:3.19, fedora:39 y archlinux:latest muestran la misma versión de kernel (6.5.0-44-generic)
  • Lo que cambia entre contenedores es el userland, como el binario uname y la libc

Por qué los contenedores son tan eficientes

  • 1. No hay duplicación del kernel

    • Cada VM carga un kernel completo en memoria (unos 100-500 MB)
    • 10 VM consumen memoria para 10 kernels; 10 contenedores usan un solo kernel
  • 2. Arranque instantáneo

    • Secuencia de arranque de una VM: BIOS → bootloader → kernel → sistema init → servicios
    • Un contenedor existe en milisegundos solo con llamadas a fork() y exec()
    • Arranque típico de una VM: 30-60 s / inicio de contenedor: aprox. 0.347 s
  • 3. Capas de imagen compartidas

    • Si ejecutas 100 contenedores desde ubuntu:22.04, la capa base de la imagen solo existe una vez en disco
    • Cada contenedor obtiene únicamente una capa delgada copy-on-write para sus cambios
  • 4. Memoria compartida a través del kernel

    • Se comparte la page cache del kernel
    • Si 50 contenedores leen el mismo archivo, el kernel lo cachea una sola vez
    • Al usar las mismas bibliotecas compartidas, también es posible compartir páginas de memoria con copy-on-write

Cálculo del límite de contenedores que pueden ejecutarse

  • Análisis de memoria (VM con 16 GB de RAM)

    • RAM total: 16,384 MB
    • Overhead del SO host: -1,024 MB
    • Daemon de Docker: -256 MB
    • Overhead del runtime de contenedores: -512 MB
    • Capacidad disponible para contenedores: 14,592 MB
  • Uso de memoria por tipo de contenedor

    • Mínimo (sleep): aprox. 1 MB
    • Alpine + app pequeña: aprox. 25 MB
    • Ubuntu + app en Python: aprox. 120 MB
    • Ubuntu + app en Java: aprox. 500 MB
    • Servicio Node.js: aprox. 200 MB
  • Máximo teórico

    • Contenedor mínimo (1 MB): 14,592
    • Alpine + app pequeña (25 MB): 583
    • Ubuntu + Python (120 MB): 121
    • Microservicio Java (500 MB): 29
  • Límites reales

    • Además de la memoria, hay que considerar:
      • Planificación de CPU: demasiados contenedores compitiendo generan picos de latencia
      • Descriptores de archivo: ulimit por defecto 1024
      • Puertos de red: solo se pueden mapear 65,535 puertos
      • PIDs: límite de /proc/sys/kernel/pid_max (por defecto: 32,768)
      • I/O de disco: overhead de OverlayFS y necesidad de recorrer muchas capas
    • En una VM de 16 GB, los límites prácticos para workloads reales suelen ser:
      • Contenedores livianos (API, workers): 50-100
      • Contenedores medianos (DB, caché): 10-30
      • Contenedores grandes (modelos ML, apps JVM): 5-10

Compatibilidad entre distribuciones Linux

  • La promesa del ABI del kernel

    • Linux mantiene una interfaz de syscalls estable
    • Un binario compilado para un kernel viejo puede funcionar sobre uno nuevo
    • Un binario de Ubuntu 18.04 corre sin problema sobre kernel 6.5
  • Cuándo se rompe la compatibilidad

    • Requisitos de funciones del kernel: si el contenedor necesita una función que el kernel no tiene (por ejemplo, io_uring requiere kernel 5.1+)
    • Dependencias de módulos del kernel: Wireguard requiere el módulo wireguard del kernel; los contenedores NVIDIA requieren drivers nvidia en el kernel
    • Restricciones de seccomp/capabilities: si el host bloquea una syscall necesaria para el contenedor (por ejemplo, para usar ptrace se necesita --cap-add SYS_PTRACE)

Guía para elegir imagen base

Imagen base Tamaño Gestor de paquetes Uso
scratch 0 MB ninguno binarios Go/Rust compilados estáticamente
alpine 7 MB apk contenedores mínimos, musl libc
distroless 20 MB ninguno orientado a seguridad, sin shell ni gestor de paquetes
debian-slim 80 MB apt equilibrio entre tamaño y compatibilidad
ubuntu 78 MB apt facilidad para desarrollo
fedora 180 MB dnf paquetes recientes, SELinux
  • Cuándo usar cada imagen

    • scratch: para binarios compilados estáticamente; incluye solo el binario y nada de SO
    • alpine: imagen mínima cuando necesitas acceso por shell; usa musl libc en lugar de glibc, así que puede haber problemas de compatibilidad
    • distroless: imagen de producción orientada a seguridad; al no tener shell ni gestor de paquetes, es más difícil de depurar, pero más segura

El límite entre espacio de usuario y kernel

  • Lo que viene de la imagen base (espacio de usuario)

    • Shell (/bin/bash, /bin/sh)
    • Biblioteca C (glibc, musl)
    • Gestor de paquetes (apt, apk, yum)
    • Utilidades básicas (ls, cat, grep)
    • Configuración del sistema init (normalmente no incluye systemd como tal)
    • Usuarios y grupos por defecto (/etc/passwd)
    • Rutas de bibliotecas y configuración
  • Lo que viene del host (kernel)

    • Planificación de procesos y gestión de memoria
    • Stack de red (TCP/IP, routing)
    • Operaciones de sistema de archivos (lectura, escritura, montaje)
    • Funciones de seguridad (namespaces, cgroups, seccomp)
    • Drivers de dispositivos (GPU, red, almacenamiento)
    • Gestión de tiempo y reloj
    • Criptografía y generación de números aleatorios
  • La ilusión que crean los namespaces

    • El kernel provee namespaces para que el contenedor parezca aislado
    • Un proceso que dentro del contenedor se ve como PID 1 puede existir en el host con un PID mucho mayor (por ejemplo, 45678)
    • El kernel mantiene el mapeo: PID 1 del contenedor → PID 45678 del host
    • Así es como el aislamiento funciona sin virtualización

Qué significa esto en producción

  • 1. Una vulnerabilidad del kernel afecta a todos los contenedores

    • Si el kernel del host tiene una vulnerabilidad, todos los contenedores quedan expuestos
    • Mantener el host parchado es indispensable
  • 2. El kernel del host limita las funciones del contenedor

    • Para usar io_uring, el host necesita kernel 5.1+
    • Las funciones de eBPF requieren kernel 4.15+ con ciertas opciones habilitadas
  • 3. Importa glibc vs musl

    • Alpine usa musl libc
    • Algunos binarios compilados para glibc pueden no funcionar
    • Ejemplo: al ejecutar un binario de glibc en Alpine, puede aparecer un error indicando que falta /lib/x86_64-linux-gnu/libc.so.6
  • 4. El “SO” del contenedor es una noción puramente organizativa

    • Desde la perspectiva del kernel, un “contenedor Ubuntu” y un “contenedor Debian” no tienen diferencia
    • Ambos son solo procesos que hacen syscalls

Malentendidos comunes

  • ❌ “Los contenedores son VM livianas”: los contenedores son procesos con aislamiento avanzado; las VM virtualizan hardware y ejecutan un kernel separado
  • ❌ “Cada contenedor tiene su propio kernel”: todos los contenedores comparten el kernel del host; el “SO” del contenedor son solo archivos de espacio de usuario
  • ❌ “Ejecutar un contenedor Ubuntu = ejecutar Ubuntu”: en realidad ejecutas herramientas de Ubuntu sobre el kernel del host; si el host es Debian, estás usando un kernel Debian
  • ❌ “La imagen base incluye un sistema operativo completo”: la imagen base solo incluye herramientas mínimas de espacio de usuario; no trae kernel, bootloader ni drivers
  • ❌ “Más contenedores = más memoria”: gracias a las capas compartidas y al page caching del kernel, los contenedores muchas veces comparten memoria de forma eficiente

Resumen clave

  • Una imagen base de Docker es un snapshot del sistema de archivos con los componentes de espacio de usuario de una distribución Linux
    • Binarios, bibliotecas y configuración que hacen que Ubuntu “se sienta” como Ubuntu
  • El verdadero sistema operativo, el kernel, se comparte con el host
  • Esta arquitectura hace posible:
    • Tiempos de inicio en milisegundos (sin arrancar kernel)
    • Overhead de memoria mínimo (un solo kernel, páginas compartidas)
    • Alta densidad (cientos de contenedores por host)
    • Rendimiento cercano al nativo (syscalls directas al kernel)
  • El trade-off es un aislamiento más débil que el de una VM: como los contenedores comparten kernel, un exploit del kernel afecta a todos los contenedores
  • Para la mayoría de los workloads, este trade-off vale la pena

9 comentarios

 
bbulbum 2026-01-22

Núcleo + herramientas = distribución
Entonces, ¿esto también cuenta como Ubuntu, no?

 
sacredshine 2026-01-21

Así que también hay tutoriales sobre crear Docker directamente en Linux, aislando directorios y haciendo varias cosas con usuarios y grupos.

 
dongho42 2026-01-21

Es útil.

 
seunggi 2026-01-21

리눅스 네임스페이스, cgroups, 및 chroot를 사용하여 자체 Docker를 구축하세요.

Así que, dicho de forma un poco exagerada, creo que se puede ver como chroot + cgroup = docker.

 
euphcat 2026-01-21

Acktuallly, eso se parece un poco más a systemd-nspawn ☝️🤓

 
hohemian 2026-01-22

En realidad

 
euphcat 2026-01-22

Sarcasmo / Autodesprecio

 
crawler 2026-01-21

Es realmente fascinante.

Parece que el texto lo explica tomando como referencia Linux, pero
si se ejecuta en Windows, entonces se estaría compartiendo un kernel virtual creado con WSL2, como dice el artículo, ¿verdad?

Si apareciera una vulnerabilidad en Docker que permitiera tocar el kernel, también me da curiosidad si habría que considerar que Windows, al añadir una capa extra de virtualización frente a Linux, sería más fuerte en seguridad.

 
minsuchae 2026-01-22

Me sorprendió un poco la reacción del comentario de arriba.
Yo daba por hecho que lo estaban usando sabiendo eso.
El kernel de Linux lo pone el host, y el resto viene de las herramientas que se usan en la distribución de Linux.

Hasta donde sé, WSL2 corre virtualizado sobre Hyper-V.
Windows - Linux en una máquina virtual - y dentro de eso otra vez un contenedor...

Básicamente, como el root dentro del contenedor no es el verdadero root de todo el sistema, en principio no puede manipular el kernel arbitrariamente.
Pero si aparece una vulnerabilidad, sí puede ser grave.

Desde el punto de vista del rendimiento, en Windows tiende a ser más lento precisamente porque pasa por una capa adicional de virtualización.