Docker: la tecnología de contenedores

Docker es una tecnología para la virtualización de aplicaciones de software basada en contenedores. El enfoque principal de Docker basado en contenedores ha transformado el desarrollo de aplicaciones en los últimos años. Ha afectado a diferentes áreas del desarrollo, incluyendo cómo se construyen las aplicaciones y los componentes, cómo se distribuyen los servicios de software y cómo se trasladan del desarrollo a la producción. Con Docker, todos estos procesos se ejecutan de forma diferente a como lo hacían antes.

Pero no solo han cambiado los procesos de desarrollo, sino también la arquitectura del software. Se ha alejado de las soluciones globales monolíticas y se ha acercado a los conglomerados de software ligero acoplado en “microservicios”. Esto, a su vez, ha hecho que los sistemas globales resultantes sean más complejos. En los últimos años, se ha establecido un software como Kubernetes para gestionar aplicaciones multicontenedor.

El desarrollo de la virtualización basada en contenedores está lejos de haber terminado, por lo que sigue siendo un campo apasionante. En este artículo, explicaremos cómo funciona Docker como tecnología subyacente. Además, veremos qué motivó el desarrollo de Docker.

Nota

El nombre “Docker” tiene varios significados. Se utiliza como sinónimo del propio software, para designar el proyecto de código abierto en el que se basa, y para designar a una empresa estadounidense que explota comercialmente varios productos y servicios.

Una breve historia de Docker

El software publicado originalmente con el nombre de “Docker” se basó en la tecnología de contenedores de Linux (LXC). LXC fue sustituido posteriormente por la propia libcontainer de Docker. Se han añadido nuevos componentes de software a medida que Docker ha seguido creciendo y se ha convertido en el estándar de la virtualización basada en contenedores. Del desarrollo de Docker han surgido conceptos como containerd, un tiempo de ejecución de contenedores con la implementación por defecto runC. En la actualidad, ambos proyectos están gestionados por la Cloud Native Computing Foundation (CNCF) y la Open Container Initiative (OCI).

Además del equipo de Docker, empresas tecnológicas líderes como Cisco, Google, Huawei, IBM, Microsoft y Red Hat participan en el desarrollo de Docker y las tecnologías relacionadas. Un avance más reciente es que ahora también se utiliza Windows como entorno nativo para los contenedores Docker, además del núcleo de Linux. Estos son algunos de los principales hitos en la historia evolutiva de Docker:

Año Hitos del desarrollo de Docker
2007 Tecnología cgroups integrada en el núcleo de Linux
2008 Lanzamiento de LXC; construye sobre cgroups y sobre espacios de nombres de Linux como hizo Docker más tarde
2013 Docker liberado como código abierto
2014 Docker disponible en Amazon EC2
2015 Kubernetes liberado
2016 Docker disponible en Windows 10 Pro a través de Hyper-V
2019  
Consejo

Al final del artículo, entraremos en detalle sobre lo que motivó el desarrollo de Docker y otras tecnologías de virtualización similares.

¿Qué es Docker?

La funcionalidad principal de Docker es la virtualización de aplicaciones en contenedores. Esto contrasta con la virtualización con máquinas virtuales (VM). Con Docker, el código de la aplicación, incluidas todas las dependencias, se empaqueta en una “imagen”. El software Docker ejecuta la aplicación empaquetada en un contenedor Docker. Las imágenes pueden moverse entre sistemas y ejecutarse en cualquier sistema que ejecute Docker.

Cita

Containers are a standardized unit of software that allows developers to isolate their app from its environment […]”. - Cita de un desarrollador de Docker, fuente: https://www.Docker.com/why-Docker

“Los contenedores son una unidad de software estandarizada que permite a los desarrolladores aislar su aplicación de su entorno [...]” (Traducido por IONOS).

Al igual que en el caso de la implantación de máquinas virtuales (VM), un objetivo principal de los contenedores Docker es aislar la aplicación que se está ejecutando. Sin embargo, a diferencia de las VM, no se virtualiza un sistema operativo completo. En su lugar, Docker asigna ciertos recursos del sistema operativo y del hardware a cada contenedor. Se puede crear cualquier número de contenedores a partir de una imagen Docker y hacerlos funcionar en paralelo. Así es como se implementan los servicios escalables en la nube.

