Introducción a Docker donde aprenderás desde los conceptos básicos hasta la orquestación de servicios con esta herramienta.
Este recurso se ha diseñado como una guía práctica de introducción a Docker donde se podrá...
- Comprender los conceptos básicos de Docker.
- Instalar y configurar Docker en diferentes sistemas operativos.
- Usar imágenes predefinidas y gestionar contenedores con Docker.
- Utilizar Docker Compose para orquestar aplicaciones multi-contenedor.
- Entender y montar puntos de montaje con Docker.
- Entender y montar redes con Docker.
Esta formación está orientada principalmente a desarrolladores/as de software que deseen adentrarse en la cultura DevOps/GitOps y quieran desarrollar, testear y desplegar aplicaciones con Docker.
La metodología de esta guía se basa en:
- Teoría y conceptos, con explicaciones claras y concisas de los puntos clave.
- Demostraciones, con ejemplos reales y cotidianos.
Para aprovechar al máximo este recurso se necesita:
- Un equipo con acceso a Internet.
- Privilegios de administrador para instalar software.
- Conocimientos básicos de desarrollo web.
Docker es una plataforma de virtualización que permite empaquetar aplicaciones y sus dependencias en contenedores ligeros y portables que pueden ejecutarse en cualquier máquina compatible con Docker.
Grosso modo virtualizar no es sólo emular el funcionamiento de una aplicación en una infraestructura diferente sino, además, poder hacer uso de dicha infraestructura como si fuera propia.
¿Qué nos aporta Docker?
- Portabilidad
- Los contenedores pueden ejecutarse de la misma manera en cualquier máquina que tenga Docker instalado con independencia del sistema operativo del host.
- Consistencia
- Los contenedores aseguran que las aplicaciones funcionen de la misma manera en cualquier entorno (desarrollo, testing, producción, etc.)
- Aislamiento
- Cada contenedor se ejecuta en su propio entorno aislado, lo que ayuda a evitar conflictos entre dependencias y facilita el mantenimiento y depuración de aplicaciones.
- Escalabilidad
- Docker permite escalar aplicaciones fácilmente al añadir o eliminar contenedores al vuelo según sea necesario.
- Eficiencia
- Los contenedores utilizan recursos de manera eficiente, ya que comparten el mismo núcleo del sistema operativo sin la sobrecarga de un hipervisor.
Docker nos permite crear un entorno estandarizado de desarrollo que podemos compartir con otros miembros del equipo, simplificando y unificando el proceso de instalación, configuración y puesta en marcha.
También nos permite crear entornos específicos para cubrir todo el ciclo de desarrollo de la aplicación (testing, staging...) así como pipelines de CI/CD.
Estos son los componentes de Docker:
- Imágenes
- Las imágenes son archivos inmutables que actúan como plantillas y que contienen todos los elementos necesarios para ejecutar una aplicación: código, entorno de ejecución, bibliotecas y dependencias, configuraciones y archivos de sistema.
- Contenedores
- Los contenedores son instancias en ejecución de una imagen de Docker. Los contenedores proporcionan una forma encapsulada y aislada de ejecutar aplicaciones.
- Volúmenes
- Los volúmenes se utilizan para almacenar datos persistentes fuera del ciclo de vida de los contenedores. Esto permite que los datos sobrevivan cuando los contenedores se detienen o eliminan.
- Redes
- Una red en Docker es un mecanismo de comunicación para que los contenedores puedan comunicarse entre sí y con el mundo exterior. Esto incluye redes bridge, host y overlay entre otras.
Un servicio es un constructo que representa a un contenedor y al conjunto de aplicaciones que se ejecutan en él. En general dicha relación es 1:1 (un contenedor <=> un servicio) pero no tiene por qué.
Docker se utiliza en una variedad de escenarios entre los que destacan:
- Desarrollo local
- Docker permite crear entornos de desarrollo consistentes que imitan el entorno de producción, con sus dependencias y configuraciones. Evitando así el efecto "en mi máquina funciona" al garantizar que todos los miembros del equipo utilizan el mismo entorno.
- Pruebas y CI/CD
- Docker facilita la creación de entornos de prueba aislados para ejecutar pruebas unitarias, de integración y de aceptación. También se integra bien con herramientas de CI/CD para automatizar la construcción, prueba y despliegue de aplicaciones.
- Despliegue en producción
- Docker permite empaquetar aplicaciones de manera optimizada para su despliegue.
- Microservicios
- Docker es ideal para arquitecturas de microservicios, ya que cada servicio puede ejecutarse en su propio contenedor aislado, facilitando la gestión, escalabilidad y actualización de cada servicio de manera independiente.
Veamos cómo podemos instalar Docker en nuestro host:
A continuación tenemos los pasos para instalar Docker en hosts con sistema operativo MS Windows:
- Descargar Docker Desktop para MS Windows desde el sitio oficial de Docker.
- Ejecutar el instalador y seguir las instrucciones.
- Reiniciar el sistema si fuera necesario.
Si por el contrario nuestro host tiene sistema operativo macOS procedemos de la siguiente manera:
- Descargar Docker Desktop para Mac desde el sitio oficial de Docker.
- Montar la imagen .dmg y arrastrar Docker a la carpeta de Aplicaciones.
- Ejecutar Docker desde la carpeta de Aplicaciones.
En cambio, si nuestro host tiene un sistema operativo basado en Linux instalamos Docker de la siguiente manera:
sudo snap refresh && sudo snap install docker
Para comprobar qué versión de Docker se ha instalado ejecutamos el siguiente comando:
Docker version 27.3.1, build ce12230
Por defecto Docker se instala necesitando privilegios de administrador (usuario root)
Para comprobar que Docker funciona correctamente, ejecutaremos el siguiente contenedor de pruebas:
sudo docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
c1ec31eb5944: Pull complete
Digest: sha256:d211f485f2dd1dee407a80973c8f129f00d54604d2c90732e8e320e5038a0348
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
Si al ejecutar el comando ves un resultado similar implica que Docker está instalado y funciona correctamente.
Para hacer que Docker se ejecute con nuestro usuario sin necesidad de usar el comando sudo, agregaremos nuestro usuario dentro del grupo de ejecución de docker:
sudo addgroup --system docker
sudo adduser $USER docker
newgrp docker
sudo snap disable docker
sudo snap enable docker
Para comprobar que todo ha ido bien, volvemos a pedirle a Docker que nos indique la versión instalada pero esta vez sin sudo:
Docker version 27.3.1, build ce12230
Si al ejecutar el comando ves un resultado similar implica que tu usuario puede ejecutar Docker correctamente.
Las imágenes son archivos inmutables que actúan como plantillas y que contienen todos los elementos necesarios para ejecutar una aplicación: código, entorno de ejecución, bibliotecas y dependencias, configuraciones y archivos de sistema.
Un container registry es un servicio o repositorio donde se almacenan y gestionan imágenes de contenedores como las de Docker. Los container registries permiten almacenar, eliminar, actualizar y/o versionar imágenes y distribuirlas de manera eficiente, facilitando el despliegue y la gestión de aplicaciones en diferentes entornos.
Algunos container registries más conocidos son Docker Hub, Google Container Registry y Amazon Elastic Container Registry entre otros.
Veamos cómo descargar imágenes desde un container registry:
Para descargar una imágen desde Docker Hub ejecutamos:
Using default tag: latest
latest: Pulling from library/debian
e756f3fdd6a3: Pull complete
Digest: sha256:3f1d6c17773a45c97bd8f158d665c9709d7b29ed7917ac934086ad96f92e4510
Status: Downloaded newer image for debian:latest
docker.io/library/debian:latest
Si al ejecutar el comando ves un resultado similar, implica que la imágen se ha descargado correctamente desde Docker Hub.
Por defecto Docker usa Docker Hub como container registry. Esto es que, si la imágen no se encuentra disponible en el host, se conectará automáticamente a Docker Hub para localizarla y descargarla.
Veamos cómo descargar una imágen desde AWS ECR:
docker pull public.ecr.aws/ubuntu/ubuntu:24.04_stable
24.04_stable: Pulling from ubuntu/ubuntu
afad30e59d72: Pull complete
Digest: sha256:fb95efe0d22be277f10250f15e5172ec0fe22c37eca2ba55e78b526c447eec23
Status: Downloaded newer image for public.ecr.aws/ubuntu/ubuntu:24.04_stable
public.ecr.aws/ubuntu/ubuntu:24.04_stable
Si al ejecutar el comando ves un resultado similar, implica que la imágen se ha descargado correctamente desde AWS ECR.
Para ver las imágenes tenemos disponibles en nuestro host ejecutamos:
REPOSITORY TAG IMAGE ID CREATED SIZE
public.ecr.aws/ubuntu/ubuntu 24.04_stable fec8bfd95b54 4 days ago 78.1MB
Podemos obtener información de la imágen mediante el siguiente comando:
docker inspect public.ecr.aws/ubuntu/ubuntu:24.04_stable
[
{
"Id": "sha256:fec8bfd95b54439b934c5033dc62d79b946291c327814f2d4df181e1d7536806",
"RepoTags": [
"public.ecr.aws/ubuntu/ubuntu:24.04_stable"
],
"RepoDigests": [
"public.ecr.aws/ubuntu/ubuntu@sha256:fb95efe0d22be277f10250f15e5172ec0fe22c37eca2ba55e78b526c447eec23"
],
"Parent": "",
"Comment": "",
"Created": "2024-10-16T09:25:57.559746466Z",
"DockerVersion": "24.0.7",
"Author": "",
"Config": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"/bin/bash"
],
"Image": "sha256:3cfd7549922ae8741a1825df62a5d01a4d4439c4c1e5c590074b9da9386b4143",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": {
"org.opencontainers.image.ref.name": "ubuntu",
"org.opencontainers.image.version": "24.04"
}
},
"Architecture": "amd64",
"Os": "linux",
"Size": 78118185,
"GraphDriver": {
"Data": {
"MergedDir": "/var/lib/docker/overlay2/95fd8ee1bc7e78f093bc8c1896ba31885b67b8e0ef245ce3d34de2ae0e20c197/merged",
"UpperDir": "/var/lib/docker/overlay2/95fd8ee1bc7e78f093bc8c1896ba31885b67b8e0ef245ce3d34de2ae0e20c197/diff",
"WorkDir": "/var/lib/docker/overlay2/95fd8ee1bc7e78f093bc8c1896ba31885b67b8e0ef245ce3d34de2ae0e20c197/work"
},
"Name": "overlay2"
},
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:27123a71e85e0540333291adccf7b9c340d089e8c3717c380d3b75cc8c7df90f"
]
},
"Metadata": {
"LastTagTime": "0001-01-01T00:00:00Z"
}
}
]
O bien, podemos inspeccionar propiedades directamente de la siguiente manera:
docker inspect public.ecr.aws/ubuntu/ubuntu:24.04_stable --format "{{.Id}}"
sha256:fec8bfd95b54439b934c5033dc62d79b946291c327814f2d4df181e1d7536806
O bien, usando el comando jq:
docker inspect public.ecr.aws/ubuntu/ubuntu:24.04_stable | jq ".[] | .Config.Labels"
{
"org.opencontainers.image.ref.name": "ubuntu",
"org.opencontainers.image.version": "24.04"
}
Si deseamos eliminar una imágen procedemos de la siguiente manera:
docker image rm public.ecr.aws/ubuntu/ubuntu:24.04_stable
Untagged: public.ecr.aws/ubuntu/ubuntu:24.04_stable
Untagged: public.ecr.aws/ubuntu/ubuntu@sha256:fb95efe0d22be277f10250f15e5172ec0fe22c37eca2ba55e78b526c447eec23
Deleted: sha256:fec8bfd95b54439b934c5033dc62d79b946291c327814f2d4df181e1d7536806
Deleted: sha256:27123a71e85e0540333291adccf7b9c340d089e8c3717c380d3b75cc8c7df90f
Podemos eliminar imágenes en desuso mediante el siguiente comando:
WARNING! This will remove all dangling images.
Are you sure you want to continue? [y/N] y
Deleted Images:
untagged: 1234567890.dkr.ecr.eu-west-1.amazonaws.com/xxx-php-fpm@sha256:ec3763c30e75e50409550b0b0c7a548498497b929c612bae7a3637ed97e704ea
deleted: sha256:a06a226f4cb17c2fef01bea1c2ea4262d49608e430189fc39a8f635288fd5104
untagged: 1234567890.dkr.ecr.eu-west-1.amazonaws.com/yyy@sha256:9838c36ffbd28daaa01740a1859c89c1efc90b848b0d9df6c0dae9c5cbd2cf2a
deleted: sha256:ce903f9dbb7ecac770b8556475480ff8f03da7a5ecd04dc2bba9ef634ea414ee
deleted: sha256:0beb4ed5bb8d1a41ce83a521821d1ed90538e5adca529e7742edab2801f98e02
deleted: sha256:6b17849287bac7148da163bbc7c22b818a292d04b8055b06e5e7faaa921f560c
deleted: sha256:85073dc3ced7b318506d7ab74c56b9ef503aa0b20dae739947d29f9951f1c43e
deleted: sha256:18c35c6df76e7eeea760b7f3e005fef97b4f72035f9a41c7edfc4ed2f3e6c38d
deleted: sha256:f800fb391dd932425032b8fdb034b65cfcacf60274c8f49fb5ac9a7f7da0eb20
deleted: sha256:771c5ba178c4c4703a88bffd3c169807d7c71ba3337fa5275782cd0ae39a5ff6
deleted: sha256:c31141764f681b21abda0d244cb638bf4244af294908078b87a011cceba2f1c3
deleted: sha256:db64b6885e45b93db1dd43836daf03246a107b9e875c2197fc610cc524516f6e
deleted: sha256:9bcea7c6121b7530a2bf5c54c336204fe3fb0b4b42b155f5062c761c572be81f
deleted: sha256:5a9e7633a4ed02545d7ddca4f6d94cc199beaa390d348806d56707b6db148ae3
deleted: sha256:899333cc3d240c34f0b2be295691b283a501fe01e8bddf04a3cb4567b0053751
deleted: sha256:84fffedbce7f446f736afb3674d9be4b2bfa5ab0a3e48a3367b201af36bf5267
deleted: sha256:931b7ff0cb6f494b27d31a4cbec3efe62ac54676add9c7469560302f1541ecaf
Total reclaimed space: 2.505GB
Este comando elimina imágenes en desuso de nuestro host, liberando espacio en disco y permitiendo a Docker funcionar de manera más eficiente.
Los contenedores son instancias en ejecución de una imagen de Docker. Los contenedores proporcionan una forma encapsulada y aislada de ejecutar aplicaciones.
Existen varios modos de ejecutar un contenedor:
Para ejecutar un comando en un contenedor efímero, es decir, que se destruye cuando termina de ejecutar el comando, procedemos de la siguiente manera:
docker run --interactive --tty php:8.3.12-cli-alpine php -v
PHP 8.3.12 (cli) (built: Sep 26 2024 22:43:42) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.3.12, Copyright (c) Zend Technologies
Aunque la manera más corta de hacerlo es:
docker run -it php:8.3.12-cli-alpine php -v
PHP 8.3.12 (cli) (built: Sep 26 2024 22:43:42) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.3.12, Copyright (c) Zend Technologies
Este comando muestra la versión de PHP del contenedor.
En vez de usar las opciones extendidas de Docker (--interactive, --tty) usamos la forma compacta -it
Podemos ejecutar un contenedor en segundo plano de la siguiente manera:
docker run -it -d caddy:2.8.4-alpine --name caddy
1c5d59fe9b0f17d58839ad388df6feaa91db129850e0c51e1755a8e36de5944e
La opción de Docker --detach, o en su forma compacta -d, permite a Docker ejecutar un contenedor en segundo plano.
Si no asignamos un nombre al contenedor, Docker nos asignará uno de manera aleatoria. Para tener organizada nuestra infraestructura se recomienda asignar un nombre autoexplicativo a cada contenedor con la opción --name
Cuando tenemos contenedores ejecutándose en segundo plano y queremos pararlos, ejecutamos el siguiente comando:
caddy
Parar un contenedor consiste en que el demonio de Docker le envíe una señal de gracefull-stop al contenedor, es decir, le solicita al contenedor que termine el comando en curso y acto seguido se para el contenedor.
Cuando tenemos parar inmediatamente la ejecución de un contenedor, ejecutamos el siguiente comando:
caddy
Cuando paramos inmediatamente un contendor el demonio de Docker mata el proceso de administración del contenedor, sin esperar a que el comando interno finalice.
Veamos cómo ver qué contenedores tenemos disponibles en nuestro host:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1c5d59fe9b0f caddy:2.8.4-alpine "caddy run --config …" 2 seconds ago Up 2 seconds 80/tcp, 443/tcp, 2019/tcp, 443/udp caddy
Aunque la forma más rápida es:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1c5d59fe9b0f caddy:2.8.4-alpine "caddy run --config …" 2 seconds ago Up 2 seconds 80/tcp, 443/tcp, 2019/tcp, 443/udp caddy
Podemos obtener información de un contenedor mediante el siguiente comando:
docker inspect caddy --format "{{.Id}} {{.Name}}"
1c5d59fe9b0f17d58839ad388df6feaa91db129850e0c51e1755a8e36de5944e /caddy
Para eliminar un contenedor ejecutamos el siguiente comando:
caddy
Para eliminar los contenedores en desuso ejecutamos el siguiente comando:
WARNING! This will remove all stopped containers.
Are you sure you want to continue? [y/N] y
Total reclaimed space: 0B
Se define la orquestación de servicios como el comportamiento de uno o varios servicios dentro de una infraestructura determinada. Dicho comportamiento se basa en Docker Compose, una herramienta que permite definir y ejecutar aplicaciones Docker desde sus contenedores y gestionar los servicios, redes y volúmenes asociados de manera declarativa utilizando archivos YAML.
Ejemplo de orquestación de servicios
Lo primero que debemos definir es un fichero docker-compose.yml:
Y definimos como su contenido...
services:
caddy:
image: caddy:2.8.4-alpine
container_name: caddy
restart: unless-stopped
ports:
- 80:80
- 443:443
Y para arrancar el contenedor ejecutamos el siguiente comando:
[+] Running 6/6
✔ caddy Pulled 3.7s
✔ 43c4264eed91 Already exists 0.0s
✔ 02040ba779ee Already exists 0.0s
✔ c257707c9719 Already exists 0.0s
✔ 06ce39c94b8d Already exists 0.0s
✔ 4f4fb700ef54 Already exists 0.0s
[+] Running 1/0
✔ Container caddy Created 0.0s
Attaching to caddy
caddy | {"level":"info","ts":1729521363.64274,"msg":"using config from file","file":"/etc/caddy/Caddyfile"}
caddy | {"level":"info","ts":1729521363.6431518,"msg":"adapted config to JSON","adapter":"caddyfile"}
caddy | {"level":"info","ts":1729521363.6435885,"logger":"admin","msg":"admin endpoint started","address":"localhost:2019","enforce_origin":false,"origins":["//localhost:2019","//[::1]:2019","//127.0.0.1:2019"]}
caddy | {"level":"warn","ts":1729521363.6436675,"logger":"http.auto_https","msg":"server is listening only on the HTTP port, so no automatic HTTPS will be applied to this server","server_name":"srv0","http_port":80}
caddy | {"level":"info","ts":1729521363.6437285,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0xc000157000"}
caddy | {"level":"info","ts":1729521363.643841,"logger":"http.log","msg":"server running","name":"srv0","protocols":["h1","h2","h3"]}
caddy | {"level":"info","ts":1729521363.6439984,"msg":"autosaved config (load with --resume flag)","file":"/config/caddy/autosave.json"}
caddy | {"level":"info","ts":1729521363.6440127,"msg":"serving initial configuration"}
caddy | {"level":"info","ts":1729521363.6490457,"logger":"tls","msg":"storage cleaning happened too recently; skipping for now","storage":"FileStorage:/data/caddy","instance":"840cdb6c-b78a-45cb-b673-2ddd332fd900","try_again":1729607763.649045,"try_again_in":86399.999999748}
caddy | {"level":"info","ts":1729521363.6491115,"logger":"tls","msg":"finished cleaning storage units"}
Docker Compose simplifica la gestión de nuestra infraestructura porque en un único fichero YAML podemos tener múltiples servicios y, como hemos visto, no es necesario definirle el nombre del fichero de nuestra orquestación porque por defecto Docker Compose busca un fichero llamado docker-compose.yml en la ruta actual.
Comprobemos que está en funcionamiento:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3645e9f8b288 caddy:2.8.4-alpine "caddy run --config …" About a minute ago Up About a minute 0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:443->443/tcp, :::443->443/tcp, 443/udp, 2019/tcp caddy
Veamos cómo interactuar con nuestro primer servicio:
xdg-open http://127.0.0.1
Este comando abre una URL en el navegador por defecto del host. Si todo ha ido bien, deberíamos ver la pantalla por defecto de Caddy.
Página por defecto de Caddy
Cuando dejemos de necesitarlo, podemos pararlo con el siguiente comando:
[+] Running 2/2
✔ Container caddy Removed 0.2s
✔ Network tmp_default Removed
Para que el contenedor pueda ser accesible por el host, Docker crea una red por defecto y le asigna un nombre compuesto por el nombre del directorio y el tipo de red.
Un punto de montaje es un acceso al sistema de ficheros del contenedor y el resto de la infraestructura (otros contenedores, el host, etc.)
Los bind mounts en Docker son una forma de compartir directorios y/o archivos entre el sistema de archivos del host y los contenedores. Esto permite que un contenedor pueda acceder directamente a los datos del host, lo que puede ser útil en diferentes escenarios, como desarrollo, depuración o compartir configuraciones entre el host y el contenedor.
A diferencia de los volúmenes de Docker, que son gestionados por Docker, en los bind mounts:
- Puedes montar directorios o archivos que ya existen en el host.
- El archivo o directorio específico del host que montas en el contenedor sigue existiendo fuera del control de Docker. Docker simplemente hace un enlace directo (bind) entre el directorio o archivo del host y el contenedor.
- El contenedor tiene acceso a esos archivos en tiempo real, y cualquier cambio que haga en esos archivos será reflejado en el host (y viceversa, si es de lectura/escritura).
Para montar un punto de montaje tipo bind mount con el host ejecutamos un comando similar a:
docker run -v ./src/index.html:/var/www/html/index.html:ro caddy
O mediante orquestación con Docker Compose:
services:
caddy:
image: caddy:2.8.4-alpine
container_name: caddy
restart: unless-stopped
ports:
- 80:80
- 443:443
volumes:
- ./src/index.html:/var/www/html/index.html:ro
Un volumen en Docker es un mecanismo para almacenar y gestionar datos persistentes de los contenedores. A diferencia de los bind mounts, los volúmenes son gestionados por Docker, lo que significa que Docker se encarga de crear, almacenar y organizar estos datos en una ubicación fuera del sistema de archivos del contenedor y del host.
Los volúmenes se caracterizan por:
- Persistencia de datos: Los datos almacenados en volúmenes persisten incluso después de que el contenedor se elimina.
- Independencia del sistema de archivos del host: Docker decide dónde almacenar el volumen, lo que lo hace más portátil y menos dependiente del entorno del host.
- Facilidad de uso y gestión: Docker proporciona comandos para crear, listar y eliminar volúmenes, lo que facilita su manejo.
- Compartir datos entre contenedores: Los volúmenes pueden ser montados en varios contenedores, permitiendo compartir datos entre ellos.
Para montar un punto de montaje tipo volumen ejecutamos un comando similar a:
docker volume create foo --sharing readonly
Para mostrar los puntos de montaje disponibles ejecutamos el comando:
DRIVER VOLUME NAME
local 2a31133caf149aa1eb63cc9fa50ed0321b1f1fd00cf5a69046fc5ad9a5f26934
local foo
Podemos obtener información del punto de montaje mediante el siguiente comando:
[
{
"CreatedAt": "2024-10-01T15:09:22+02:00",
"Driver": "local",
"Labels": {
"com.docker.compose.project": "new",
"com.docker.compose.version": "2.29.7",
"com.docker.compose.volume": "foo"
},
"Mountpoint": "/var/lib/docker/volumes/caddy/_data",
"Name": "foo",
"Options": null,
"Scope": "local"
}
]
O bien, podemos inspeccionar propiedades directamente de la siguiente manera:
docker inspect foo --format "{{.Name}}"
foo
O bien, usando el comando jq:
docker inspect foo | jq ".[] | .Labels"
{
"com.docker.compose.project": "new",
"com.docker.compose.version": "2.29.7",
"com.docker.compose.volume": "foo"
}
Para eliminar un punto de montaje ejecutamos un comando similar a:
foo
Podemos eliminar puntos de montaje en desuso mediante el siguiente comando:
WARNING! This will remove anonymous local volumes not used by at least one container.
Are you sure you want to continue? [y/N] y
Deleted Volumes:
5d305328d48f4649634d48dfe90e7b03b47fa7e804aed3ba08f07cf53daf50c5
f7b819426cf0b5327ed1a5f007e090d6de38e3bd5be935fc98f9e8645406a9f7
749a69af366363e8ba04e5ebc827a8724f6e5a362c63f91ac66a4e39ecb21a88
foo
Total reclaimed space: 481.2kB
Ejemplo de punto de montaje
Creamos un fichero index.php:
Con el siguiente su contenido...
Ahora definimos nuestro fichero docker-compose.yml:
Con el siguiente su contenido...
services:
php:
container_name: php
image: php:8.3.12-fpm-alpine
restart: unless-stopped
command: php -S 0.0.0.0:80
volumes:
- ./:/var/www/html
ports:
- 80:80
Y arrancamos nuestro contenedor:
[+] Running 11/11
✔ php Pulled 7.8s
✔ 43c4264eed91 Already exists 0.0s
✔ bb15916673af Already exists 0.0s
✔ 9feb3258c4c6 Already exists 0.0s
✔ 32e436918c34 Already exists 0.0s
✔ e567f6a279d6 Already exists 0.0s
✔ 57c15fd3b41b Already exists 0.0s
✔ fb0b325a6065 Already exists 0.0s
✔ 6b0b83336881 Already exists 0.0s
✔ 7fe54b97407c Already exists 0.0s
✔ b118019426b3 Already exists 0.0s
[+] Running 1/0
✔ Container php Created 0.0s
Attaching to php
php | [Tue Oct 22 09:00:39 2024] PHP 8.3.12 Development Server (http://0.0.0.0:80) started
Podemos ver que el servicio php está activo y que el servidor built-in de PHP está a la escucha de nuevas peticiones.
Ahora podemos abrir nuestro navegador y visitar la ruta http://localhost/index.php
xdg-open http://localhost/index.php
Acabamos de montar un contenedor PHP con un punto de montaje tipo bind mount, donde todo el contenido de la carpeta actual es compartido con el contenedor en el path /var/www/html.
Recuerda que este tipo de montaje sincroniza automáticamente el contenido de dichos archivos o directorios en tiempo real por lo que puedes modificar el contenido del fichero index.php con tu IDE favorito y refrescar la pestaña del navegador para ver los cambios aplicados.
Para parar el contenedor actual basta con pulsar Ctrl+C y finalizar así su ejecución.
Estos son los tipos de drivers para redes soportados en Docker:
- Bridge
- Es el tipo de red predeterminado que usa Docker. Los contenedores conectados a una red bridge pueden comunicarse entre sí a través de sus direcciones IP, pero no pueden ser accedidos desde fuera de Docker, a menos que se expongan puertos específicos.
- Host
- El contenedor comparte el sistema de red con el host, es decir, usa la red del sistema anfitrión directamente. Esto elimina la sobrecarga de virtualización de la red, pero puede generar conflictos si el contenedor usa puertos que ya están ocupados en el host.
- Overlay
- Se utiliza principalmente en entornos Docker Swarm o Kubernetes. Permite la creación de redes entre múltiples hosts Docker, para que los contenedores en diferentes máquinas puedan comunicarse como si estuvieran en la misma red.
- Macvlan
- Asigna una dirección MAC única a cada contenedor, haciendo que se comporten como dispositivos físicos en la red. Es útil cuando se quiere que los contenedores se integren directamente con la red local y tengan su propia IP asignada por el router, como si fueran máquinas físicas.
- None
- El contenedor no tiene acceso a ninguna red. Es útil cuando quieres ejecutar contenedores completamente aislados, sin conectividad externa.
Para mostrar las redes disponibles ejecutamos el comando:
NETWORK ID NAME DRIVER SCOPE
7dc87e11750f bridge bridge local
ac7555030bb0 host host local
e28b875bf8c0 none null local
Para crear una red ejecutamos un comando similar a:
docker network create -d bridge --subnet 172.24.0.0/16 --gateway 172.24.0.1 foo
2669335abb1594e17ae04b96c620aa8b23bcaa91f07311e8a23c33e71b8e7397
Para obtener información de una red ejecutamos el siguiente comando:
[
{
"Name": "foo",
"Id": "2669335abb1594e17ae04b96c620aa8b23bcaa91f07311e8a23c33e71b8e7397",
"Created": "2024-10-22T13:10:37.978135494+02:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.24.0.0/16",
"Gateway": "172.24.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]
O bien, podemos inspeccionar propiedades directamente de la siguiente manera:
docker inspect foo --format "{{.Id}} {{.Name}}"
2669335abb1594e17ae04b96c620aa8b23bcaa91f07311e8a23c33e71b8e7397 foo
O bien, usando el comando jq:
docker inspect foo | jq ".[] | .IPAM.Config"
[
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
]
Para eliminar una red ejecutamos un comando similar a:
foo
Podemos eliminar redes en desuso mediante el siguiente comando:
WARNING! This will remove all custom networks not used by at least one container.
Are you sure you want to continue? [y/N] y
Ejemplo de uso de una red
Podemos crear y usar redes personalizadas mediante los siguientes comandos:
docker network create foo
docker run -d --name php --network foo -p 8080:80 -v ./:/var/www/html php:8.3.12-fpm-alpine php -S 0.0.0.0:80
e3aea50253031d36d24d2e1d7befb3d25a43846c53fdc1e81a8f1f41ba1bb86f
O bien, usando la orquestación mediante Docker Compose:
services:
php:
container_name: php
image: php:8.3.12-fpm-alpine
restart: unless-stopped
command: php -S 0.0.0.0:80
volumes:
- ./:/var/www/html
ports:
- 8080:80
hostname: server_php
networks:
- foo
networks:
foo:
driver: bridge
ipam:
config:
- subnet: 172.24.0.0/16
gateway: 172.24.0.1
Y levantamos la infraestructura con el siguiente comando:
[+] Running 1/1
✔ Container php Recreated 0.1s
Attaching to php
php | [Tue Oct 22 11:55:07 2024] PHP 8.3.12 Development Server (http://0.0.0.0:80) started
Ahora podemos abrir nuestro navegador y visitar la ruta http://localhost:8080/index.php
xdg-open http://localhost:8080/index.php
Cuando finalicemos de trabajar con el contenedor podemos pararlo con el siguiente comando:
Hemos visto cómo podemos crear redes explícitamente o bien, mediante orquestación usando Docker Compose.