Una introducción para principiantes a contenedores, máquinas virtuales y Docker

Si es un programador o un técnico, es probable que al menos haya oído hablar de Docker: una herramienta útil para empaquetar, enviar y ejecutar aplicaciones dentro de "contenedores". Sería difícil no hacerlo, con toda la atención que recibe en estos días, tanto de desarrolladores como de administradores de sistemas. Incluso los perros grandes como Google, VMware y Amazon están creando servicios para respaldarlo.

Independientemente de si tiene o no un caso de uso inmediato en mente para Docker, sigo pensando que es importante comprender algunos de los conceptos fundamentales sobre qué es un "contenedor" y cómo se compara con una máquina virtual (VM). Si bien Internet está lleno de excelentes guías de uso para Docker, no pude encontrar muchas guías conceptuales para principiantes, particularmente sobre lo que se compone de un contenedor. Entonces, con suerte, esta publicación resolverá ese problema :)

Comencemos por comprender qué son las máquinas virtuales y los contenedores.

¿Qué son "contenedores" y "VM"?

Los contenedores y las máquinas virtuales son similares en sus objetivos: aislar una aplicación y sus dependencias en una unidad autónoma que pueda ejecutarse en cualquier lugar.

Además, los contenedores y las máquinas virtuales eliminan la necesidad de hardware físico, lo que permite un uso más eficiente de los recursos informáticos, tanto en términos de consumo de energía como de rentabilidad.

La principal diferencia entre contenedores y máquinas virtuales está en su enfoque arquitectónico. Miremos más de cerca.

Maquinas virtuales

Una VM es esencialmente una emulación de una computadora real que ejecuta programas como una computadora real. Las máquinas virtuales se ejecutan sobre una máquina física mediante un "hipervisor" . Un hipervisor, a su vez, se ejecuta en una máquina host o en "bare-metal" .

Analicemos la jerga:

Un hipervisor es una pieza de software, firmware o hardware sobre el que se ejecutan las máquinas virtuales. Los propios hipervisores se ejecutan en computadoras físicas, denominadas "máquina host" . La máquina host proporciona recursos a las VM, incluida la RAM y la CPU. Estos recursos se dividen entre máquinas virtuales y se pueden distribuir como mejor le parezca. Por lo tanto, si una máquina virtual está ejecutando una aplicación con más recursos, puede asignar más recursos a esa que a las otras máquinas virtuales que se ejecutan en la misma máquina host.

La máquina virtual que se ejecuta en la máquina host (nuevamente, usando un hipervisor) también se denomina a menudo una "máquina invitada". Esta máquina invitada contiene tanto la aplicación como lo que necesite para ejecutar esa aplicación (por ejemplo, binarios y bibliotecas del sistema). También lleva una pila completa de hardware virtualizado propia, incluidos los adaptadores de red virtualizados, el almacenamiento y la CPU, lo que significa que también tiene su propio sistema operativo invitado completo. Desde el interior, la máquina huésped se comporta como una unidad propia con sus propios recursos dedicados. Desde el exterior, sabemos que es una máquina virtual: recursos compartidos proporcionados por la máquina host.

Como se mencionó anteriormente, una máquina invitada puede ejecutarse en un hipervisor alojado o en un hipervisor completo . Hay algunas diferencias importantes entre ellos.

En primer lugar, un hipervisor de virtualización alojado se ejecuta en el sistema operativo de la máquina host. Por ejemplo, una computadora que ejecuta OSX puede tener una VM (por ejemplo, VirtualBox o VMware Workstation 8) instalada en la parte superior de ese OS. La VM no tiene acceso directo al hardware, por lo que tiene que pasar por el sistema operativo host (en nuestro caso, el OSX de Mac).

El beneficio de un hipervisor alojado es que el hardware subyacente es menos importante. El sistema operativo del host es responsable de los controladores de hardware en lugar del hipervisor en sí y, por lo tanto, se considera que tiene más "compatibilidad de hardware". Por otro lado, esta capa adicional entre el hardware y el hipervisor crea más recursos generales, lo que reduce el rendimiento de la máquina virtual.

