Desmitificando namespaces y contenedores en Linux

Exploraremos en segundo plano para comprender el back-end de la tecnología de contenedores Linux.

Los contenedores han invadido el mundo. Ya sea que pienses en Kubernetes, Docker, CoreOS, Silverblue o Flatpak. Cuando escuches el término, está claro que las aplicaciones modernas se ejecutan en contenedores para mayor comodidad, seguridad y escalabilidad.

Sin embargo, los contenedores pueden ser confusos de entender. ¿Qué significa ejecutar en un contenedor? ¿Cómo pueden los procesos en un contenedor interactuar con el resto de la computadora en la que se están ejecutando?

Al código abierto no le gusta el misterio, por lo que este artículo explica el backend de la tecnología de contenedores.

Namespaces

Los espacios de nombres son comunes en el mundo de la programación. Si trabajas en los lugares altamente técnicos del mundo de la informática, entonces probablemente hayas visto un código como este:

O puedes que hayas visto esto en XML:

Este tipo de frases proporcionan contexto para los comandos utilizados más tarde en un archivo de código fuente. La única razón por la que C++ sabe, por ejemplo, qué quieren decir los programadores cuando escriben cout es porque C++ sabe que el namespace cout es una palabra significativa.

Si es demasiado técnico para que lo imagines, te sorprenderá saber que todos usamos namespace todos los días en la vida real también. No los llamamos espacio de nombres, pero usamos el concepto todo el tiempo.

Por ejemplo, la frase “Soy un fanático de la empresa” tiene un significado en una compañía de informática que ofreces grandes negocios (que comúnmente se llaman “empresas”), pero puede tener un significado diferente en una convención de ciencia ficción.

La pregunta “¿qué motor está trabajando?” tiene un significado en un garaje y un significado diferente en el desarrollo web. No siempre declaramos un espacio de nombres en una conversación informal porque somos humanos, y nuestros cerebros pueden adaptarse rápidamente para determinar el contexto. Empero, para las computadoras, el espacio de nombres debe declararse explícitamente.

Para los contenedores, un namespace es lo que define los límites de la “conciencia” de un proceso de qué más se está ejecutando a su alrededor.

lsns

Puede que no te des cuenta, pero tu máquina Linux mantiene silenciosamente diferentes namespaces específicos para procesos dados. Al usar una versión reciente del paquete util-linux, puedes listar los namespaces existentes en tu máquina:

Si tu versión de util-linux no proporciona el comando lsns, puedes ver las entradas del espacio de nombres en /proc:

Cada proceso que se ejecuta en tu máquina Linux se enumera con un ID de proceso (PID). A cada PID se le asigna un espacio de nombres.

Los PID en el mismo espacio de nombres pueden tener acceso entre sí porque están programados para operar dentro de un espacio de nombres dado.

Los PID en diferentes espacios de nombres no pueden interactuar entre sí de manera predeterminada porque se ejecutan en un contexto o namespace diferente.

Esta es la razón por la cual un proceso que se ejecuta en un “contenedor” bajo un namespaces no puede acceder a información fuera de su contenedor.  O inclusive información que se ejecuta dentro de un contenedor diferente.

Crear un nuevo namespace

Una característica habitual del software que trata con contenedores es la gestión automática del namespace.

Un administrador humano que inicie una nueva aplicación o entorno en contenedor no tiene que usar lsns para verificar qué espacios de nombres existen y luego crear uno nuevo manualmente.

El software que usa namespaces PID lo hace automáticamente con la ayuda del kernel de Linux. Sin embargo, puede imitar el proceso manualmente para obtener una mejor comprensión de lo que sucede en segundo plano.

Primero, debes identificar un proceso que no se está ejecutando en tu computadora.

Para este ejemplo, usaré la shell Z (Zsh) porque estoy ejecutando la shell Bash en mi máquina. Si estás ejecutando Zsh en tu computadora, debes usar Bash o tcsh o algún otra shell que no estés ejecutando actualmente.

