Qué es Docker: Análisis detallado
Actualizado el
a las
Docker se ha posicionado como una herramienta fundamental en el desarrollo de software, ofreciendo un alto grado de flexibilidad y facilidad de uso. Estas características han consolidado a esta tecnología como una solución indispensable en diversos escenarios, desde la migración de servidores hasta la homogeneización de entornos de desarrollo.
Publicado el
a las
Qué es Docker
Docker es un conjunto de herramientas de código abierto para crear, gestionar, desplegar o compartir contenedores. Entre ellas están Docker Compose, Docker Hub, Docker CLI y Docker Engine, que proporcionan una capa de abstracción que facilita la gestión de contenedores en comparación a otras soluciones como LXC.
Esta capa de abstracción es uno de los principales valores añadidos que ofrece Docker. Para una mayor comprensión también es necesario saber qué es un contenedor, cuál es la arquitectura detrás de Docker y qué diferencia a un contenedor de una máquina virtual entre otras cosas.
Qué es un contenedor
En informática un contenedor es software empaquetado junto a todas las dependencias necesarias para ejecutarse de forma aislada del sistema anfitrión, lo que los convierte en una herramienta de utilidad en múltiples escenarios.
Una de las características más interesantes es que se ejecutan de manera independiente del resto de procesos, de otros contenedores y del entorno, sea este un servidor, una máquina local o incluso sistemas operativos tan diversos como GNU/Linux, Windows o macOS.
Los contenedores no son un concepto nuevo, sin embargo la propuesta de Docker transformó la forma de trabajar con ellos. Antes de Docker crear contenedores era más complejo y requería conocimientos sobre herramientas como LXC, utilizadas con el mismo fin pero a un más bajo nivel que Docker.
Diferencias entre contenedores y máquinas virtuales
A diferencia de las máquinas virtuales, los contenedores son más ligeros porque comparten el Kernel del sistema anfitrión y solo incluyen las dependencias necesarias, lo que reduce el espacio que ocupan en disco y optimiza mejor el uso de los recursos del sistema.
Por otro lado, las máquinas virtuales necesitan un sistema operativo completo y contar con recursos propios asignados; esto hace que además de ser más pesadas, sean menos portátiles. En general consumen más recursos que un contenedor.
Sin embargo tanto contenedores como máquinas virtuales tienen pros y contras, así como también distintos casos de uso. En el caso de los contenedores que tienden a ser más ligeros y a compartir el kernel con el sistema anfitrión, son más propensos a comprometer el sistema si un atacante detecta una vulnerabilidad. Mientras que con las máquinas virtuales esto resulta más difícil porque están completamente aisladas del sistema principal.
Gracias a sus características los contenedores son la opción preferida para trabajar con aplicaciones autocontenidas, no solo por consumir menos espacio y hacer un mejor uso de los recursos del sistema, sino también por su portabilidad y la experiencia que ofrecen durante y después del desarrollo de una aplicación.
La arquitectura interna de Docker
Las múltiples herramientas que componen Docker garantizan una experiencia completa y fluida, lo que a su vez estimula el desarrollo de nuevas tecnologías para contenedores.
En un principio Docker utilizaba la tecnología de GNU/Linux, LXC, más adelante desarrolló la suya propia llamada libcontainer, y si bien ambas interactúan directamente con el Kernel, contar con su propia tecnología le permitió deshacerse de la dependencia de los tiempos de desarrollo de LXC y a su vez de las limitaciones que esto conlleva.
Con su propia tecnología Docker ganó más control sobre la optimización de procesos y dio mayor flexibilidad al equipo. Sin depender de LXC, Docker dio forma a su propia filosofía sobre como trabajar con contenedores.
Docker Engine
Docker Engine es una herramienta compuesta por tres componentes; Docker CLI, Docker Daemon y una API REST que les permite comunicarse entre sí.
Esta arquitectura está organizada de forma que el cliente (Docker CLI) se comunica con el servidor (Docker Daemon) a través de una API REST. Esto es lo que permite trabajar con contenedores utilizando Docker.
Docker CLI y Docker Daemon utilizan sockets UNIX o interfaces de red para comunicarse. Esto quiere decir que cuando Docker Daemon se encuentra en la misma máquina que Docker CLI, utilizan sockets UNIX y en caso contrario, interfaces de red.
Docker Desktop
La forma más sencilla de instalar Docker Engine es utilizando Docker Desktop. Se trata de una aplicación de escritorio con todo lo necesario para trabajar con Docker a través de una interfaz gráfica, lo que elimina la necesidad de la línea de comandos.
Docker Desktop es también la forma de instalar Docker Engine en sistemas como MacOS o Windows.
En sistemas basados en GNU/Linux los contenedores desplegados antes de la instalación de Docker Desktop no estarán disponibles. Porque a diferencia de Docker Engine, Docker Desktop se ejecuta en una máquina virtual que crea y utiliza su propio contexto.
Docker Desktop está disponible en varias distribuciones de GNU/Linux; Debian, Ubuntu, Fedora, Arch y RHEL.
Instalar Docker Engine a través de Docker Desktop introduce dos requisitos legales para no tener que pagar una subscripción;
- El número de empleados no puede exceder los 250.
- Los beneficios anuales no deben exceder los 10 millones de dolares estadounidenses.
Docker Compose
Docker Compose es una herramienta diseñada para trabajar con aplicaciones multi-contenedor de manera eficiente y simplificada en cualquier entorno, desarrollo, stage o producción.
Al igual que en la arquitectura interna de Docker Engine, la comunicación con Docker Compose se realiza a través de Docker CLI. Esta herramienta permite orquestar varios contenedores a través de un archivo de configuración .yml, siguiendo las especificaciones de compose espec, también establecidas por Docker.
El archivo de configuración de Docker Compose se basa exclusivamente en directivas y configuraciones, cosa que lo hace ligero y fácil de compartir entre equipos.
Una experiencia completa y fluida
Para crear un contenedor con Docker es necesario partir de un archivo de configuración que puede llamarse docker-compose o simplemente compose y utilizar una de las extensiones .yaml o .yml.
De forma independiente al nombre o la extensión, el archivo permite configurar todas las directivas necesarias de un contenedor. También es posible definir múltiples contenedores en el mismo archivo.
El siguiente ejemplo incluye tres contenedores; el primero corresponde a una imagen de Nginx, que podría servir como proxy inverso para los otros servicios como MySQL o PhpMyAdmin.
En el primer bloque se define el servicio de Nginx, un servidor web, proxy inverso y balanceador de carga. En dicho bloque se comienza por definir el nombre del servicio y, dentro de él, el resto de configuraciones necesarias:
- image: Define la imagen que se utilizará para crear el contenedor, si no existe en cache, Docker intentará descargarla automáticamente de Docker Hub.
- container_name: Asigna un nombre personalizado al contenedor; en caso de no configurar uno, Docker lo generará en formato hexadecimal.
- restart: Define la política de reinicio del contenedor. Según la política elegida; el contenedor puede detenerse a causa de un reinicio, un error en alguno de los servicios de los que depende, o incluso por un error interno. También puede intentar reiniciarse hasta que se detenga manualmente o se elimine.
- environment: Sirve para declarar las variables de entorno del servicio. Los nombres pueden variar según la imagen, por lo que, para conocer las variables disponibles, es necesario consultar la documentación de la imagen en Docker Hub.
- volumes: El atributo volumes especifica dónde se deben almacenar los datos persistentes del contenedor, como logs, sites-availables y sites-enabled. Según las necesidades de cada proyecto, los datos pueden variar.
- ports: El atributo ports es crucial para la comunicación entre el sistema anfitrión y el contenedor. Por ejemplo, el puerto interno puede configurarse para escuchar en el puerto 3000, y si en el sistema anfitrión ya existe un proceso escuchando en el mismo puerto, el atributo ports puede redirigir el puerto interno al puerto 3001.
- networks: Es útil para conectar los distintos servicios y contenedores entre sí, ya que cada contenedor está aislado del resto. Para acceder a ellos es necesario configurar una red compartida. En el ejemplo, 'my_network' es compartida por los tres contenedores.
Aunque se han definido tres contenedores distintos, están conectados a través del atributo networks, lo que resalta varios puntos importantes, siendo los más destacados el potencial de escalabilidad y portabilidad.
Portabilidad
La portabilidad y la diversidad de entornos de producción o desarrollo son unos de los principales cuellos de botella. La mayoría de servidores son sistemas basados en GNU/Linux que en algunos casos comparten la misma versión del Kernel o paquetes preinstalados, lo que no evita que existan diferencias entre las distintas distribuciones.
Cada distribución configura y compila el kernel de manera única e incluye herramientas adicionales que las distinguen. Lo que funciona en Debian puede no hacerlo en openSUSE, salvo que se trate de la misma distribución en desarrollo y producción, o de versiones derivadas.
No obstante, aún en este escenario los contenedores tienen un valor intrínseco, reducen los riesgos de errores por dependencias o problemas de compatibilidad entre entornos. Esto junto a la estadarización de herramientas internas de Docker permite que existan otras alternativas como Podman que resuelven la falta de soporte oficial en algunas distribuciones.
El modelo estándar
La estandarización de herramientas internas y participación activa de Docker en el mundo del software libre, estimulan el desarrollo de otras alternativas o implementaciones para distribuciones o sistemas sin soporte oficial.
Un ejemplo de distribuciones que no cuentan con soporte oficial por parte de Docker es openSUSE. Sin embargo, a pesar de no contar con soporte oficial, es posible utilizar Docker en esta distribución de la mano de The Moby Project, un framework de Docker.
Aunque este framework no está precisamente pensado para equipos de desarrollo que necesitan una solución completa o lista para utilizar, la comunidad de openSUSE se ha encargado de ensamblar los componentes necesarios para contar con su propia versión de Docker. Esto no sin que existan algunas diferencias entre la versión oficial y la de openSUSE.
The Moby Project según su propia definición, es comparable a un "set de legos" de componentes estándar para ensamblar plataformas personalizadas, o bien para hacer parches en un build personalizado de Docker, como ha hecho openSUSE.
Cuál es la diferencia entre el build de openSUSE y el oficial de Docker
La diferencia entre el build de openSUSE y el oficial de Docker es que este último da soporte a distribuciones específicas y está optimizado para funcionar de manera consistente con el resto de herramientas que componen la plataforma.
En cambio el build de openSUSE tiene parches orientados mas bien a funcionar con el ecosistema de dicha distribución tales como; Yast que además incluye Zypper para gestionar paquetes desde la terminal, lo que lo convierte en algo más complejo de implementar que el resto de distribuciones.
Reflexiones finales
Aunque se han tratado una variedad de aspectos de Docker, no se han tocado temas como Dockerfile, Docker Hub o cómo funciona internamente un contenedor, y cuya lectura es recomendada.
En conclusión, Docker se ha consolidado como una herramienta fundamental en el desarrollo de software moderno, a tal punto que se ha convertido en el estándar de facto para trabajar con contenedores.
El soporte para distintos sistemas operativos y distribuciones, más su aporte a la comunidad open source hacen que sea una opción acertada para cualquier proyecto o nivel de experiencia con esta herramienta.