Un entorno de hipervisor bare metal aborda el problema de rendimiento mediante la instalación y ejecución desde el hardware de la máquina host. Debido a que interactúa directamente con el hardware subyacente, no necesita un sistema operativo host para ejecutarse. En este caso, lo primero que se instalará en el servidor de una máquina host como sistema operativo será el hipervisor. A diferencia del hipervisor alojado, un hipervisor bare-metal tiene sus propios controladores de dispositivo e interactúa con cada componente directamente para cualquier E / S, procesamiento o tareas específicas del sistema operativo. Esto da como resultado un mejor rendimiento, escalabilidad y estabilidad. La compensación aquí es que la compatibilidad de hardware es limitada porque el hipervisor solo puede tener tantos controladores de dispositivo integrados.

Después de toda esta charla sobre hipervisores, es posible que se pregunte por qué necesitamos esta capa de "hipervisor" adicional entre la máquina virtual y la máquina host.

Bueno, dado que la VM tiene un sistema operativo virtual propio, el hipervisor juega un papel esencial al proporcionar a las VM una plataforma para administrar y ejecutar este sistema operativo invitado. Permite que las computadoras host compartan sus recursos entre las máquinas virtuales que se ejecutan como invitados encima de ellas.

Como puede ver en el diagrama, las máquinas virtuales empaquetan el hardware virtual, un kernel (es decir, el sistema operativo) y el espacio de usuario para cada nueva máquina virtual.

Envase

A diferencia de una máquina virtual que proporciona virtualización de hardware, un contenedor proporciona virtualización a nivel de sistema operativo abstrayendo el "espacio de usuario". Verá lo que quiero decir cuando desempaquetamos el término contenedor .

Para todos los propósitos y propósitos, los contenedores parecen una máquina virtual. Por ejemplo, tienen espacio privado para el procesamiento, pueden ejecutar comandos como root, tienen una interfaz de red privada y una dirección IP, permiten rutas personalizadas y reglas de iptable, pueden montar sistemas de archivos, etc.

La única gran diferencia entre los contenedores y las VM es que los contenedores * comparten * el kernel del sistema host con otros contenedores.

Este diagrama le muestra que los contenedores empaquetan solo el espacio del usuario, y no el kernel o hardware virtual como lo hace una VM. Cada contenedor tiene su propio espacio de usuario aislado para permitir que varios contenedores se ejecuten en una sola máquina host. Podemos ver que toda la arquitectura a nivel del sistema operativo se comparte entre contenedores. Las únicas partes que se crean desde cero son los contenedores y las bibliotecas. Esto es lo que hace que los contenedores sean tan ligeros.

¿Dónde entra Docker?

Docker es un proyecto de código abierto basado en contenedores de Linux. Utiliza características del kernel de Linux como espacios de nombres y grupos de control para crear contenedores en la parte superior de un sistema operativo.

Los contenedores están lejos de ser nuevos; Google ha estado utilizando su propia tecnología de contenedores durante años. Otras tecnologías de contenedores de Linux incluyen Solaris Zones, BSD jails y LXC, que existen desde hace muchos años.

Entonces, ¿por qué Docker está ganando impulso de repente?

1. Facilidad de uso: Docker ha hecho que sea mucho más fácil para cualquier persona (desarrolladores, administradores de sistemas, arquitectos y otros) aprovechar los contenedores para crear y probar rápidamente aplicaciones portátiles. Permite a cualquier persona empaquetar una aplicación en su computadora portátil, que a su vez puede ejecutarse sin modificaciones en cualquier nube pública, nube privada o incluso bare metal. El mantra es: "construir una vez, ejecutar en cualquier lugar".

2. Velocidad: los contenedores Docker son muy ligeros y rápidos. Dado que los contenedores son solo entornos de espacio aislado que se ejecutan en el kernel, ocupan menos recursos. Puede crear y ejecutar un contenedor Docker en segundos, en comparación con las máquinas virtuales, que pueden tardar más porque tienen que arrancar un sistema operativo virtual completo cada vez.

