El caso

Funcionaba en mi máquina. En el servidor, no.

Isaac desarrolló el sistema de pedidos con una versión de Python en su notebook. En el servidor había otra versión, y faltaba una biblioteca. Pasó la tarde instalando dependencias a mano, y cada servidor nuevo repetía el problema.

Una VM por aplicación lo resolvería, pero cada una cargaría un sistema operativo entero solo para ejecutar un programa.

Este post parte de Kernel: el núcleo del sistema operativo, de Procesos e hilos: cómo el sistema ejecuta tus programas y de Máquinas virtuales.

El problema

El programa depende del entorno

Un programa no corre solo: depende de una versión de lenguaje, de bibliotecas y de configuraciones del sistema. Cuando el entorno de desarrollo y el de producción son distintos, el mismo código se comporta de formas distintas.

La VM resuelve la separación, pero con un costo: cada aplicación trae su propio kernel y su propio sistema operativo, con su memoria, su disco y su tiempo de arranque.

La solución

Contenedor: un proceso aislado

Un contenedor es un proceso común, que corre sobre el kernel del propio servidor, con dos cosas que lo aíslan: lo que puede ver y lo que puede usar. No hay un segundo kernel. Por eso un contenedor arranca en segundos y necesita poca memoria extra.

Namespaces (lo que ve)

Cada contenedor tiene su propia visión de procesos, red, puntos de montaje y usuarios. Dentro de él, el programa cree que está solo.

En la tienda: el sistema de pedidos ve solo sus propios procesos y su propio puerto de red.

cgroups (lo que puede usar)

El kernel limita cuánta CPU y memoria puede consumir el grupo de procesos.

En la tienda: el informe no puede quitarle la memoria que necesita el sistema de pedidos.

Imagen (el paquete)

Un paquete con el sistema de archivos y las dependencias de la aplicación, que corre igual en cualquier servidor.

En la tienda: el mismo paquete corre en el notebook de Isaac y en el servidor.

No los confundas

Contenedor × VM

Contenedor

“¿Necesito otro kernel, o solo un proceso aislado?”

Ejemplo
Diez instancias del sistema de pedidos en el mismo servidor, compartiendo el kernel.
Foco
Arranca en segundos, usa poca memoria extra, pero el kernel es compartido.

Máquina virtual

“¿Necesito otro sistema operativo, o un aislamiento más fuerte?”

Ejemplo
Un sistema Windows corriendo en un servidor Linux, o clientes que no pueden compartir el kernel.
Foco
Cada VM tiene su kernel. Aislamiento más fuerte, con más costo de memoria y de arranque.

Por dentro

Namespaces y cgroups

Los namespaces y los cgroups son funciones del kernel de Linux. Un contenedor es, en la práctica, un proceso que el kernel colocó dentro de un conjunto de namespaces y de un cgroup.

  • Namespace de procesos: el contenedor ve solo sus propios procesos, y su proceso principal aparece con un número propio, como el PID 1.
  • Namespace de red: cada contenedor tiene sus propias interfaces y puertos.
  • Namespace de montajes: el contenedor ve su propio sistema de archivos, montado a partir de la imagen.
  • cgroup: define los límites de CPU y de memoria, y cuenta lo que consume el grupo.

Como todo esto ocurre en el kernel, un contenedor que supera su memoria es terminado por el kernel, igual que un proceso común, y no tumba el servidor.

Empaquetar

Imagen y registro

Una imagen es un paquete inmutable: las capas del sistema de archivos y la configuración para iniciar el programa. Cada capa se reutiliza: dos imágenes que parten de la misma base comparten esa base en el disco.

Un contenedor es una instancia en ejecución de una imagen. Recibe una capa escribible encima, y todo lo que escribe en ella desaparece cuando el contenedor se elimina.

Las imágenes se guardan en un registro (registry), de donde los servidores las descargan. Quien necesita que los datos sobrevivan al contenedor usa un volumen, un área de almacenamiento montada en él.

En la práctica

Limitar y ejecutar

Con Docker, una de las herramientas más conocidas, un comando inicia un contenedor con límites de memoria y de CPU:

bash
docker run --memory 512m --cpus 1 nginx

El comando descarga la imagen de nginx, crea los namespaces, aplica el cgroup e inicia el proceso. Un contenedor que consume más de 512 MB es terminado, y los demás contenedores del servidor siguen corriendo.

SituaciónElección
Varios servicios en Python con versiones distintas, en el mismo servidorContenedores
Un sistema Windows y uno Linux en el mismo hardwareVM
Datos de una base que tienen que sobrevivir al contenedorVolumen, o un servicio de base gestionado
Actualizar la aplicación sin tocar el servidorCambiar la imagen y reiniciar el contenedor

Términos clave

Seis términos para recordar

Toca la tarjeta para ver la definición.

Prueba rápida

Tres preguntas, estilo examen

01

Dos contenedores corren en el mismo servidor. ¿Qué comparten?

02

Un equipo quiere que una base de datos conserve sus archivos después de eliminar el contenedor. ¿Qué debe usar?

03

¿Qué función del kernel limita cuánta memoria puede usar un contenedor?


El contenedor resuelve el problema de empaquetar y aislar una aplicación sin pagar un sistema operativo entero por ella. Entender los namespaces y los cgroups muestra qué está aislado y qué no, y por eso la elección entre contenedor y VM depende del riesgo que aceptas y del sistema operativo que necesitas. El próximo paso es ver cómo corren estas piezas en la nube.