Aunque hablemos de Docker como una pieza de software, en realidad se trata de múltiples componentes de software que se comunican a través de la API Docker Engine. Además, se utiliza un gran número de objetos especiales de Docker, como las mencionadas imágenes y contenedores. Los flujos de trabajo específicos de Docker están compuestos por los componentes de software y los objetos Docker. Veamos en detalle cómo interactúan.

Software Docker

La base del software Docker es el “Docker Engine”. Se utiliza principalmente para gestionar y controlar los contenedores y sus imágenes subyacentes. Para las funcionalidades que van más allá se utilizan herramientas especiales, necesarias principalmente para gestionar aplicaciones que consisten en grupos de contenedores.

Docker Engine

Docker Engine se ejecuta en un sistema o servidor local y consta de dos componentes:

  1. El Docker daemon (Dockerd). Se ejecuta siempre en segundo plano y recibe las peticiones de la API Docker Engine. Dockerd responde a los comandos adecuados para gestionar los contenedores Docker y otros objetos Docker.
  2. El cliente Docker (Docker). Es un programa de línea de comandos. El cliente Docker se utiliza para controlar el motor Docker y proporciona comandos para crear y construir contenedores Docker, así como para crear, obtener y versionar imágenes Docker.

API Docker Engine

La API Docker Engine es una REST API. Interactúa con el Docker daemon. Existen “software development kits” (SKDs) para Go y Python que permiten integrar la API del Docker Engine en los proyectos de software. También existen bibliotecas similares para más de una docena de otros lenguajes de programación. Puedes acceder a la API con la línea de comandos mediante el comando Docker. Además, puedes acceder a la API directamente utilizando cURL o herramientas similares.

Herramientas Docker

Cuando utilizas máquinas virtuales, sueles utilizar sistemas formados por varios componentes de software. En cambio, la virtualización de contenedores con Docker favorece los conjuntos de microservicios mal acoplados. Estos son adecuados para soluciones distribuidas en la nube que ofrecen un alto grado de modularidad y alta disponibilidad. Sin embargo, este tipo de sistemas se vuelven muy complejos rápidamente. Para gestionar eficazmente las aplicaciones en contenedores, se utilizan herramientas de software especiales conocidas como “orchestrators” u orquestadoras.

Docker Swarm y Docker Compose son dos herramientas oficiales de Docker que están disponibles para orquestar conjuntos de contenedores. El comando “Docker swarm” puede utilizarse para combinar varios Docker Engines en un solo motor virtual. Los motores individuales pueden operar entonces en múltiples sistemas e infraestructuras. El comando “Docker compose” se utiliza para crear aplicaciones multicontenedor conocidas como “stacks” o pilas.

El orquestador Kubernetes, desarrollado originalmente por Google, es más fácil de usar que Swarm y Compose. Se ha establecido como estándar y es ampliamente utilizado por la industria. Las empresas de alojamiento y otros proveedores de soluciones de “Software as a Service” (SaaS) y de “Platform as a Service” (PaaS) utilizan cada vez más Kubernetes como infraestructura de base.

Objetos Docker

Los flujos de trabajo en el ecosistema Docker son el resultado de cómo los objetos Docker interactúan entre sí. Se gestionan mediante la comunicación con la API del Docker Engine. Veamos en detalle cada tipo de objeto.

Imagen Docker

Una imagen Docker es una plantilla de solo lectura para crear uno o más contenedores idénticos. Las imágenes Docker son efectivamente las semillas del sistema; se utilizan para agrupar y entregar aplicaciones.

Se utilizan varios repositorios para compartir imágenes Docker. Hay repositorios públicos y privados. En el momento de escribir este artículo, hay más de cinco millones de imágenes diferentes disponibles para su descarga en el popular “Docker Hub”. Los comandos Docker “Docker pull” y “Docker push” se utilizan para descargar una imagen de un repositorio o compartirla allí.

Las imágenes Docker se construyen por capas. Cada capa representa un cambio específico en la imagen. Esto da lugar a un versionado continuo de las imágenes, que permite volver a un estado anterior. Una imagen existente puede utilizarse como base para crear una nueva imagen.

Dockerfile

Un Dockerfile es un archivo de texto que describe la estructura de una imagen Docker. Un Dockerfile es similar a un script de procesamiento por lotes; el archivo contiene comandos que describen una imagen. Cuando ejecutas un Dockerfile, los comandos se procesan uno tras otro. Cada comando crea una nueva capa en la imagen Docker. Así que también puedes pensar en un Dockerfile como una especie de receta utilizada como base para crear una imagen.

