Quizá hayas escuchado hablar de Docker alguna vez. Se trata de una tecnología que promete permitir ejecutar aplicaciones y programas de ordenador en un lugar conocido como contenedor. A menudo lo usan para ejecutar aplicaciones de red que se ejecutan en servidores, pero en realidad puedes usarlo en tu propio ordenador personal para instalar programas como bases de datos, o aplicaciones alojadas que tengan interfaz web, de forma rápida y limpia.
Docker facilita el uso de cgroups
Docker se aprovecha de una tecnología presente en el núcleo Linux denominada cgroups. Esta tecnología permite limitar la cantidad de recursos que recibe un proceso en Linux, como el tiempo de CPU que recibe, la cantidad de RAM que puede usar, o incluso directorios de sistema a los que puede entrar.
Lo normal en tu ordenador es que cuando ejecutes un programa, éste tenga acceso a todos los recursos de la máquina. Eso significa que un proceso podría empezar a buscar el último decimal de pi y empezar a consumir un porcentaje importante de CPU que deje hambrientos otros programas en ejecución. O podría reservar suficiente memoria RAM como para dejar tiritando tu sistema. O entrar a cualquier directorio del disco duro para el que tenga permiso y consultar información.
Con cgroups, precisamente puedes aislar un proceso o grupo de procesos para limitar la cantidad máxima de CPU y memoria RAM que van a poder utilizar, e incluso separar la interfaz de red que usan para acceder a internet o cambiar su directorio raíz, de tal forma que sólo vean una porción pequeña del sistema de archivos para impedir que se metan donde no toca. También se puede limitar el uso de ciertas llamadas a sistema, como la posibilidad de que tengan su propia tabla de procesos en ejecución, para que no puedan ver qué procesos tienes en ejecución en tu máquina.
Y Docker precisamente lo que hace es aprovecharse de estas características y crear una forma conveniente de iniciar aplicaciones metidas en uno de esos cgroups. Para ello lo primero que hace es utilizar imágenes de Docker, que puedes ver precisamente como el conjunto de archivos que definirán el sistema de archivos virtual y limitado que verá Docker. Aquí es donde meterás los archivos que permiten funcionar tu programa. Esas imágenes también llevan metadatos, como el usuario con el que se debe ejecutar la imagen, o las variables de entorno a definir al lanzar el programa.
Una vez que tengas una imagen, puedes pedirle a Docker que la lance, creando un contenedor. Un contenedor es una ejecución de una imagen, y se llama así porque, al igual que un contenedor en un barco que transporta mercancías, todo lo que hay dentro de un contenedor está aislado de otros contenedores, y a menudo apilado en un gran sistema lleno de contenedores.
Pero una cosa muy simple que tienes que tener en cuenta es que lanzar un contenedor es simplemente una forma un poco bruta de pedirle a GNU/Linux que ejecute un programa, manteniendo el proceso bajo unas condiciones muy concretas y limitadas como las que contaba antes. La ventaja es que la operación es mucho más simple porque no hay que interactuar con cgroups a mano.
Docker no es virtualización
Es importante diferenciar Docker y cgroups de la virtualización. Cuando virtualizas, lo que haces es usar un programa que simula ser todo un ordenador completo, creando una máquina virtual. Hoy en día las máquinas virtuales son más rápidas gracias al uso de hipervisores y a cierta colaboración que ofrece la CPU del ordenador, pero esencialmente cuando instalas una distribución GNU/Linux en una máquina virtual, estás ejecutando un segundo kernel, simulado dentro de un programa que corres dentro de tu ordenador.
Pero al usar Docker, compartes el kernel de tu ordenador o servidor. En otras palabras, el mismo kernel que atiende las llamadas a sistema en los programas que ejecutas dentro de Docker es el que utilizas en tu máquina. Esta es la razón por la que si pruebas a ejecutar uname
dentro de un contenedor de Docker en el que ese comando esté disponible, siempre va a reportar la misma versión del núcleo Linux que la que use tu máquina física, incluso aunque te lleves esa imagen de Docker a otros entornos con otras versiones de núcleo Linux.
Un contenedor de Docker puede pararse y reanudarse mediante operaciones como start y stop. Sin embargo, cuando detienes un contenedor de Docker, no lo estás congelando en el tiempo, que es lo que harías con una máquina virtual si suspendes su ejecución. Cuando detienes un contenedor de Docker verdaderamente detienes todos los procesos en ejecución, que se reanudarán la siguiente vez que se encienda el container.
Los overlays: el punto fuerte de Docker
Uno de los puntos fuertes de Docker es su sistema de overlays. Un overlay es una capa del sistema de archivos que se pone encima de otra, para agregar o quitar cosas.
Por ejemplo, si tienes un sistema de archivos en tu imagen de Docker que contiene 3 archivos, pero luego le pones encima usando un overlay otro sistema de archivos que contiene 2 archivos, al final solaparán y acabarás teniendo una imagen con 5 archivos. Puedes usar los overlays para agregar, modificar y quitar archivos.
Esto es fuerte porque permite crear imágenes reusables. Por ejemplo, existe una imagen de Docker llamada debian:slim
, que contiene una instalación pequeña de Debian, con una shell y las herramientas mínimas para usar Docker. Mediante overlay, puedes agregar en otra imagen tus propios archivos de programa a la imagen debian:slim
, y el resultado es una imagen que tiene Debian y tus archivos.
Lo normal es que una imagen final de Docker tenga varios overlays, a medida que se usan capas. Por ejemplo, la capa con Debian, la capa con un entorno de ejecución, y la capa con los archivos de programa. A este sistema, se le debe sumar también el volumen que guarda los archivos locales cuando ejecutas una imagen de Docker, para asegurarse de que algunas cosas no se pierdan cuando se detiene la ejecución de una imagen.
¿Por qué se usa tanto Docker?
Docker ha sido adoptado como una tecnología clave moderna para la industria informática y de servidores precisamente por la conveniencia a la hora de fabricar y compartir las imágenes.
Típicamente, instalar un programa en un servidor requería instalarlo a mano y configurarlo. Pero a veces, ese programa depende de paquetes que faltan en el gestor de paquetes del servidor, o pide versiones de una librería que son diferentes de las que aporta el sistema operativo.
Al usar una imagen de Docker, congelamos todo el entorno en el que se va a ejecutar el programa, y eso incluye tanto la distribución, como la versión de la distribución, como otras bibliotecas. Podrías usar la imagen de Docker de Fedora para fabricar una imagen de Docker de un programa que se ejecute sobre Fedora, y como la imagen ya trae todo lo necesario para arrancar el userland de Fedora, luego ejecutarlo en un entorno Docker en Debian, y funcionará de forma transparente.