El objetivo es encontrar algo que puedas probar que no se está ejecutando. Puedes probar que algo no se está ejecutando con el comando pidof, que consulta tu sistema para descubrir el PID de cualquier aplicación que nombres:

Mientras no te devuelva ningún PID, la aplicación que has consultado no se está ejecutando.

Unshare

El comando unshare ejecuta un programa en un espacio de nombres no compartido de su proceso padre. Hay muchos tipos de espacios de nombres disponibles, así que debes leer la página de manual de unshare para ver todas las opciones disponibles.

Para crear un nuevo espacio de nombres para tu comando de prueba:

Debido a que Zsh es una shell interactivo, convenientemente te trae a su espacio de nombres al momento de la ejecución. No todos los procesos hacen eso, porque algunos procesos se ejecutan en segundo plano, dejándote en el indicador en su espacio de nombres nativo. Mientras permanezcas en la sesión de Zsh, puedes ver que ha dejado el espacio de nombres habitual viendo el PID de tu nuevo proceso bifurcado:

Si sabes algo acerca de las ID de proceso de Linux, entonces sabes que el PID 1 siempre está reservado.  Generalmente por la naturaleza del proceso de arranque, para la aplicación de inicialización (systemd en la mayoría de las distribuciones que no son Slackware, Devuan y quizás algunas instalaciones personalizadas de Arch).

Es casi imposible para Zsh, o cualquier aplicación que no sea una aplicación de inicialización de arranque, ser PID 1 (porque sin un sistema init, una computadora no sabría cómo arrancar). Sin embargo, hasta donde tu shell sabe en esta demostración, Zsh ocupa la ranura PID 1.

A pesar de lo que tu shell te está diciendo, el PID 1 en tu sistema no ha sido reemplazado. Debes abril una segunda terminal o pestaña de terminal en tu computadora y ver el PID 1:

Y luego debes encontrar el PID de Zsh:

Resultado

Como puedes ver, tu sistema “host” ve el panorama general y entiende que Zsh realmente se está ejecutando como un PID de número alto (probablemente no será 7723 en tu computadora, excepto por coincidencia).

Zsh se ve a sí mismo como PID 1 solo porque su alcance está limitado (o contenido) a su espacio de nombres. Una vez que haya bifurcado un proceso en su propio espacio de nombres, sus procesos secundarios se numeran a partir del 1, pero solo dentro de ese espacio de nombres.

Namespaces, junto con otras tecnologías como cgroups y más, forman la base de la contenedorización. Comprender que los namespaces existen dentro del contexto del espacio de nombres más amplio de un entorno host (en esta demostración, esa es tu computadora, pero en el mundo real el host suele ser un servidor o una nube híbrida) puede ayudarte a comprender cómo y por qué actúan las aplicaciones en contenedores como lo hacen ellos.

Por ejemplo, un contenedor que ejecuta un blog de WordPress no “sabe” que no se está ejecutando en un contenedor. Sabe que tiene acceso a un núcleo y algo de RAM y cualquier archivo de configuración que le hayas proporcionado, pero probablemente no puede acceder a tu directorio de inicio. De igual manera cualquier directorio al que no le hayas dado permiso específicamente para acceder.

Además, un proceso desbocado dentro de ese software de blogs no puede afectar a ningún otro proceso en su sistema. Esto porque hasta donde sabe, el “árbol” PID solo vuelve a 1, y 1 es el contenedor en el que se está ejecutando.

Conclusión 

Los contenedores son una poderosa característica de Linux, y se están volviendo más populares cada día. Ahora que comprendes cómo funcionan, intenta explorar la tecnología de contenedores como Kubernetes o Silverblue, y observa qué puedes hacer con las aplicaciones en contenedores. Los contenedores son Linux, así que inícialos, inspecciónalos cuidadosamente y aprende sobre la marcha.