Aprender Docker, un enfoque práctico - José Juan Sánchez Hernández - E-Book

Aprender Docker, un enfoque práctico E-Book

José Juan Sánchez Hernández

0,0

Beschreibung

Si quiere aprender a utilizar la tecnología de contenedores que ha revolucionado el desarrollo y la distribución de aplicaciones web, ha llegado al libro indicado. Antes de que Docker apareciese en la industria del desarrollo de software, era complicado garantizar la consistencia entre los entornos de desarrollo y producción. Era habitual encontrarse con aplicaciones que no se ejecutaban de forma correcta en el entorno de producción porque utilizaban dependencias con diferentes versiones de las que se habían utilizado en el entorno de desarrollo. La tecnología de contenedores de software soluciona este problema, ya que permite empaquetar y distribuir una aplicación con todas las dependencias que necesita garantizando que se va a ejecutar de la misma manera sobre cualquier tipo de infraestructura. Gracias a esta forma de desarrollar y distribuir las aplicaciones, se ha conseguido eliminar las inconsistencias entre los entornos de desarrollo y producción. Los desarrolladores de aplicaciones ya no necesitan crear máquinas virtuales pesadas en sus equipos de trabajo. Docker les permite disponer de un entorno de desarrollo mucho más ligero, que utiliza menos recursos y ofrece mayores prestaciones. El uso de los contenedores ha permitido acelerar el proceso de desarrollo de las aplicaciones, así como facilitar la forma de distribuirlas y la automatización del despliegue en producción. Cada vez hay más equipos de desarrollo y operaciones que están utilizando la tecnología de contenedores Docker en sus flujos de trabajo. Si todavía no sabe utilizar Docker y quiere aprender para mejorar profesionalmente, no espere más, este libro le ayudará a conseguirlo. José Juan Sánchez Hernández es ingeniero en Informática y tiene un máster en Técnicas Informáticas Avanzadas por la Universidad de Almería, donde trabajó como investigador. Sus principales líneas de investigación están en el área de la compresión y transmisión de imágenes, sobre las que ha realizado publicaciones en congresos nacionales e internacionales. También ha trabajado como analista de sistemas y programador en el ámbito de la empresa pública y privada. Actualmente desarrolla su carrera profesional como profesor de enseñanza secundaria en la especialidad de Informática y es docente en los ciclos formativos de grado superior de Administración de Sistemas Informáticos en Red y Desarrollo de Aplicaciones Web.

Sie lesen das E-Book in den Legimi-Apps auf:

Android
iOS
von Legimi
zertifizierten E-Readern

Seitenzahl: 341

Das E-Book (TTS) können Sie hören im Abo „Legimi Premium” in Legimi-Apps auf:

Android
iOS
Bewertungen
0,0
0
0
0
0
0
Mehr Informationen
Mehr Informationen
Legimi prüft nicht, ob Rezensionen von Nutzern stammen, die den betreffenden Titel tatsächlich gekauft oder gelesen/gehört haben. Wir entfernen aber gefälschte Rezensionen.



 

 

 

Aprender Docker, un enfoque práctico

Primera edición, 2022

©    2022 José Juan Sánchez Hernández

©    MARCOMBO, S.L. 2022

        www.marcombo.com

Diseño de la cubierta: ENEDENÚ DISEÑO GRÁFICO

Corrección: Mónica Muñoz

Maquetación: Reverté-Aguilar

Directora de producción: M.a Rosa Castillo

