Docker es una de las herramientas más populares en los últimos tiempos para gestionar entornos de desarrollo y productivos. Su concepción y uso resulta en una forma muy distinta de pensar la forma en la que distribuimos nuestro software y cómo configuramos los ambientes para que todo funcione perfecto. Tan importante resulta, que decidí crear el "Manifiesto de Docker", para explicar la esencia de esta herramienta. Pero antes de llegar alManifiesto, conviene tener en claro los conceptos iniciales. ¿Qué resuelve Docker? ¿Para qué surgió? ¡Vamos allá!
Un poquito de contexto
Uno de los problemas más comunes en el software es que el código que escribimos funciona diferente al ejecutarse en distintos ambientes. El motivo es que sencillamente el ambiente de desarrollo (como puede ser nuestra propia computadora) y el de producción (por ejemplo, un servidor dedicado), son distintos. Y si bien algunos lenguajes actuales como Java o .Net ofrecen soluciones para abstraernos del sistema operativo, del manejo de memoria y del trabajo a bajo nivel, sigue existiendo la posibilidad de que de los entornos no sean enteramente compatibles con el código o los tests que se hagan, debido a factores como versiones, configuraciones u otras circunstancias.
Como desarrolladores ya estamos acostumbrados a no tenemos control sobre el hardware ni sistema operativo del entorno productivo, y sólo en algunos casos contamos con información limitada al respecto. Por ejemplo: que es un Ubuntu (pero no si es 12, 14, o 16), que soporta Python y PHP (pero no qué versiones, o con qué parches), etc. ¿Y qué pasa si el entorno de producción no es capaz de ejecutar nuestro código?
En el mejor de los casos, aún teniendo algún tipo de control sobre el ambiente productivo, tal vez podamos administrar su configuración, pero mantenerla a lo largo del tiempo podría volverse una disciplina en sí misma, sin mencionar los aspectos de seguridad y control de acceso que dicha responsabilidad conlleva.
Y entonces, ¿qué hacer?
Entonces… un día, un tal Solomon (no el rey Salomón, sino Solomon Hykes), junto a otros ingenieros de la empresa dotCloud, se juntaron a discutir los conflictos y problemas comentados previamente. Y es así que dan a luz a un proyecto llamado Docker, el cual tuvo como objetivo principal permitir la creación de paquetes estándar pensados para despliegue, llamados contenedores, que incluyeran todo lo necesario para que una aplicación funcione (dependencias, servicios, configuraciones, etc).
¡Sí, claro! Como una Máquina Virtual...
No al 100%. Si bien los conceptos son muy similares, un contenedor es más ligero, ya que mientras que a una máquina virtual necesitas instalarle un sistema operativo para funcionar, un contenedor de Docker funciona utilizando el sistema operativo que tiene la máquina en la que se ejecuta el contenedor.
Digamos que el contenedor de Docker toma los recursos más básicos que no cambian de un ordenador a otro y los aspectos más específicos del sistema que pueden dar más problemas a la hora de llevar el software de un lado a otro y los meten en el interior del contenedor. Esto hace que hace tan ligero a los contenedores Docker.
Docker brinda soluciones
Eficiencia
Una característica de Docker es su eficiencia. Esto es gracias a su sistema de archivos por capas, donde cada capa sólo contiene la diferencia entre la anterior y la actual a nivel file system (que ocupan sólo lo justo), y al mover imágenes sólo se transfieren las capas que no existen en el repositorio destino.
Administración
Otra característica es la administración de dependencias funcionales de nuestra aplicación (base de datos, etc.). Instalar, configurar, actualizar este tipo de servicios suelen ser tareas, cuando menos, engorrosas. El setup es usualmente específico para cada sistema operativo, algunas funcionalidades tienen limitaciones en dependiendo del entorno, arquitectura o file system, y muchas veces terminamos haciendo retoques aquí y allá que no quedan persistidos o documentados en ningún lado, y que luego olvidamos para sólo recordar cuando nos damos cuenta que algo “anda raro” en producción (luego de un rato de debugging). Y algo peor a que nuestra aplicación no funcione: ¡que funcione distinto a como esperamos y sin que entendamos por qué! Resulta muy valioso contar con una forma de poder administrar estos servicios de manera práctica, y Docker lo hace fácil.
Arquitectura como código
Cualquier aplicación que crece se vuelve cada vez más compleja, y no sólo hacia adentro (su propio código y lógica) sino también hacia afuera. De una pequeña web app monolítica pasamos a una Single Page Application que se construye y sirve con Webpack, que depende de una API en Node.js que depende de MongoDB, y otra en ruby que hace uso de MySQL y… así es como tenemos una arquitectura con un diseño complejo.
Proyectos de este tamaño suelen ser desarrollados por distintos equipos, por lo que la comunicación efectiva se vuelve un factor crítico, y donde cualquier malentendido o cambios propios de cada miembro pueden causar problemas en el rendimiento del grupo. Lograr que todo el equipo esté al tanto del estado actual y entienda cómo funciona la aplicación en su totalidad se vuelve algo muy difícil de lograr.
Gracias a docker-compose y su forma sencilla de especificar los distintos servicios que conforman una aplicación, no sólo se vuelve sencillo para los desarrolladores levantar, bajar y configurar cada una de las partes de la aplicación, sino que al tratarse de un archivo de texto (en formato YAML) pasa a formar parte del sistema de versionado de código y así queda persistida en el tiempo la evolución de la misma commit a commit, haciendo que entender o repasar la arquitectura completa sea tan práctico como leer un archivo y listo. Y como bonus, docker-compose permite hacer funcionar toda esa complejidad con un simple docker-compose up! Como diría José María Listorti: ¡MÁGICO!
El Manifiesto de Docker
¡Y llegamos finalmente al Manifiesto! Como conclusión quiero resaltar que, si reflexionamos sobre lo que logra cada uno de los aspectos mencionados, se ve claramente que todos apuntan estos aspectos a lo mismo. Y es así que podemos detallar finalmente mi versión del Manifiesto de Docker:
- Interacción entre los miembros del equipo por sobre las islas de conocimiento.
- Preocupación por escribir código que agregue valor a nuestro producto por sobre preocupación en configuraciones de sistemas, entornos o ambientes.
- Efectuar un cambio en cualquier componente sin afectar al resto.
- Hacer crecer al software de una manera no apocalíptica.
No será tan popular como el Manifiesto Ágil, pero sirve para explicar el espíritu de Docker, ¿no? :)