Contenedor Docker

Ahora pasemos al concepto principal del universo Docker: los contenedores Docker. Mientras que una imagen Docker es una plantilla inerte, un contenedor Docker es una instancia activa y en ejecución de una imagen. Una imagen Docker existe localmente en una única copia y solo ocupa un poco de espacio de almacenamiento. En cambio, se pueden crear varios contenedores Docker a partir de la misma imagen y ejecutarse en paralelo.

Cada contenedor Docker consume una cierta cantidad de recursos del sistema para su ejecución, como el uso de la CPU, la RAM, las interfaces de red, etc. Un contenedor Docker puede ser creado, iniciado, detenido y destruido. También se puede guardar el estado de un contenedor en ejecución como una nueva imagen.

Volumen Docker

Como hemos visto, se crea un contenedor Docker en funcionamiento a partir de una imagen no modificable. Pero ¿qué pasa con los datos que se utilizan dentro del contenedor y que deben conservarse más allá de su vida útil? Los volúmenes Docker se utilizan para este caso de uso. Un volumen Docker existe fuera de un contenedor específico. Así, varios contenedores pueden compartir un volumen. Los datos contenidos en el volumen se almacenan en el sistema de archivos del host. Esto significa que un volumen Docker es como una carpeta compartida en una máquina virtual.

¿Cómo funciona Docker?

El principio de actuación básico de Docker funciona de forma similar a la tecnología de virtualización LXC desarrollada anteriormente: ambos se basan en el núcleo de Linux y realizan una virtualización basada en contenedores. Tanto Docker como LXC combinan dos objetivos contradictorios:

  1. Los contenedores que se ejecutan comparten el mismo núcleo de Linux, lo que los hace más ligeros que las máquinas virtuales.
  2. Los contenedores en ejecución están aislados unos de otros y solo tienen acceso a una cantidad limitada de recursos del sistema.

Tanto Docker como LXC utilizan “kernel namespaces” y “control groups” para lograr estos objetivos. Veamos cómo funciona en detalle.

Kernel de Linux

El kernel de Linux es el componente central del sistema operativo de código abierto GNU/Linux. El kernel gestiona el hardware y controla los procesos. Cuando se ejecuta Docker fuera de Linux, se necesita un hypervisor o una máquina virtual para proporcionar la funcionalidad del kernel de Linux. En macOS, se utiliza xhyve, un derivado del hypervisor BSD bhyve. En Windows 10, Docker utiliza el hypervisor Hyper-V.

Namespaces de Kernel

Los namespaces son una característica del núcleo de Linux. Particionan los recursos del kernel y así aseguran que los procesos permanezcan separados unos de otros. Un proceso de namespace solo puede ver los recursos del kernel de ese mismo namespace. Aquí hay una visión general de los namespaces utilizados en Docker:

Namespace Descripción Explicación
UTS Identificación del sistema Asignar a los contenedores sus propios nombres de host y dominio
PID Identificación de procesos Cada contenedor utiliza su propio espacio de nombres para los ID de los procesos; los PID de otros contenedores no son visibles; así, dos procesos en diferentes contenedores pueden utilizar el mismo PID sin conflicto.
IPC Comunicación entre procesos Los espacios de nombres IPC aíslan los procesos de un contenedor para que no puedan comunicarse con los procesos de otros contenedores.
NET Recursos de red Asignar recursos de red separados, como direcciones IP o tablas de enrutamiento, a un contenedor.
MNT Puntos de montaje del sistema de archivos Restringe el sistema de archivos del host a una sección muy definida desde el punto de vista del contenedor

Control groups

Los control groups, normalmente abreviados como cgroups, se utilizan para organizar los procesos de Linux jerárquicamente. A un proceso (o grupo de procesos) se le asigna una cantidad limitada de recursos del sistema. Esto incluye RAM, núcleos de CPU, almacenamiento masivo y dispositivos de red (virtuales). Mientras que los espacios de nombres aíslan los procesos entre sí, los control groups imitan el acceso a los recursos del sistema. Esto garantiza que el sistema general siga funcionando cuando se utilizan varios contenedores.

¿Cuáles son las ventajas de Docker?

Echemos un vistazo a la historia del desarrollo de software para comprender las ventajas de Docker. ¿Cómo se construye, entrega y ejecuta el software? ¿Qué partes del proceso han cambiado fundamentalmente? El software es la contrapartida del hardware, el ordenador físico. Sin el software, el ordenador no es más que un trozo de materia. Mientras que el hardware es fijo e inalterable, el software puede reconstruirse y personalizarse. La interacción de ambos niveles da lugar a este maravilloso mundo digital.