3. Docker Hub: los usuarios de Docker también se benefician del ecosistema cada vez más rico de Docker Hub, que puede considerar como una "tienda de aplicaciones para imágenes de Docker". Docker Hub tiene decenas de miles de imágenes públicas creadas por la comunidad que están disponibles para su uso. Es increíblemente fácil buscar imágenes que satisfagan sus necesidades, listas para desplegar y usar con poca o ninguna modificación.

4. Modularidad y escalabilidad: Docker facilita la división de la funcionalidad de su aplicación en contenedores individuales. Por ejemplo, es posible que tenga su base de datos de Postgres ejecutándose en un contenedor y su servidor Redis en otro mientras su aplicación Node.js está en otro. Con Docker, es más fácil vincular estos contenedores para crear su aplicación, lo que facilita escalar o actualizar componentes de forma independiente en el futuro.

Por último, pero no menos importante, ¿quién no ama a la ballena Docker? ;)

Conceptos fundamentales de Docker

Ahora que tenemos el panorama general en su lugar, repasemos las partes fundamentales de Docker pieza por pieza:

Motor Docker

El motor de Docker es la capa en la que se ejecuta Docker. Es un tiempo de ejecución ligero y herramientas que administra contenedores, imágenes, compilaciones y más. Se ejecuta de forma nativa en sistemas Linux y se compone de:

1. Un Docker Daemon que se ejecuta en la computadora host.

2. Un cliente Docker que luego se comunica con Docker Daemon para ejecutar comandos.

3. Una API REST para interactuar con el Docker Daemon de forma remota.

Cliente Docker

El cliente de Docker es con lo que usted, como usuario final de Docker, se comunica. Piense en ello como la interfaz de usuario de Docker. Por ejemplo, cuando lo hace ...

se está comunicando con Docker Client, que luego comunica sus instrucciones al Docker Daemon.

Docker Daemon

El demonio de Docker es lo que realmente ejecuta los comandos enviados al cliente de Docker, como construir, ejecutar y distribuir sus contenedores. El Docker Daemon se ejecuta en la máquina host, pero como usuario, nunca se comunica directamente con el Daemon. El cliente Docker también puede ejecutarse en la máquina host, pero no es necesario. Puede ejecutarse en una máquina diferente y comunicarse con el Docker Daemon que se ejecuta en la máquina host.

Dockerfile

Un Dockerfile es donde escribe las instrucciones para crear una imagen de Docker. Estas instrucciones pueden ser:

  • EJECUTE apt-get y install some-package : para instalar un paquete de software
  • EXPOSE 8000: para exponer un puerto
  • ENV ANT_HOME / usr / local / apache-ant para pasar una variable de entorno

Etcétera. Una vez que haya configurado su Dockerfile, puede usar el comando docker build para crear una imagen a partir de él. Aquí hay un ejemplo de un Dockerfile:

Imagen de Docker

Las imágenes son plantillas de solo lectura que crea a partir de un conjunto de instrucciones escritas en su Dockerfile. Las imágenes definen tanto cómo desea que se vea su aplicación empaquetada y sus dependencias * y * qué procesos se ejecutarán cuando se lance.

La imagen de Docker se crea con un Dockerfile. Cada instrucción en el Dockerfile agrega una nueva "capa" a la imagen, con capas que representan una parte del sistema de archivos de imágenes que agrega o reemplaza la capa debajo de ella. Las capas son clave para la estructura ligera pero poderosa de Docker. Docker usa un sistema de archivos Union para lograr esto:

Sistemas de archivos de unión

Docker utiliza Union File Systems para crear una imagen. Puede pensar en un sistema de archivos de unión como un sistema de archivos apilable, lo que significa que los archivos y directorios de sistemas de archivos separados (conocidos como ramas) se pueden superponer de forma transparente para formar un solo sistema de archivos.

