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:
docker run --memory 512m --cpus 1 nginxEl 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ón | Elección |
|---|---|
| Varios servicios en Python con versiones distintas, en el mismo servidor | Contenedores |
| Un sistema Windows y uno Linux en el mismo hardware | VM |
| Datos de una base que tienen que sobrevivir al contenedor | Volumen, o un servicio de base gestionado |
| Actualizar la aplicación sin tocar el servidor | Cambiar 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.