Software en una máquina física

Tradicionalmente, el software se ha creado para ser ejecutado en una máquina física. Pero cuando lo hacemos nos topamos rápidamente con un muro. El software solo puede ejecutarse en un determinado hardware. Requiere, por ejemplo, un procesador concreto.

Además, el software más complejo no suele funcionar de forma completamente autónoma, sino que está integrado en un ecosistema de software. Este incluye un sistema operativo, bibliotecas y dependencias. Para que todos los componentes interactúen correctamente, deben estar disponibles las versiones adecuadas. También hay una configuración que describe cómo se vinculan los componentes individuales entre sí.

Si quieres ejecutar varias aplicaciones en una máquina en paralelo, rápidamente surgen conflictos de versiones. Una aplicación puede necesitar una versión de un componente que sea incompatible con otra aplicación. En el peor de los casos, cada aplicación tendría que ejecutarse en su propia máquina física. Lo cierto es que las máquinas físicas son caras y no se pueden escalar fácilmente. Así que si los requisitos de recursos de una aplicación crecen, puede ser necesario migrar a una nueva máquina física.

Otro problema surge del hecho de que el software en desarrollo se utiliza en diferentes entornos. Un desarrollador escribe el código en el sistema local y lo ejecuta allí para probarlo. La aplicación pasa por varias etapas de prueba antes de pasar a producción, incluyendo un entorno de prueba para garantizar la calidad o un entorno de ensayo para que el equipo del producto lo pruebe.

Los distintos entornos suelen existir en máquinas físicas diferentes. Casi siempre hay diferencias en las versiones del sistema operativo, la biblioteca y la configuración. ¿Cómo puedes conciliarlas todas? Porque si los entornos difieren entre sí, las pruebas pierden su sentido. Además, hay que sustituir un sistema si falla. ¿Cómo puedes garantizar la coherencia? Es difícil enfrentarse a estos problemas en máquinas físicas.

Las máquinas virtuales como un paso en la dirección correcta

Los problemas antes mencionados relacionados con las máquinas físicas han provocado el aumento de la popularidad de las máquinas virtuales (VM). La idea básica es integrar una capa entre el hardware y el sistema operativo o el sistema operativo anfitrión y los sistemas operativos ajenos. Una VM desacopla el entorno de la aplicación del hardware subyacente. La combinación específica de un sistema operativo, una aplicación, unas bibliotecas y una configuración puede reproducirse a partir de una imagen. Además de aislar completamente una aplicación, esto permite a los desarrolladores agrupar varias aplicaciones en un “dispositivo”.

Las imágenes de las máquinas virtuales pueden moverse entre máquinas físicas y varios sistemas operativos virtualizados pueden ejecutarse en paralelo. Esto garantiza la escalabilidad de la aplicación. Sin embargo, la virtualización del sistema operativo consume muchos recursos y es excesiva para casos de uso sencillos.

Las ventajas de la virtualización de contenedores con Docker

Las imágenes utilizadas en la virtualización de contenedores no necesitan un sistema operativo. La virtualización de contenedores es más ligera y proporciona casi tanto aislamiento como las máquinas virtuales. Una imagen de contenedor combina el código de la aplicación con todas las dependencias necesarias y la configuración. Las imágenes son transferibles entre sistemas, y los contenedores construidos sobre ellas pueden reproducirse. Los contenedores pueden utilizarse en varios entornos, como el de desarrollo, el de producción, el de pruebas y el de preparación. El control de las versiones de las capas y de las imágenes también proporciona una buena dosis de modularidad.

Resumamos las principales ventajas de la virtualización de aplicaciones basada en Docker frente a la utilización de una VM. Un contenedor Docker:

  • no contiene su propio sistema operativo y hardware simulado
  • comparte un núcleo de sistema operativo con otros contenedores alojados en el mismo sistema
  • es ligero y compacto en términos de uso de recursos en comparación con una aplicación basada en una máquina virtual
  • se inicia más rápido que una máquina virtual
  • puede ejecutarse en varias instancias de la misma imagen en paralelo
  • puede utilizarse junto con otros servicios basados en contenedores mediante la orquestación
  • es ideal para el desarrollo en local
¿Le ha resultado útil este artículo?
Page top