El contenido de los directorios que tienen la misma ruta dentro de las ramas superpuestas se ve como un solo directorio combinado, lo que evita la necesidad de crear copias separadas de cada capa. En cambio, a todos se les puede dar indicaciones sobre el mismo recurso; cuando ciertas capas necesitan ser modificadas, creará una copia y modificará una copia local, dejando el original sin cambios. Así es como los sistemas de archivos pueden * parecer * que se pueden escribir sin permitir realmente las escrituras. (En otras palabras, un sistema de "copia en escritura").

Los sistemas en capas ofrecen dos ventajas principales:

1. Sin duplicaciones: las capas ayudan a evitar la duplicación de un conjunto completo de archivos cada vez que utiliza una imagen para crear y ejecutar un nuevo contenedor, lo que hace que la creación de instancias de contenedores Docker sea muy rápida y económica.

2. Segregación de capas: hacer un cambio es mucho más rápido: cuando cambia una imagen, Docker solo propaga las actualizaciones a la capa que se cambió.

Volúmenes

Los volúmenes son la parte de "datos" de un contenedor, que se inicializa cuando se crea un contenedor. Los volúmenes le permiten conservar y compartir los datos de un contenedor. Los volúmenes de datos están separados del sistema de archivos de unión predeterminado y existen como directorios y archivos normales en el sistema de archivos del host. Por lo tanto, incluso si destruye, actualiza o reconstruye su contenedor, los volúmenes de datos permanecerán intactos. Cuando desee actualizar un volumen, realice cambios en él directamente. (Como beneficio adicional, los volúmenes de datos se pueden compartir y reutilizar entre varios contenedores, lo cual es bastante bueno).

Contenedores Docker

Un contenedor Docker, como se mencionó anteriormente, envuelve el software de una aplicación en una caja invisible con todo lo que la aplicación necesita para ejecutarse. Eso incluye el sistema operativo, el código de la aplicación, el tiempo de ejecución, las herramientas del sistema, las bibliotecas del sistema, etc. Los contenedores de Docker se crean a partir de imágenes de Docker. Dado que las imágenes son de solo lectura, Docker agrega un sistema de archivos de lectura y escritura sobre el sistema de archivos de solo lectura de la imagen para crear un contenedor.

Además, luego de crear el contenedor, Docker crea una interfaz de red para que el contenedor pueda hablar con el host local, adjunte una dirección IP disponible al contenedor y ejecute el proceso que especificó para ejecutar su aplicación al definir la imagen.

Una vez que haya creado correctamente un contenedor, podrá ejecutarlo en cualquier entorno sin tener que realizar cambios.

Hacer doble clic en "contenedores"

¡Uf! Son muchas partes móviles. Una cosa que siempre me dio curiosidad fue cómo se implementa realmente un contenedor, especialmente porque no hay ningún límite de infraestructura abstracto alrededor de un contenedor. Después de leer mucho, todo tiene sentido, ¡así que aquí está mi intento de explicárselo! :)

El término "contenedor" es en realidad solo un concepto abstracto para describir cómo algunas características diferentes trabajan juntas para visualizar un "contenedor". Repasemos muy rápido:

1) Espacios de nombres

Los espacios de nombres proporcionan contenedores con su propia vista del sistema Linux subyacente, lo que limita lo que el contenedor puede ver y acceder. Cuando ejecuta un contenedor, Docker crea espacios de nombres que usará el contenedor específico.

Hay varios tipos diferentes de espacios de nombres en un kernel que Docker utiliza, por ejemplo:

a. NET: Proporciona un contenedor con su propia vista de la pila de red del sistema (por ejemplo, sus propios dispositivos de red, direcciones IP, tablas de enrutamiento IP, directorio / proc / net, números de puerto, etc.).

segundo. PID: PID significa ID de proceso. Si alguna vez ha ejecutado ps aux en la línea de comandos para comprobar qué procesos se están ejecutando en su sistema, habrá visto una columna llamada "PID". El espacio de nombres PID brinda a los contenedores su propia visión de alcance de los procesos que pueden ver e interactuar, incluido un init independiente (PID 1), que es el "antepasado de todos los procesos".

C. MNT: le da a un contenedor su propia vista de los "montajes" en el sistema. Por lo tanto, los procesos en diferentes espacios de nombres de montaje tienen diferentes vistas de la jerarquía del sistema de archivos.