Docker y el logotipo de Docker son marcas comerciales o marcas comerciales registradas de Docker, Inc. en los Estados Unidos y/o en otros países. Docker, Inc. y otras partes también pueden tener derechos de marca registrada en otros términos utilizados en este libro.

Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta obra solo puede ser realizada con la autorización de sus titulares, salvo excepción prevista por la ley. Diríjase a Cedro (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta obra.

ISBN: 978-84-267-3448-8

Producción del ePub: booqlab

A mis padres, José y María;a mi mujer, Noelia ya mi hija, María

Índice general

Lista de siglas y acrónimos

Acerca de este libro

CAPÍTULO 1Conceptos básicos

1.1. ¿Qué es Docker?

1.1.1. Docker como tecnología de contenedores

1.1.2. Docker, Inc.

1.2. ¿Qué ventajas nos aporta el uso de Docker?

1.3. Breve historia de las tecnologías de aislamiento

1.4. ¿Qué es una máquina virtual?

1.5. ¿Qué es un contenedor?

1.6. Analogía con los contenedores de transporte marítimo

1.7. Diferencias entre contenedores y máquinas virtuales

1.8. Arquitectura de Docker

1.9. Cliente de Docker

1.9.1. Docker CLI

1.9.2. Docker Compose

1.10. Docker Engine

1.10.1. Docker Engine API

1.10.2. Docker daemon

1.11.Container runtime

1.11.1.containerd

1.11.2.runc

1.12. Docker Registry

1.12.1. Docker Hub

1.12.2. Otros registros

1.13. Objetos de Docker

1.13.1. Imágenes

1.13.2. Contenedores

1.13.3. Volúmenes

1.13.4. Redes

1.14. Orquestación de contenedores

1.15. Organizaciones y estándares

1.15.1. Open Container Initiative (OCI)

1.15.2. Cloud Native Computing Foundation (CNCF)

1.16. Alternativas a Docker

CAPÍTULO 2Instalación de Docker

2.1. Docker Engine Community

2.2. Instalación de Docker Engine en Linux

2.2.1. Métodos de instalación en Ubuntu

2.2.2. Instalación en Ubuntu utilizando el script oficial

2.2.3. Instalación en Ubuntu desde los repositorios oficiales de Docker

Configuración del repositorio oficial de Docker

Instalación de Docker Engine

Comprobación de la instalación

2.2.4. Pasos posteriores a la instalación en Linux

Configuración del usuario

Configuración de Docker Engine para que se inicie automáticamente

2.3. Instalación de Docker Desktop en Windows 10

2.3.1. Instalación de Docker Desktop utilizando WSL 2 como backend

2.4. Instalación de Docker Engine en Windows Server

Comprobación de la instalación

2.5. Instalación de Docker Desktop en macOS

2.6. Play with Docker

CAPÍTULO 3Imágenes Docker

3.1. ¿Qué es una imagen Docker?

3.2. ¿Qué es un repositorio de imágenes?

3.3. ¿Qué es un tag?

3.4. ¿Qué es el digest de una imagen?

3.5. ¿Qué es el namespace de un repositorio?

3.6. ¿Qué es un registry?

3.7. Docker Hub

3.7.1. Repositorios de imágenes oficiales

3.7.2. Repositorios de imágenes de organizaciones verificadas

3.7.3. Repositorios de imágenes de la comunidad

3.8. El archivo Dockerfile

3.9. Creación de una imagen a partir de un archivo Dockerfile

3.10. Cómo publicar una imagen en Docker Hub

3.11. Capas de una imagen

3.12. Soporte para múltiples arquitecturas

3.13. Formato de las imágenes Docker

3.14. Buscar imágenes en Docker Hub ‒ docker search

3.14.1. Buscar imágenes por nombre

3.14.2. Buscar imágenes utilizando filtros

3.15. Administración de imágenes – docker image

3.16. Descargar imágenes – docker pull

3.16.1. Descargar imágenes desde Docker Hub

3.16.2. Descargar imágenes desde un registry diferente a Docker Hub

3.17. Mostrar imágenes – docker images

3.17.1. Mostrar las imágenes que tenemos descargadas

3.17.2. Mostrar las imágenes intermedias

3.17.3. Mostrar el identificador de las imágenes

3.17.4. Mostrar el digest de las imágenes

3.18. Eliminar imágenes – docker rmi

3.18.1. Eliminar una imagen por su tag

3.18.2. Eliminar una imagen por su identificador

3.18.3. Eliminar todas las imágenes, excepto las dangling images

3.18.4. Eliminar las dangling images

3.18.5. Eliminar las imágenes que no están siendo utilizadas

3.19. Consultar el historial de una imagen – docker history

3.20. Mostrar información detallada – docker inspect

CAPÍTULO 4Contenedores Docker

4.1. Ciclo de vida de un contenedor

4.2. Administración de contenedores – docker container

4.3. Creación y ejecución de contenedores – docker run

4.4. Hello World!

4.5. Obtener el listado de contenedores – docker ps

4.5.1. Obtener el listado de los contenedores que están en ejecución

4.5.2. Obtener el listado de todos los contenedores

4.5.3. Obtener el identificador de los contenedores que están en ejecución

4.5.4. Obtener el identificador de todos los contenedores

4.6. Ejecutar un comando dentro de un contenedor

4.7. Ejecutar un contenedor en modo interactivo

4.8. Eliminar contenedores – docker rm

4.8.1. Eliminar un contenedor que está detenido

4.8.2. Eliminar un contenedor que está en ejecución

4.8.3. Eliminar un contenedor y sus volúmenes anónimos asociados

4.8.4. Eliminar los contenedores que están detenidos

4.8.5. Eliminar los contenedores que están en ejecución y detenidos

4.9. Mostrar la salida estándar de un contenedor – docker logs

4.10. Creación de un contenedor en segundo plano

4.11. Publicar los puertos de un contenedor

4.11.1. Opción -p, --publish

4.11.2. Opción -P, --publish-all

4.12. Cómo conectarnos a un contenedor en ejecución

4.12.1.docker attach

4.12.2.docker exec

4.13. Copiar archivos y directorios – docker cp

4.14. Cómo detener un contenedor – docker stop

4.15. Cómo iniciar un contenedor – docker start

4.16. Políticas de reinicio

CAPÍTULO 5Almacenamiento en Docker

5.1. Almacenamiento en Docker

5.2.Bind mounts

5.3. Formas de crear un bind mount en un contenedor

5.4. Crear un bind mount de solo lectura

5.5. Cómo crear las rutas de los directorios en un bind mount

5.6. Ejemplo para compartir el código fuente de una aplicación

5.7. Ejemplo para compartir un archivo de configuración

5.8. Volúmenes

5.9. Administración de volúmenes – docker volume

5.10. Creación de un volumen

5.10.1. Creación de un volumen con el comando docker volume create

5.10.2. Creación de un volumen al crear un contenedor con docker run

5.11. Inspeccionar un volumen

5.12. Obtener el listado de volúmenes

5.13. Eliminar volúmenes que no están en uso

5.14. Eliminar volúmenes

5.15. Ejemplo de creación de un contenedor con MySQL

5.15.1. Creación de un contenedor MySQL con un volumen anónimo

5.15.2. Creación de un contenedor MySQL con un volumen con nombre

5.15.3. Inicializar un contenedor de MySQL con una base de datos

CAPÍTULO 6Redes en Docker

6.1. Redes en Docker

6.1.1. Container Network Model (CNM)

6.1.2. libnetwork

6.1.3.Drivers de red

6.2. Redes de tipo bridge

6.3. Administración de redes – docker network

6.4. Obtener el listado de redes

6.5. Creación de una red

6.6. Conectar un contenedor a una red

6.6.1. Con el comando docker network connect

6.6.2. Con la opción --network cuando se crea un contenedor

6.7. Desconectar un contenedor de una red

6.8. Inspeccionar una red

6.9. Eliminar las redes que no están en uso

6.10. Eliminar redes

6.11. Cómo conectar un contenedor Adminer con MySQL

6.11.1. Utilizando enlaces entre contenedores con la opción --link

6.11.2. Utilizando una red bridge definida por el usuario

6.12. Cómo conectar WordPress, MySQL y phpMyAdmin

CAPÍTULO 7Docker Compose

7.1. ¿Qué es Docker Compose?

7.1.1. La especificación Compose

7.1.2. La implementación Docker Compose

7.2. Instalación de Docker Compose v2

7.3. El archivo de configuración de Docker Compose

7.3.1. version

7.3.2. services

7.3.3. volumes

7.3.4. networks

7.3.5. configs

7.3.6. secrets

7.4. Comandos de Docker Compose

7.5. Ejemplo de un servicio Apache HTTP Server

7.6. Ejemplo de un servicio MySQL

7.7. Cómo utilizar archivos con variables de entorno

7.8. Ejemplo de una aplicación con dos servicios

7.9. Despliegue de una aplicación WordPress

7.10. Creación de un entorno de desarrollo LAMP

7.11. Creación de un entorno de desarrollo LEMP

7.12. Repositorio con proyectos de ejemplo

ANEXO Adocker system

Lista de siglas y acrónimos

ACI

Azure Container Instances

API

Application Programming Interface

CI/CD

Continuous Integration/Continuous Delivery

CLI

Command Line Interface

CMS

Content Management System

CNCF

Cloud Native Computing Foundation

CNM

Container Network Model

CRI

Container Runtime Interface

DCE

Docker Community Edition

DEE

Docker Enterprise Edition

ECS

Elastic Container Service

HTTP

Hypertext Transfer Protocol

HTTPS

Hypertext Transfer Protocol Secure

IPAM

Internet Protocol Address Management

ISO

International Organization for Standardization

JSON

JavaScript Object Notation

MAC

Media Access Control

MCR

Mirantis Container Runtime

OCI

Open Container Initiative

PaaS

Platform as a Service

RC

Release Candidate

REST

Representational State Transfer

SDK

Software Development Kit

SDN

Software Defined Networking

SLAT

Second Level Address Translation

UUID

Universally Unique Identifier

VM

Virtual Machine

VMM

Virtual Machine Monitor

WSL

Windows Subsystem for Linux

Acerca de este libro

Este libro está dirigido a estudiantes, profesores, profesionales y a todas aquellas personas que quieran conocer qué es la tecnología de contenedores Docker y todavía no sepan cómo empezar a utilizarla en su entorno de trabajo.

El libro está dividido en siete capítulos, que lo ayudarán a tener una visión global de todos los componentes que forman el ecosistema Docker y a conocer con detalle cómo utilizar cada uno de los principales objetos de Docker: imágenes, contenedores, volúmenes y redes.

En el primer capítulo, encontrará los conceptos básicos que debe conocer antes de entrar en materia. La lectura de este capítulo es fundamental para entender con mayor claridad el resto del libro. El capítulo 2 lo ayudará a conocer los detalles del proceso de instalación de Docker Engine para Linux y Docker Desktop para Windows y macOS.

El capítulo 3 está dedicado al estudio de las imágenes Docker. Aquí aprenderemos lo que es un repositorio, un tag o un digest de una imagen. Veremos cómo podemos crear una imagen, cómo publicarla en Docker Hub y todos los comandos básicos para poder gestionar las imágenes en un host de Docker.

En el capítulo 4, estudiaremos los comandos necesarios para gestionar el ciclo de vida básico de un contenedor. En el capítulo 5, se analizan todas las posibilidades que nos ofrece Docker para implementar persistencia de datos entre un contenedor y el host de Docker. Aprenderemos las diferencias entre un bind mount y un volumen, además de los comandos necesarios para gestionarlos.

En el capítulo 6, estudiaremos los diferentes tipos de redes que existen en Docker y los comandos básicos para poder gestionarlas. El último capítulo está dedicado a la herramienta Docker Compose, donde se explica cómo gestionar y ejecutar aplicaciones con múltiples contenedores Docker de una forma muy sencilla.

CAPÍTULO 1

Conceptos básicos

1.1. ¿Qué es Docker?

Cuando hablamos de Docker, podemos referirnos a la tecnología que permite desarrollar, desplegar y ejecutar aplicaciones en entornos aislados denominados «contenedores». O también podemos referirnos a la empresa Docker, Inc., que es la que desarrolla esta tecnología.

1.1.1. Docker como tecnología de contenedores

Docker permite empaquetar las aplicaciones en contenedores que incluyen todo lo necesario para que se puedan ejecutar en un entorno de manera aislada. Cada contenedor almacena el código fuente de la aplicación, los archivos de configuración y todas las dependencias software que necesita. Esta estrategia permite que las aplicaciones se puedan ejecutar de la misma manera sobre cualquier infraestructura que tenga soporte para Docker, tanto de forma local como en la nube.

Con la tecnología de contenedores para aplicaciones, ya no es necesario preocuparse por el software que está instalado en la máquina donde se ejecuta el contenedor, porque todo lo que necesita la aplicación está incluido dentro del propio contenedor. Esta forma de trabajar resuelve el problema de «it works on my machine», donde una aplicación puede funcionar correctamente en el entorno de desarrollo, pero tiene errores en el entorno de producción, porque los dos entornos no son idénticos y contienen versiones de software diferentes.

Cada vez hay más equipos de desarrollo y operaciones que están utilizando la tecnología de contenedores Docker en sus flujos de trabajo. Esto ha permitido acelerar el proceso de desarrollo de las aplicaciones, ha facilitado la forma de distribuirlas y ha acelerado la automatización del despliegue en producción.

1.1.2. Docker, Inc.

Docker, Inc. es una empresa tecnológica estadounidense que fue fundada por Solomon Hykes en 2008 como dotCloud. En sus orígenes, ofrecía servicios de PaaS (Platform as a Service) y, en marzo de 2013, liberó como un proyecto open source un componente de su plataforma al que llamó Docker. Este proyecto tuvo una gran aceptación y supuso un cambio significativo en la forma de desarrollar, distribuir y desplegar software.

En octubre de 2013, dotCloud pasó a llamarse Docker, Inc. y, a partir de ese momento, fue cuando la empresa decidió centrarse en el desarrollo de su tecnología de contenedores.

1.2. ¿Qué ventajas nos aporta el uso de Docker?

Las principales ventajas que nos aporta el uso de Docker son las siguientes:

Soluciona el problema «it works on my machine».

Permite tener un entorno de desarrollo limpio, seguro y portátil.

Facilita la automatización de pruebas, integración y empaquetado.

Posibilita empaquetar una aplicación con todas las dependencias que necesita (código fuente, librerías, configuración, etc.) para ser ejecutada en cualquier plataforma.

Se eliminan inconsistencias entre los entornos de desarrollo, pruebas y producción.

El proceso de despliegue es rápido y repetible.

1.3. Breve historia de las tecnologías de aislamiento

Antes de Docker, ya existían implementaciones de aislamiento de recursos, como chroot (1982), FreeBSD jail (2000), Solaris Zones (2004), cgroups (2006) o Linux Containers (LXC), en el año 2008.

En primer lugar, apareció la llamada del sistema chroot, que fue desarrollada para la versión 7 de UNIX (1979) y, posteriormente, se incorporó a la versión 4 de BSD (1980). Esta operación permite cambiar el directorio raíz del sistema de archivos de un proceso y de sus procesos hijos aislando, de esta manera, su ejecución e impidiendo que puedan acceder a los archivos que están por encima del nuevo directorio raíz.

Años más tarde, FreeBSD se basó en la idea de chroot para desarrollar FreeBSD jail (2000), una implementación que permitía dividir un sistema en varios subsistemas llamados «jaulas» o jails. Todas las jaulas comparten el mismo kernel del sistema operativo anfitrión. Cada jaula puede tener un directorio raíz aislado del resto de sistema de archivos, su propio grupo de usuarios, su propio nombre de host y su propia dirección IP.

En 2004, se liberó el proyecto Solaris Zones, que permitía aislar la ejecución de los procesos del resto del sistema metiéndolos en zonas o sandboxes. Cada zona se comporta como un servidor virtual completamente aislado.

En 2006, los ingenieros de Google empezaron a trabajar en lo que más tarde se llamaría cgroups o «grupos de control». Los grupos de control son un mecanismo que permiten controlar la asignación de recursos a los procesos. Permiten definir jerarquías en las que se agrupan los procesos y establecer cuáles serán los límites de uso de los recursos del sistema (CPU, memoria, red, etc.). Esta funcionalidad se integró en el kernel de Linux en la versión 2.6.24 aunque, años más tarde, fue rediseñada y la versión que se encuentra actualmente en el kernel es conocida como cgroup v2.

Linux Containers, o LXC (2008), es un método de virtualización a nivel de sistema operativo que permite ejecutar aplicaciones en entornos aislados que comparten el kernel de Linux del sistema operativo anfitrión. Utiliza las características del kernel de cgroups para controlar la asignación de recursos a los procesos y namespaces para supervisar a qué recursos pueden acceder los procesos.

Las primeras versiones de Docker utilizaban LXC para ejecutar contenedores, pero fue reemplazado por una herramienta interna a la que llamaron libcontainer. Actualmente, libcontainer forma parte de runc, un runtime de contenedores que cumple con las especificaciones OCI (Open Container Initiative) del que hablaremos más adelante.

1.4. ¿Qué es una máquina virtual?

Una «máquina virtual» o virtual machine (VM) es un entorno que emula la misma funcionalidad de una máquina física. Una máquina virtual hace uso de los recursos que se le hayan asignado; cuenta con su propia CPU, memoria, interfaz de red, almacenamiento y su propio sistema operativo.

Las máquinas virtuales se crean y ejecutan sobre un software llamado «hipervisor» o virtual machine monitor (VMM). El hipervisor se ejecuta sobre una máquina física y actúa como una capa intermedia entre el hardware de la máquina anfitriona o host y la máquina virtual. El hipervisor se encarga de gestionar y distribuir los recursos de la máquina física (CPU, memoria, almacenamiento, etc.) entre las máquinas virtuales.

Es posible crear varias máquinas virtuales sobre una misma máquina física. Cada una de las máquinas virtuales estará aislada del resto, tendrá sus propios recursos y contará con su propio sistema operativo, que no tiene por qué ser el mismo que el de la máquina anfitriona.

1.5. ¿Qué es un contenedor?

Un «contenedor» se puede definir como una unidad estándar de software que permite empaquetar el código fuente de una aplicación y todas sus dependencias, para que se pueda distribuir y ejecutar de forma rápida y fiable en diferentes entornos.

También se puede definir como un proceso que ha sido aislado de todos los demás procesos de la máquina anfitriona donde se está ejecutando. Aunque es posible tener más de un proceso en un contenedor, las buenas prácticas nos recomiendan ejecutar solo un proceso por contenedor.

1.6. Analogía con los contenedores de transporte marítimo

El término docker, en inglés, significa «estibador», que es la persona encargada de realizar la carga y descarga de un buque u otros medios de transporte. Para entender mejor qué es un «contenedor», podemos establecer una analogía entre los contenedores que se utilizan en el transporte marítimo y los contenedores software.

Los contenedores de transporte marítimo:

Cumplen una normativa ISO (International Organization for Standardization), donde se establecen las medidas, tamaño y forma de los contenedores.

Se rigen por un estándar para distribuir mercancías.

Pueden ser transportados en cualquier embarcación que cumpla el estándar ISO.

Los contenedores software:

Cumplen con los estándares abiertos de la industria de los contenedores software desarrollados por la OCI (Open Container Initiative).

Se rigen por un estándar para distribuir software.

Pueden ser ejecutados en cualquier runtime que cumpla el estándar OCI.

1.7. Diferencias entre contenedores y máquinas virtuales

A continuación, se enumeran las principales diferencias que existen entre contenedores y máquinas virtuales:

Una máquina virtual necesita un sistema operativo completo para poder funcionar, mientras que un contenedor no, ya que comparte el kernel del sistema operativo de la máquina donde se está ejecutando.

Los contenedores necesitan menos recursos que las máquinas virtuales. Con el mismo hardware, es posible tener un mayor número de contenedores que de máquinas virtuales.

El tiempo necesario para iniciar un contenedor es mucho menor que el de una máquina virtual. Un contenedor se puede iniciar en cuestión de milisegundos, mientras que una máquina virtual puede llegar a necesitar algunos minutos.

Por lo general, los contenedores son más ligeros que las máquinas virtuales. El tamaño de los contenedores se suele medir en megabytes, mientras que el de las máquinas virtuales se suele medir en gigabytes.

Un contenedor se puede ejecutar en una máquina virtual pero no al revés.

Los contenedores no han venido para sustituir a las máquinas virtuales, ya que cada tecnología es adecuada para casos de uso diferentes. Podemos decir que ambas tecnologías se complementan entre sí.

Figura 1.1. Diferencias entre contenedores y máquinas virtuales.

1.8. Arquitectura de Docker

Al inicio, Docker fue diseñado con una arquitectura monolítica, pero, más tarde, fue rediseñado a una arquitectura modular, formada por diferentes componentes que pueden ser reemplazados o incluso utilizarse en otros proyectos.

Cada uno de los componentes de Docker se desarrolla por separado y muchos de ellos forman parte del proyecto Moby. El proyecto Moby es un proyecto open source creado por Docker, Inc. en 2017, donde se desarrollan componentes y herramientas que pueden ser utilizados para crear productos basados en la tecnología de contenedores. Podemos decir que Moby es el proyecto donde se desarrollan los componentes y Docker es un producto que utiliza esos componentes:

Los principales componentes de Docker que debemos conocer son los siguientes:

Cliente de Docker.

Docker CLI.

Docker Compose.

Docker Engine.

Docker Engine API.

Docker daemon.

Container Runtime.

Containerd.

Runc.

Docker Registry.

En la figura 1.2, se trata de dar una visión global de cómo se relacionan los componentes que forman la arquitectura de Docker. En la figura se muestran tres bloques que representan al cliente, el host de Docker y el Registry. El host de Docker será una máquina que tiene instalado el componente Docker Engine y puede estar dentro o fuera de nuestra red local. En una instalación habitual, el cliente también estará instalado en el host de Docker, pero no es un requisito obligatorio. En los siguientes apartados de este capítulo, se irá describiendo con más detalle cada uno de estos componentes.

Figura 1.2. Componentes de la arquitectura de Docker.

1.9. Cliente de Docker

Docker utiliza una arquitectura cliente-servidor, donde una aplicación cliente interactúa con un servicio llamado Docker daemon. Un mismo cliente puede comunicarse con más de un servicio Docker daemon. La comunicación entre el cliente y el servidor se realiza a través de una API HTTP, conocida como Docker Engine API.

Las aplicaciones cliente permiten que un usuario pueda interaccionar con el servicio Docker daemon para realizar acciones como crear y ejecutar contenedores. Las aplicaciones oficiales que se pueden utilizar como cliente son Docker CLI (Command Line Interface) y Docker Compose. Aunque cualquier aplicación cliente que haga uso de la API de Docker Engine puede ser un cliente válido.

El cliente y el servidor se pueden ejecutar en la misma máquina o pueden estar en máquinas separadas. Cuando el cliente y el servidor están en la misma máquina, la comunicación entre ambos se puede realizar a través de un socket IPC o un socket TCP. Y, cuando se encuentran en máquinas separadas, se realiza a través de un socket TCP.

A continuación, se muestran dos ejemplos donde se describe a alto nivel cómo es el flujo de ejecución de un comando y cuáles son los componentes que intervienen.

Ejemplo de ejecución del comando docker pull

En la figura 1.3, se muestra el flujo de ejecución a alto nivel del comando docker pull, que es el comando utilizado para descargar una imagen de un Docker Registry.

En este ejemplo, se realizan los siguientes pasos:

1. En primer lugar, el usuario ejecuta el comando docker pull desde la línea de comandos y el cliente Docker CLI se comunica con el servicio Docker daemon.

2. El servicio Docker daemon se encarga de gestionar la petición al Docker Registry para descargar la imagen solicitada por el cliente.

3. La imagen se descarga del Docker Registry y se almacena en el host de Docker.

Figura 1.3. Ejemplo del flujo de ejecución a alto nivel del comando docker pull.

Ejemplo de ejecución del comando docker run

En la figura 1.4, se muestra el flujo de ejecución a alto nivel del comando docker run, que es el comando utilizado para crear y ejecutar un contenedor a partir de una imagen.

En este ejemplo, se realizan los siguientes pasos:

1. En primer lugar, el usuario ejecuta el comando docker run desde la línea de comandos y el cliente Docker CLI se comunica con el servicio Docker daemon.

2. El servicio Docker daemon comprueba si la imagen que ha seleccionado el usuario para crear el contenedor se encuentra en el host de Docker. Si la imagen no estuviese en el host, entonces la descargaría automáticamente del Docker Registry.

3. Se crea un contenedor a partir de la imagen seleccionada y se inicia la ejecución.

Figura 1.4. Ejemplo del flujo de ejecución a alto nivel del comando docker run.

1.9.1. Docker CLI

Docker CLI es el cliente oficial de Docker. Es una interfaz de línea de comandos que permite a los usuarios interaccionar con el servicio Docker daemon. El comando base de esta aplicación es docker.

El caso de uso más habitual de Docker CLI es cuando se quiere interactuar con un único contenedor, mientras que Docker Compose se suele utilizar para trabajar con aplicaciones que ejecutan varios contenedores a la vez.

En las distribuciones Linux, podemos instalar la versión Community Edition de Docker CLI con el paquete docker-ce-cli, que es un paquete independiente de Docker Engine, mientras que, en los sistemas operativos Windows y Mac, viene incluido en la aplicación Docker Desktop, que incluye también Docker Engine, entre otros componentes.

1.9.2. Docker Compose

Docker Compose es una aplicación utilizada desde la línea de comandos y permite a los usuarios interaccionar con el servicio Docker daemon.

Esta aplicación permite definir y ejecutar aplicaciones con múltiples contenedores. Utiliza un archivo de configuración con formato YAML, para definir los servicios, las redes y los volúmenes de los que consta la aplicación que queremos ejecutar. Una de las ventajas que nos ofrece Docker Compose es que solo hay que ejecutar un comando para crear y ejecutar todos los servicios que se han definido en el archivo YAML de configuración.

Actualmente, existen dos versiones de Docker Compose. La versión v1 está desarrollada en Python y tiene que ser instalada como una herramienta adicional con el nombre de docker-compose, mientras que la nueva versión v2 está desarrollada en Go e integra el comando compose dentro del cliente oficial de Docker CLI. Por lo tanto, para utilizar la nueva versión, se utiliza el comando docker compose.

En el momento de escribir este libro, la versión v2 de Docker Compose solo está incluida en la aplicación Docker Desktop para Windows y macOS. Se espera que, muy pronto, también estará disponible en el cliente oficial Docker CLI para Linux. De momento, se puede instalar de forma manual como un plugin de Docker CLI.

1.10. Docker Engine

Docker Engine es el componente principal de Docker, encargado de crear, ejecutar y gestionar contenedores. Tiene un diseño modular y está formado por varios componentes que cumplen los estándares abiertos de la Open Container Iniciative (OCI). Entre sus componentes, destacamos los siguientes:

Docker Engine API.

Docker daemon.

Container Runtime.

Gestión de redes y almacenamiento.

Docker Build (BuildKit), para la creación de imágenes.

Distribution, para la interacción con los registros de contenedores.

Soporte nativo para la orquestación de contenedores con Docker Swarm (SwarmKit).

Gestión de plugins.

El componente principal de Docker Engine es el Docker daemon, que es un proceso encargado de administrar las imágenes, contenedores, redes y volúmenes. En muchas ocasiones, cuando nos referimos a Docker Engine, nos estamos refiriendo realmente al proceso Docker daemon.

En las primeras versiones, al instalar Docker Engine, se incluía el cliente Docker CLI, el Docker daemon y el Container Runtime, pero, actualmente, son tres componentes independientes que se tienen que instalar en paquetes separados.

Docker Engine se ejecuta de forma nativa en los sistemas Linux y Windows Server. En el resto de los sistemas operativos Windows, que no sean Windows Server, y en los sistemas operativos Mac, no se ejecuta de forma nativa, sino que lo hace sobre una máquina virtual Linux.

En las distribuciones Linux, podemos instalar la versión Community Edition de Docker Engine con el paquete docker-ce, mientras que, en los sistemas operativos Windows y Mac, viene incluido en la aplicación Docker Desktop.

Figura 1.5. Componentes de Docker Engine.

1.10.1. Docker Engine API

La comunicación entre un cliente Docker y el servicio Docker daemon se realiza a través de una API HTTP conocida como Docker Engine API.

Esta API implementa todas las operaciones que un usuario puede realizar desde el cliente oficial de Docker CLI; por ejemplo, cuando un usuario ejecuta el comando docker ps desde la línea de comandos, el cliente Docker CLI está haciendo una petición GET al endpoint/containers/json de la API. El servicio Docker daemon ejecuta la petición del cliente y le devuelve una respuesta en formato JSON.

La API suele cambiar cada vez que se libera una nueva versión de Docker Engine. Para que los clientes que tienen una versión antigua de la API puedan seguir manteniendo compatibilidad con las nuevas versiones de Docker Engine, se incluye un prefijo en la URL de los endpoints de la API indicando la versión que quieren utilizar para comunicarse; por ejemplo, una llamada al endpoint/v1.41/containers/json utilizaría la versión 1.41 de la API, mientras que una llamada a /v1.40/containers/json usaría la versión 1.40.

Puede consultar la especificación completa de la API en la web oficial de Docker. En el momento de escribir este libro, la última versión disponible es la v1.41:

En la actualidad, existe un SDK oficial para Go y otro para Python, que permite a los desarrolladores crear aplicaciones que interactúan con la API de Docker Engine. También existe una gran variedad de librerías no oficiales que han sido desarrolladas por la comunidad para otros lenguajes de programación. A continuación, se muestra un ejemplo de cómo se puede hacer uso de la API de Docker Engine con la utilidad curl.

Ejemplo

En este ejemplo, vamos a utilizar la utilidad curl como cliente para realizar una llamada al endpointhttp://localhost/v1.41/containers/json de la API de Docker Engine, para obtener un listado de todos los contenedores que están en ejecución:

1. En este ejemplo, utilizamos un socket UNIX porque el cliente y el servicio Docker daemon se encuentran en la misma máquina. Como este ejemplo se ha realizado en una máquina Linux, el socket que utiliza el servicio Docker daemon está ubicado en /var/run/docker.sock.

2. Indicamos el endpoint de la API de Docker Engine al que queremos hacer la llamada.

Si no existe ningún contenedor en ejecución, obtendremos una respuesta vacía:

Pero, si existen contenedores en ejecución, obtendremos una respuesta similar a la que se muestra a continuación. Tenga en cuenta que se han omitido algunos valores de la respuesta para simplificarla y mejorar su legibilidad:

Si desea profundizar más sobre el empleo de los SDKs de Docker, puede consultar la web oficial, donde encontrará diferentes ejemplos de uso:

1.10.2. Docker daemon

El servicio Docker daemon es el encargado de crear y gestionar todos los objetos con los que trabaja Docker, como las imágenes, los contenedores, las redes y los volúmenes. Este servicio se ejecuta en un proceso llamado dockerd.

El cliente Docker se comunica con el servicio Docker daemon a través de una API HTTP y el servicio Docker daemon se comunica con el container runtime. En las primeras versiones, el Docker daemon también incluía el container runtime, pero, actualmente, son dos componentes independientes. El container runtime de Docker está formado por containerd y runc, de los que hablaremos más adelante.

El servicio Docker daemon expone una API HTTP para comunicarse con los clientes Docker y puede utilizar tres tipos de sockets para interaccionar con ellos: unix, tcp y fd.

En una instalación habitual, el cliente Docker y el Docker daemon suelen estar en la misma máquina. Cuando trabajamos con contenedores Linux, se utiliza un socket de tipo UNIX que estará en la ruta /var/run/docker.sock. Para poder hacer uso de este socket, es necesario tener permisos de root o pertenecer al grupo de usuarios del sistema docker. Cuando trabajamos con contenedores Windows, se utiliza un named pipe, que estará en la ruta \\.\pipe\docker_engine.

Si vamos a trabajar en un entorno donde el cliente y el Docker daemon se ejecutan en diferentes máquinas, entonces necesitaremos utilizar un socket TCP. En este caso, la comunicación se realiza por defecto sobre un canal no seguro en el puerto 2375. Esta configuración puede ser adecuada para un entorno de desarrollo, pero nunca se debe utilizar en un entorno de producción. En un entorno de producción, se debe usar una conexión cifrada con TLS y se suele emplear el puerto 2376.

1.11.Container runtime

El container runtime es el software encargado de ejecutar los contenedores. Docker Engine utiliza dos tipos de container runtime:

containerd:container runtime de alto nivel.

runc:container runtime de bajo nivel.

1.11.1.containerd

Es un container runtime de alto nivel, que se ejecuta como un proceso daemon. Este componente se encarga de administrar el ciclo de vida de un contenedor dentro de un host. Realiza tareas como descargar las imágenes de los registros de los contenedores, almacenarlas en el host, supervisar la ejecución de los contenedores o gestionar el almacenamiento y las redes.

Este componente está diseñado para ser embebido dentro de otros sistemas más complejos. Aunque puede ser utilizado desde la línea de comandos con el cliente ctr, que se incluye en la instalación por defecto.

Se trata de un proyecto open source, que fue creado por Docker, Inc. en 2016, junto a Google e IBM. En 2017, fue donado a la Cloud Native Computing Foundation (CNCF) y, en 2019, se convirtió en un proyecto graduado de la CNCF. Esto indica que el proyecto se encuentra en un nivel de madurez adecuado para ser utilizado por las empresas más conservadoras a la hora de adoptar nuevas tecnologías.

El componente containerd también implementa la interfaz CRI (Container Runtime Inferface) de Kubernetes. Esto quiere decir que este container runtime puede ser utilizado en un cluster de Kubernetes para crear y ejecutar contenedores a partir de imágenes Docker, que son imágenes compatibles con la especificación OCI.

1.11.2.runc

Es un container runtime de bajo nivel, encargado de interaccionar con el kernel del host de la máquina anfitriona donde se ejecutan los contenedores. Utiliza el componente libcontainer para interaccionar con el sistema operativo del host.

Se trata de una implementación de código abierto de la especificación OCI Runtime Specification, que describe cómo tiene que ser la configuración, el entorno de ejecución y el ciclo de vida de un contenedor.

Figura 1.6. Ejemplo de cómo interaccionan containerd y runc con el resto de los componentes.

1.12. Docker Registry

Un Docker Registry, o registro de contenedores Docker, es un servicio encargado de almacenar y distribuir repositorios de imágenes Docker. Los usuarios pueden utilizar este servicio para publicar sus repositorios de imágenes y compartirlos con otros usuarios de una forma sencilla.

Un repositorio de imágenes es un conjunto de imágenes que se agrupan bajo el mismo nombre dentro del mismo registro. Cada una de las imágenes de un repositorio está etiquetada con un tag, que se suele utilizar para indicar su versión.

En el capítulo 3, estudiaremos con más detalle qué es un registro y los diferentes tipos que existen. En este apartado, solo se trata de mostrar una descripción general que nos ayude a entender, de forma global, cuál es el papel de cada uno de los componentes de Docker.

1.12.1. Docker Hub

Docker Hub es el registro de contenedores oficial de Docker. Es el registro que viene configurado por defecto cuando se instala Docker Engine, aunque puede ser reemplazado por otros registros de contenedores.

Los usuarios pueden utilizar repositorios públicos de Docker Hub para almacenar y compartir sus imágenes de forma gratuita, o pueden contratar una suscripción de pago para disponer de repositorios privados ilimitados.

Entre las principales funcionalidades de Docker Hub, podemos destacar las siguientes:

Permite crear equipos y organizaciones.

Posibilita descargar y utilizar una gran cantidad de imágenes oficiales y de organizaciones verificadas, de forma gratuita.

Facilita crear y publicar imágenes de manera automática a partir del contenido de un repositorio externo de GitHub o Bitbucket; por ejemplo, un repositorio de Docker Hub se puede configurar para crear una imagen de modo automático cada vez que se publican nuevos cambios en el código de una aplicación que está alojada en un repositorio de GitHub o BitBucket. Además, se pueden configurar una serie de test para que la imagen solo se publique cuando los test se hayan ejecutado con éxito.

También proporciona el uso de webhooks para desencadenar acciones en otros servicios externos; por ejemplo, cada vez que se publica una imagen en Docker Hub, se puede activar un webhook, que hace una petición POST a una determinada URL que podemos configurar en Docker Hub.

1.12.2. Otros registros

Además de Docker Hub, podemos hacer uso de otros registros de contenedores como:

GitHub Container Registry.

GitLab Container Registry.

DigitalOcean Container Registry.

Amazon Elastic Container Registry.

Azure Container Registry.

Google Cloud Container Registry.

1.13. Objetos de Docker

Los principales objetos de Docker con los que vamos a trabajar a lo largo del libro son:

Imágenes.

Contenedores.

Volúmenes.

Redes.

Hemos dedicado un capítulo para estudiar con detalle cada uno de estos objetos. En esta sección, solo mostraremos una descripción general de cada uno de ellos.

1.13.1. Imágenes

Las imágenes contienen el sistema de archivos que utilizarán los contenedores Docker. Para crear un contenedor, es necesario utilizar una imagen de forma obligatoria. A partir de la misma imagen, se pueden crear todos los contenedores que se necesiten. Por lo tanto, podemos decir que las imágenes son como unas plantillas que contienen el estado inicial del sistema de archivos raíz del contenedor.

En el capítulo 3, aprenderemos cómo crear una imagen, cómo publicarla en Docker Hub y todos los comandos básicos para poder gestionar las imágenes en un host de Docker.

1.13.2. Contenedores

Un contenedor Docker se crea a partir de una imagen. Se puede definir como un proceso que ha sido aislado de todos los demás procesos de la máquina donde se está ejecutando, también conocida como el «host de Docker». Los contenedores pueden tener más de un proceso en ejecución, pero las buenas prácticas recomiendan ejecutar un solo proceso por contenedor.

En el capítulo 4, estudiaremos todos los comandos necesarios para gestionar el ciclo de vida completo de un contenedor.

Contenedores Linux y contenedores Windows

En Docker, existen dos tipos de contenedores.

Contenedores Linux

Los contenedores Linux se pueden ejecutar en los sistemas operativos Linux, macOS y Windows. Estos contenedores solo se pueden crear a partir de imágenes específicas para contenedores Linux.

Cuando estos contenedores se ejecutan en un sistema operativo Linux, utilizan el kernel del sistema operativo de la máquina anfitriona. Sin embargo, cuando se ejecutan en macOS, lo hacen en una máquina virtual ligera sobre el hipervisor HyperKit, que es específico de macOS. En Windows, su ejecución dependerá del backend utilizado en Docker Desktop. En este caso, existen dos posibilidades:

WSL 2 como backend de Docker Desktop. En este caso, los contenedores Linux se ejecutan de forma nativa, sin ningún tipo de emulación, utilizando el kernel de Linux desarrollado por Microsoft para WSL 2.

Hyper-V como backend de Docker Desktop. En este caso, los contenedores Linux se ejecutan en una máquina virtual en Hyper-V basada en LinuxKit. Esta solución ofrece un rendimiento mucho menor que WSL 2; por este motivo, no se recomienda su uso para ejecutar contenedores Linux.

Figura 1.7. Contenedores Linux.

Contenedores Windows

Estos contenedores solo se pueden crear en los sistemas operativos Windows y Windows Server. Las imágenes que utilizan son imágenes específicas para contenedores Windows.

Los contenedores Windows disponen de dos modos de aislamiento en tiempo de ejecución:

Aislamiento de Hyper-V. En este modo, los contenedores se ejecutan en una máquina virtual en Hyper-V y cada contenedor tiene su propio kernel. Ofrece un buen nivel de aislamiento en el ámbito del hardware entre los contenedores y el host, así como un buen nivel de seguridad. Este es el modo de aislamiento que utilizan por defecto los contenedores Windows que se ejecutan en Windows 10.

Aislamiento de procesos. En este modo, los contenedores no tienen su propio kernel, sino que utilizan el kernel del sistema operativo anfitrión, del mismo modo que lo hacen los contenedores Linux de forma nativa. Este es el modo de aislamiento que utilizan por defecto los contenedores Windows que se ejecutan en Windows Server.

Figura 1.8. Contenedores Windows.

1.13.3. Volúmenes

Los volúmenes son un mecanismo que nos ofrece Docker para implementar persistencia de datos en un contenedor. Los contenedores que no tienen volúmenes asociados perderán todos los datos cuando el contenedor finalice su ejecución y se elimine. Si queremos conservar los datos que se han generado durante la ejecución del contenedor, necesitamos utilizar volúmenes.

Un contenedor puede tener asociados uno o varios volúmenes. Estos permiten almacenar en el host de Docker los datos de los directorios del contenedor que queremos conservar cuando el contenedor se elimine. El ciclo de vida de los volúmenes es independiente del ciclo de vida de los contenedores. Por lo tanto, los volúmenes seguirán estando disponibles, aunque se eliminen los contenedores que los están utilizando.

En el capítulo 5, estudiaremos las posibilidades que nos ofrece Docker para implementar persistencia de datos entre los contenedores y el host de Docker.

1.13.4. Redes

Docker nos permite crear diferentes tipos de redes para que los contenedores puedan comunicarse entre ellos y con el exterior. El componente principal de Docker Engine que se encarga de la gestión de las redes en Docker es libnetwork.

En el capítulo 6, estudiaremos los diferentes tipos de redes que podemos crear en Docker y los comandos básicos para poder gestionarlas.

1.14. Orquestación de contenedores

En muchas situaciones, vamos a necesitar que las aplicaciones se ejecuten sobre un cluster de servidores, para garantizar la alta disponibilidad y escalabilidad de los servicios.

Para desplegar una aplicación basada en contenedores en un cluster, vamos a necesitar el uso de herramientas adicionales, para realizar tareas que no se pueden hacer de forma manual. Estas herramientas son conocidas como «orquestadores de contenedores» y los más conocidos dentro del ecosistema Docker son Docker Swarm y Kubernetes.

Entre las tareas que suele realizar un orquestador de contenedores, podemos destacar las siguientes:

Automatiza el despliegue de una aplicación en un cluster de servidores.

Crea y ejecuta los contenedores entre los diferentes nodos del cluster.

Balancea la carga entre todos los contenedores.

Escala los servicios de forma automática cuando sea necesario.

Permite que una aplicación se recupere automáticamente de los errores.

Posibilita actualizar una aplicación sin que exista tiempo de inactividad.

Docker Swarm

Docker Swarm es un orquestador que viene integrado de forma nativa en Docker Engine. Este orquestador está siendo desarrollado por Docker, Inc. y forma parte del proyecto Moby Project, con el nombre de SwarmKit.

Figura 1.9. Ejemplo de un cluster de Docker Swarm formado por tres nodos Managers y cuatro Workers.

Kubernetes

Kubernetes, también conocido como K8s, es el orquestador más utilizado en la actualidad. Este orquestador fue desarrollado originalmente por Google, pero fue donado a la Cloud Native Computing Foundation (CNCF), que actualmente es la encargada de su desarrollo.

Kubernetes puede utilizar diferentes container runtimes para ejecutar contenedores. El único requisito reside en que sean compatibles con una API llamada Container Runtime Interface (CRI), que es la que permite interaccionar al container runtime