re. UTS: UTS son las siglas de UNIX Timesharing System. Permite que un proceso identifique identificadores del sistema (es decir, nombre de host, nombre de dominio, etc.). UTS permite que los contenedores tengan su propio nombre de host y nombre de dominio NIS que es independiente de otros contenedores y del sistema host.

mi. IPC: IPC significa Comunicación entre procesos. El espacio de nombres de IPC es responsable de aislar los recursos de IPC entre los procesos que se ejecutan dentro de cada contenedor.

F. USUARIO: este espacio de nombres se utiliza para aislar a los usuarios dentro de cada contenedor. Funciona al permitir que los contenedores tengan una vista diferente de los rangos uid (ID de usuario) y gid (ID de grupo), en comparación con el sistema host. Como resultado, el uid y el gid de un proceso pueden ser diferentes dentro y fuera de un espacio de nombres de usuario, lo que también permite que un proceso tenga un usuario sin privilegios fuera de un contenedor sin sacrificar el privilegio de root dentro de un contenedor.

Docker usa estos espacios de nombres juntos para aislar y comenzar la creación de un contenedor. La siguiente característica se llama grupos de control.

2) Grupos de control

Los grupos de control (también llamados cgroups) es una característica del kernel de Linux que aísla, prioriza y da cuenta del uso de recursos (CPU, memoria, E / S de disco, red, etc.) de un conjunto de procesos. En este sentido, un cgroup asegura que los contenedores de Docker solo usen los recursos que necesitan y, si es necesario, establece límites a los recursos que un contenedor * puede * usar. Cgroups también se asegura de que un solo contenedor no agote uno de esos recursos y detenga todo el sistema.

Por último, los sistemas de archivos de unión es otra característica que utiliza Docker:

3) Sistema de archivos de unión aislado:

Descrito anteriormente en la sección Imágenes de Docker :)

Esto es realmente todo lo que hay en un contenedor Docker (por supuesto, el diablo está en los detalles de implementación, como cómo administrar las interacciones entre los diversos componentes).

El futuro de Docker: Docker y las máquinas virtuales coexistirán

Si bien Docker ciertamente está ganando mucho impulso, no creo que se convierta en una amenaza real para las máquinas virtuales. Los contenedores seguirán ganando terreno, pero hay muchos casos de uso en los que las máquinas virtuales siguen siendo más adecuadas.

Por ejemplo, si necesita ejecutar varias aplicaciones en varios servidores, probablemente tenga sentido utilizar máquinas virtuales. Por otro lado, si necesita ejecutar muchas * copias * de una sola aplicación, Docker ofrece algunas ventajas convincentes.

Además, si bien los contenedores le permiten dividir su aplicación en partes discretas más funcionales para crear una separación de preocupaciones, también significa que hay un número creciente de partes para administrar, que pueden volverse difíciles de manejar.

La seguridad también ha sido un área de preocupación con los contenedores Docker: dado que los contenedores comparten el mismo núcleo, la barrera entre contenedores es más delgada. Mientras que una máquina virtual completa solo puede emitir hipercalls al hipervisor del host, un contenedor Docker puede realizar llamadas al sistema al kernel del host, lo que crea una superficie más grande para el ataque. Cuando la seguridad es particularmente importante, es probable que los desarrolladores elijan máquinas virtuales, que están aisladas por hardware abstraído, lo que hace que sea mucho más difícil interferir entre sí.

Por supuesto, es seguro que cuestiones como la seguridad y la gestión evolucionarán a medida que los contenedores tengan más exposición en la producción y un mayor escrutinio por parte de los usuarios. Por ahora, el debate sobre contenedores frente a máquinas virtuales es realmente mejor para las personas de operaciones de desarrollo que los viven y respiran todos los días.

Conclusión

Espero que ahora esté equipado con el conocimiento que necesita para aprender más sobre Docker y tal vez incluso usarlo en un proyecto algún día.

Como siempre, escríbeme en los comentarios si he cometido algún error o si puedo ser útil de todos modos. :)