Docker

Docker Docker

Dockerlink image 0

Contenedoreslink image 1

Hello worldlink image 2

Ejecutar el primer contenedor tipo Hello world con el comando docker run hello-world

!docker run hello-world
      
Unable to find image 'hello-world:latest' locally
      
latest: Pulling from library/hello-world
      
      85e32844: Pull complete 457kB/2.457kBBDigest: sha256:dcba6daec718f547568c562956fa47e1b03673dd010fe6ee58ca806767031d1c
      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/
      
      

Como no tenemos el contenedor guardado en local, docker lo descarga de docker hub. Si ahora volvemos a ejecutar el contenedor, ya no aparecerá el primer mensaje, en el que indica que se está descargando

	
!docker run hello-world
!docker run hello-world
Copy
	
Unable to find image 'hello-world:latest' locally
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/

Para ver los contenedores que están corriendo ejecutar docker ps

	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

Como vemos no hay ningún contenedor abierto. Pero sin embargo, si ejecutamos docker ps -a (all) vemos que si aparecen

	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1efb51bbbf38 hello-world "/hello" 10 seconds ago Exited (0) 9 seconds ago strange_thompson
5f5705e7603e hello-world "/hello" 15 seconds ago Exited (0) 14 seconds ago laughing_jang

Vemos que aparecen dos contenedores llamados hello-world que son los dos que hemos ejecutado antes. Por tanto cada vez que ejecutamos el comando run, docker crea un nuevo contenedor, no ejecuta uno que ya exista

Si queremos tener más información de uno de los dos contenedores podemos ejecutar docker inspect <id>, donde <id> corresponde a la ID del docker que se ha mostrado en la lista anterior

	
!docker inspect 1efb51bbbf38
Copy
	
[
{
"Id": "1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e",
"Created": "2023-09-04T03:59:17.795499354Z",
"Path": "/hello",
"Args": [],
"State": {
"Status": "exited",
"Running": false,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 0,
"ExitCode": 0,
"Error": "",
"StartedAt": "2023-09-04T03:59:18.406663026Z",
"FinishedAt": "2023-09-04T03:59:18.406181184Z"
},
"Image": "sha256:9c7a54a9a43cca047013b82af109fe963fde787f63f9e016fdc3384500c2823d",
"ResolvConfPath": "/var/lib/docker/containers/1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e/hostname",
"HostsPath": "/var/lib/docker/containers/1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e/hosts",
"LogPath": "/var/lib/docker/containers/1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e/1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e-json.log",
"Name": "/strange_thompson",
...
}
}
}
]

Como acordarnos de IDs es complicado para nosotros, docker asigna nombres a los contenedores para facilitarnos la vida. Así en la lista anterior, la última columna corresponde al nombre que ha asignado docker a cada contenedor, de modo que si ahora ejecutamos docker inspect <name> obtendremos la misma información que con la ID

Vuelvo a ejecutar docker ps -a para volver a ver la lista

	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1efb51bbbf38 hello-world "/hello" 2 minutes ago Exited (0) 2 minutes ago strange_thompson
5f5705e7603e hello-world "/hello" 2 minutes ago Exited (0) 2 minutes ago laughing_jang

Y ahora ejecuto docker inspect <name> para ver la información del contenedor

	
!docker inspect strange_thompson
Copy
	
[
{
"Id": "1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e",
"Created": "2023-09-04T03:59:17.795499354Z",
"Path": "/hello",
"Args": [],
"State": {
"Status": "exited",
"Running": false,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 0,
"ExitCode": 0,
"Error": "",
"StartedAt": "2023-09-04T03:59:18.406663026Z",
"FinishedAt": "2023-09-04T03:59:18.406181184Z"
},
"Image": "sha256:9c7a54a9a43cca047013b82af109fe963fde787f63f9e016fdc3384500c2823d",
"ResolvConfPath": "/var/lib/docker/containers/1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e/hostname",
"HostsPath": "/var/lib/docker/containers/1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e/hosts",
"LogPath": "/var/lib/docker/containers/1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e/1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e-json.log",
"Name": "/strange_thompson",
...
}
}
}
]

Pero por qué con docker ps no vemos ningún contenedor y con docker ps -a sí. Esto es porque docker ps solo muestra los contenedores que están corriendo, mientras que docker ps -a muestra todos los contenedores, los que están corriendo y los que están apagados

Podemos crear un contenedor asignándole un nombre nosotros mediante el comando docker run --name <name> hello-world

	
!docker run --name hello_world hello-world
Copy
	
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/

Esto será más cómodo para nosotros, ya que podremos controlar nosotros los nombres de los contenedores

Si ahora queremos crear otro contenedor con el mismo nombre no podremos, porque docker no permite que se dupliquen los nombres de los contenedores. De modo que si queremos renombrar el contenedor podemos usar el comando docker rename <old name> <new name>

	
!docker rename hello_world hello_world2
Copy

Tenemos ahora un montón de contenedores iguales. Así que si queremos borrar alguno tenemos que usar el comando docker rm <id> ó docker rm <name>

	
!docker rename hello_world hello_world2
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f432c9c2ca21 hello-world "/hello" 9 seconds ago Exited (0) 8 seconds ago hello_world2
1efb51bbbf38 hello-world "/hello" 4 minutes ago Exited (0) 4 minutes ago strange_thompson
5f5705e7603e hello-world "/hello" 4 minutes ago Exited (0) 4 minutes ago laughing_jang
	
!docker rm hello_world2
Copy
	
hello_world2

Si volvemos a ver la lista de contenedores, el contenedor hello_world2 ya no estará

	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1efb51bbbf38 hello-world "/hello" 5 minutes ago Exited (0) 5 minutes ago strange_thompson
5f5705e7603e hello-world "/hello" 5 minutes ago Exited (0) 5 minutes ago laughing_jang

Si queremos borrar todos los contenedores, podemos hacerlo uno a uno, pero como es muy pesado, podemos borrar todos mediante el comando docker container prune. Este comando elimina solo los contenedores que estén parados

	
!docker container prune
Copy
	
WARNING! This will remove all stopped containers.
Are you sure you want to continue? [y/N] y

Docker pregunta si estás seguro, y si le dices que sí, borra todos. Si ahora listo todos los contenedores no aparece ninguno

	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

El modo iteractivolink image 3

Vamos a ejecutar un ubuntu mediante el comando docker run ubuntu

	
!docker run ubuntu
Copy
	
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
Digest: sha256:20fa2d7bb4de7723f542be5923b06c4d704370f0390e4ae9e1c833c8785644c1[1A
Status: Downloaded newer image for ubuntu:latest

Como vemos ahora ha tardado más en descargar. Si listamos los contenedores mediante el comando docker ps vemos que no aparece el contenedor que acabamos de crear, es decir, no está corriendo

	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

Listamos ahora todos los contenedores

	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
da16b3a85178 ubuntu "bash" 4 seconds ago Exited (0) 3 seconds ago hardcore_kare

Vemos que el estado del contenedor es Exited (0)

Si nos fijamos en comando del contenedor aparece bash y junto al estado Exited (0) nos indica que ha arrancado Ubuntu, ha ejecutado su bash, ha terminado la ejecución y ha devuelto un 0. Esto pasa porque al bash de Ubuntu no se le ha dicho nada que hacer. Para solucionar esto, ahora vamos a ejecutar el contenedor mediante el comando docker run -it ubuntu, con it lo que le estamos indicando es que lo queremos ejecutar en modo iterativo

	
!docker run -it ubuntu
Copy
	
root@5b633e9d838f:/#

Ahora vemos que estamos dentro del bash de ubuntu. Si ejecutamos el comando cat /etc/lsb-release podemos ver la distribución de Ubuntu

	
!root@5b633e9d838f:/# cat /etc/lsb-release
Copy
	
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=22.04
DISTRIB_CODENAME=jammy
DISTRIB_DESCRIPTION="Ubuntu 22.04.1 LTS"

Si abrimos otra terminal y vemos la lista de contenedores, ahora si aparecerá el contenedor corriendo Ubuntu

	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5b633e9d838f ubuntu "bash" 3 minutes ago Up 3 minutes funny_mirzakhani

Vemos el contenedor con Ubuntu y en su estado podemos ver UP

Si vemos ahora la lista de todos los contenedores, veremos que aparecen los dos contenedores con Ubuntu, el primero apagado y el segundo el que está corriendo

	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5b633e9d838f ubuntu "bash" 3 minutes ago Up 3 minutes funny_mirzakhani
da16b3a85178 ubuntu "bash" 3 minutes ago Exited (0) 3 minutes ago hardcore_kare

Si volvemos a la terminal donde teníamos Ubuntu corriendo dentro de un docker, si escribimos exit saldremos de Ubuntu.

	
!root@5b633e9d838f:/# exit
Copy
	
exit

Si ejecutamos docker ps el contenedor ya no aparece

	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

Pero si ejecuto docker ps -a sí que aparece. Esto quiere decir que el contenedor se apagó

	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5b633e9d838f ubuntu "bash" 4 minutes ago Exited (0) 27 seconds ago funny_mirzakhani
da16b3a85178 ubuntu "bash" 4 minutes ago Exited (0) 4 minutes ago hardcore_kare

Esto ocurre porque al escribir exit, en realidad lo estamos escribiendo en la consola del bash de Ubuntu, lo que significa que que estamos terminando el proceso bash de Ubuntu.

Ciclo de vida de un contenedorlink image 4

En docker, cuando el proceso principal de un contenedor se termina, se apaga el contenedor. Dentro de un contenedor pueden ejecutarse varios procesos, pero solo cuando se termina el proceso principal se apaga el contenedor

Por tanto, si queremos correr un contenedor que no se apague cuando finalice un proceso, debemos hacer que su proceso principal no se termine. En este caso, que no finalice bash

Si queremos ejecutar un contenedor con ubuntu, pero que no finalice cuando termine el proceso de bash lo podemos hacer de la siguiente manera

	
!docker run --name alwaysup -d ubuntu tail -f /dev/null
Copy
	
ce4d60427dcd4b326d15aa832b816c209761d6b4e067a016bb75bf9366c37054

Lo que hacemos es primero darle el nombre alwaysup, en segundo lugar pasarle la opción -d (detach) para que el contenedor se ejecute en segundo plano y por último le decimos el proceso principal que queremos que se ejecute en el contenedor, que en este caso es tail -f /dev/null que equivale a un comando nop

Esto nos devolverá la ID del contenedor, pero no estaremos dentro de ubuntu como pasaba antes

Si ahora vemos la lista de contenedores que se están ejecutando aparece el contenedor que acabamos de crear

	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ce4d60427dcd ubuntu "tail -f /dev/null" 18 seconds ago Up 17 seconds alwaysup

Como ya tenemos un contenedor corriendo siempre, podemos conectarnos al el mediante el comando exec. Le decimos el nombre o la ID del contenedor y le pasamos el proceso que queremos que se ejecuta. Además pasamos la opción -it para decirle que sea iteractivo

	
!docker exec -it alwaysup bash
Copy
	
root@ce4d60427dcd:/#

Ahora volvemos a estar dentro de ubuntu. Si ejecutamos el commando ps -aux podemos ver una lista de los procesos que se están ejecutando dentro de ubuntu.

	
!ps -aux
Copy
	
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 2820 1048 ? Ss 13:04 0:00 tail -f /dev/null
root 7 0.0 0.0 4628 3796 pts/0 Ss 13:04 0:00 bash
root 15 0.0 0.0 7060 1556 pts/0 R+ 13:05 0:00 ps -aux

Vemos solo tres procesos, el ps -aux, el bash y el tail -f /dev/null

Este contenedor va a estar siempre encendido mientras el proceso tail -f /dev/null siga corriendo

Si salimos del contenedor con el comando exit y ejecutamos el comando docker ps vemos que el contenedor sigue encendido

	
!exit
Copy
	
exit
	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ce4d60427dcd ubuntu "tail -f /dev/null" 2 minutes ago Up 2 minutes alwaysup

Para poder finalizar el proceso y poder apagar el contenedor debemos usar el comando docker stop <name>

	
!docker stop alwaysup
Copy
	
alwaysup

Si ahora volvemos a listar los contenedores encendidos ya no aparece el contenedor con Ubuntu

	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

Y si listamos todos los contenedores, aparece el contenedor con Ubuntu, y su estado Exited

	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ce4d60427dcd ubuntu "tail -f /dev/null" 14 minutes ago Exited (137) About a minute ago alwaysup
5b633e9d838f ubuntu "bash" 19 minutes ago Exited (0) 15 minutes ago funny_mirzakhani
da16b3a85178 ubuntu "bash" 20 minutes ago Exited (0) 20 minutes ago hardcore_kare

Tambien podemos pausar un contenedor mediante el comando docker pause <name>

	
!docker run --name alwaysup -d ubuntu tail -f /dev/null
Copy
	
8282eaf9dc3604fa94df206b2062287409cc92cbcd203f1a018742b5c171c9e4

Ahora lo pausamos

	
!docker pause alwaysup
Copy
	
alwaysup

Si volvemos a ver todos los contenedores, vemos que el contenedor con Ubuntu está pausado

	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8282eaf9dc36 ubuntu "tail -f /dev/null" 41 seconds ago Up 41 seconds (Paused) alwaysup
5b633e9d838f ubuntu "bash" 19 minutes ago Exited (0) 15 minutes ago funny_mirzakhani
da16b3a85178 ubuntu "bash" 20 minutes ago Exited (0) 20 minutes ago hardcore_kare

Contenedores de un solo usolink image 5

Si a la hora de ejecutar un contenedor, ponemos la opción --rm, ese contenedor se va a borrar cuando termine de ejecutarse.

	
!docker run --rm --name autoremove ubuntu:latest
Copy

Si ahora vemos qué contenedores tenemos

	
!docker run --rm --name autoremove ubuntu:latest
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

Vemos que no está el contenedor que acabamos de crear

Exponer contenedores al mundo exteriorlink image 6

Vamos a crear un nuevo contenedor con un servidor

	
!docker run -d --name proxy nginx
Copy
	
Unable to find image 'nginx:latest' locally
latest: Pulling from library/nginx
f1ad4ce1: Pulling fs layer
b079d0f8: Pulling fs layer
5fbbebc6: Pulling fs layer
ffdd25f4: Pulling fs layer
32c8fba2: Pulling fs layer
24b8ba39: Pull complete 393kB/1.393kBB[5ADigest: sha256:2888a97f7c7d498bbcc47ede1ad0f6ced07d72dfd181071dde051863f1f79d7b
Status: Downloaded newer image for nginx:latest
1a530e04f14be082811b72ea8b6ea5a95dad3037301ee8a1351a0108ff8d3b30

Esto crea un servidor, vamos a volver a listar los contenedores que están corriendo

	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1a530e04f14b nginx "/docker-entrypoint.…" 1 second ago Up Less than a second 80/tcp proxy

Ahora aparece una nueva columna con el puerto, y nos dice que el servidor que acabamos de crear está en el puerto 80 bajo el protocolo tcp.

Si abrimos un navegador e intentamos conectarnos al servidor mediante http://localhost:80 no conseguimos conectar. Esto es porque cada contenedor tiene su propia interfaz de red. Es decir, el servidor está escuchando en el puerto 80 del contenedor, pero nosotros estamos intentando conectar al puerto 80 del host

Paramos el contenedor para relanzarlo de otra forma

	
!docker stop proxy
Copy
	
proxy

Si listamos los contenedores no aparece corriendo

	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

Lo borramos para volver a crearlo

	
!docker rm proxy
Copy
	
proxy

Si listamos todos los contenedores ya no está

	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ce4d60427dcd ubuntu "tail -f /dev/null" 19 minutes ago Exited (137) 5 minutes ago alwaysup
5b633e9d838f ubuntu "bash" 24 minutes ago Exited (0) 20 minutes ago funny_mirzakhani
da16b3a85178 ubuntu "bash" 24 minutes ago Exited (0) 24 minutes ago hardcore_kare

Para volver a crear el contenedor con el servidor y poderlo ver desde el host, tenemos que usar la opción -p (publish), indicando en primer lugar el puerto en el que queremos verlo en el host y a continuación el puerto del contenedor, es decir, -p <ip host>:<ip conteiner>

	
!docker run -d --name proxy -p 8080:80 nginx
Copy
	
c199235e42f76a30266f6e1af972e0a59811806eb3d3a9afdd873f6fa1785eae

Listamos los contenedores

	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c199235e42f7 nginx "/docker-entrypoint.…" 22 seconds ago Up 21 seconds 0.0.0.0:8080->80/tcp, :::8080->80/tcp proxy

Vemos que el puerto del contenedor es 0.0.0.0:8080->80/tcp. Si ahora vamos a un navegador e introducimos 0.0.0.0:8080 podremos acceder al servidor del contenedor

Al listar los contenedores, en la columna PORTS indica 0.0.0.0:8080->80/tcp, lo que nos ayuda a ver la relación de puertos

Para ver los logs del contenedor, mediante el comando docker logs <name> puedo ver los logs del contenedor

	
!docker logs proxy
Copy
	
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2022/09/13 13:24:06 [notice] 1#1: using the "epoll" event method
2022/09/13 13:24:06 [notice] 1#1: nginx/1.23.1
2022/09/13 13:24:06 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)
2022/09/13 13:24:06 [notice] 1#1: OS: Linux 5.15.0-46-generic
2022/09/13 13:24:06 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2022/09/13 13:24:06 [notice] 1#1: start worker processes
2022/09/13 13:24:06 [notice] 1#1: start worker process 31
2022/09/13 13:24:06 [notice] 1#1: start worker process 32
2022/09/13 13:24:06 [notice] 1#1: start worker process 33
2022/09/13 13:24:06 [notice] 1#1: start worker process 34
2022/09/13 13:24:06 [notice] 1#1: start worker process 35
2022/09/13 13:24:06 [notice] 1#1: start worker process 36
2022/09/13 13:24:06 [notice] 1#1: start worker process 37
2022/09/13 13:24:06 [notice] 1#1: start worker process 38
2022/09/13 13:24:06 [notice] 1#1: start worker process 39
2022/09/13 13:24:06 [notice] 1#1: start worker process 40
2022/09/13 13:24:06 [notice] 1#1: start worker process 41
...
172.17.0.1 - - [13/Sep/2022:13:24:40 +0000] "GET /favicon.ico HTTP/1.1" 404 555 "http://0.0.0.0:8080/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" "-"
172.17.0.1 - - [13/Sep/2022:13:25:00 +0000] "üâV$Zqi'×ü[€ïºåÇè÷&3nSëÉì‘ÂØщž¾ Ç?áúaΐ˜uã/ØRfOHì’+“\»±¿Òm°9 úúÀ+À/À,À0̨̩ÀÀœ/5“šš localhostÿ" 400 157 "-" "-" "-"
172.17.0.1 - - [13/Sep/2022:13:25:00 +0000] "ü)šbCÙmñ†ëd"ÏÄE‡#~LÁ„µ‘k˜«lî[0 ÐÒ`…Æ‹…R‹‡êq{Pòû⨝IôtH™~Ê1-|Ž êêÀ+À/À,À0̨̩ÀÀœ/5“" 400 157 "-" "-" "-"
172.17.0.1 - - [13/Sep/2022:13:26:28 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" "-"

Ahora puedo ver todas las peticiones que se le han hecho al servidor. Pero si quiero ver los logs en tiempo real, mediante docker logs -f <name> lo puedo hacer

	
!docker logs -f proxy
Copy
	

Ahora puedo ver los logs en tiempo real. Para salir introducir CTRL+C

Como puede llegar un momento en el que haya muchos logs, si solo quieres los últimos logs, mediante la opción --tail <num> puedo ver los últimos <num> logs. Si añado la opción -f estaremos viendo siempre los últimos <num> logs

	
!docker logs --tail 10 proxy
Copy
	
2022/09/13 13:24:06 [notice] 1#1: start worker process 41
2022/09/13 13:24:06 [notice] 1#1: start worker process 42
172.17.0.1 - - [13/Sep/2022:13:24:16 +0000] "üEŽ¶ EgóɚœÊì§y#3’•ÜQïê$¿# ƒ÷-,s!rê|®ß¡LZª4y³t«ÀÎ_¸çÿ'φ êêÀ+À/À,À0̨̩ÀÀœ/5“ŠŠ localhostÿ" 400 157 "-" "-" "-"
172.17.0.1 - - [13/Sep/2022:13:24:16 +0000] "ü}©Dr{Œ;z‚­¼ŠzÂxßšæl?§àDoK‘'g»µ %»ýق?ۀ³TöcJ÷åÂÒ¼¢£ë½=R¼ƒ‰… ÊÊÀ+À/À,À0̨̩ÀÀœ/5“šš localhostÿ" 400 157 "-" "-" "-"
172.17.0.1 - - [13/Sep/2022:13:24:39 +0000] "GET / HTTP/1.1" 200 615 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" "-"
2022/09/13 13:24:40 [error] 34#34: *3 open() "/usr/share/nginx/html/favicon.ico" failed (2: No such file or directory), client: 172.17.0.1, server: localhost, request: "GET /favicon.ico HTTP/1.1", host: "0.0.0.0:8080", referrer: "http://0.0.0.0:8080/"
172.17.0.1 - - [13/Sep/2022:13:24:40 +0000] "GET /favicon.ico HTTP/1.1" 404 555 "http://0.0.0.0:8080/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" "-"
172.17.0.1 - - [13/Sep/2022:13:25:00 +0000] "üâV$Zqi'×ü[€ïºåÇè÷&3nSëÉì‘ÂØщž¾ Ç?áúaΐ˜uã/ØRfOHì’+“\»±¿Òm°9 úúÀ+À/À,À0̨̩ÀÀœ/5“šš localhostÿ" 400 157 "-" "-" "-"
172.17.0.1 - - [13/Sep/2022:13:25:00 +0000] "ü)šbCÙmñ†ëd"ÏÄE‡#~LÁ„µ‘k˜«lî[0 ÐÒ`…Æ‹…R‹‡êq{Pòû⨝IôtH™~Ê1-|Ž êêÀ+À/À,À0̨̩ÀÀœ/5“" 400 157 "-" "-" "-"
172.17.0.1 - - [13/Sep/2022:13:26:28 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" "-"

Si además añadimos la opción -t podemos ver la fecha y hora de cada log, de esta manera si hemos tenido un problema, podemos saber en qué momento ha ocurrido

	
!docker logs --tail -t 10 proxy
Copy
	
2022-09-13T13:24:06.573362728Z 2022/09/13 13:24:06 [notice] 1#1: start worker process 41
2022-09-13T13:24:06.651127107Z 2022/09/13 13:24:06 [notice] 1#1: start worker process 42
2022-09-13T13:24:16.651160189Z 172.17.0.1 - - [13/Sep/2022:13:24:16 +0000] "üEŽ¶ EgóɚœÊì§y#3’•ÜQïê$¿# ƒ÷-,s!rê|®ß¡LZª4y³t«ÀÎ_¸çÿ'φ êêÀ+À/À,À0̨̩ÀÀœ/5“ŠŠ localhostÿ" 400 157 "-" "-" "-"
2022-09-13T13:24:16.116817914Z 172.17.0.1 - - [13/Sep/2022:13:24:16 +0000] "ü}©Dr{Œ;z‚­¼ŠzÂxßšæl?§àDoK‘'g»µ %»ýق?ۀ³TöcJ÷åÂÒ¼¢£ë½=R¼ƒ‰… ÊÊÀ+À/À,À0̨̩ÀÀœ/5“šš localhostÿ" 400 157 "-" "-" "-"
2022-09-13T13:24:39.117398081Z 172.17.0.1 - - [13/Sep/2022:13:24:39 +0000] "GET / HTTP/1.1" 200 615 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" "-"
2022-09-13T13:24:39.117412408Z 2022/09/13 13:24:40 [error] 34#34: *3 open() "/usr/share/nginx/html/favicon.ico" failed (2: No such file or directory), client: 172.17.0.1, server: localhost, request: "GET /favicon.ico HTTP/1.1", host: "0.0.0.0:8080", referrer: "http://0.0.0.0:8080/"
2022-09-13T13:24:40.117419389Z 172.17.0.1 - - [13/Sep/2022:13:24:40 +0000] "GET /favicon.ico HTTP/1.1" 404 555 "http://0.0.0.0:8080/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" "-"
2022-09-13T13:25:00.117434249Z 172.17.0.1 - - [13/Sep/2022:13:25:00 +0000] "üâV$Zqi'×ü[€ïºåÇè÷&3nSëÉì‘ÂØщž¾ Ç?áúaΐ˜uã/ØRfOHì’+“\»±¿Òm°9 úúÀ+À/À,À0̨̩ÀÀœ/5“šš localhostÿ" 400 157 "-" "-" "-"
2022-09-13T13:25:00.223560881Z 172.17.0.1 - - [13/Sep/2022:13:25:00 +0000] "ü)šbCÙmñ†ëd"ÏÄE‡#~LÁ„µ‘k˜«lî[0 ÐÒ`…Æ‹…R‹‡êq{Pòû⨝IôtH™~Ê1-|Ž êêÀ+À/À,À0̨̩ÀÀœ/5“" 400 157 "-" "-" "-"
2022-09-13T13:26:25.223596738Z 172.17.0.1 - - [13/Sep/2022:13:26:28 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" "-"

Paramos y borramos el contenedor

	
!docker rm -f proxy
Copy
	
proxy
	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ce4d60427dcd ubuntu "tail -f /dev/null" 26 minutes ago Exited (137) 13 minutes ago alwaysup
5b633e9d838f ubuntu "bash" 31 minutes ago Exited (0) 27 minutes ago funny_mirzakhani
da16b3a85178 ubuntu "bash" 32 minutes ago Exited (0) 32 minutes ago hardcore_kare

Datos en Dockerlink image 7

Bind mountslink image 8

Vamos a ver los contenedores que tenemos parados

	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ce4d60427dcd ubuntu "tail -f /dev/null" 26 minutes ago Exited (137) 13 minutes ago alwaysup
5b633e9d838f ubuntu "bash" 31 minutes ago Exited (0) 28 minutes ago funny_mirzakhani
da16b3a85178 ubuntu "bash" 32 minutes ago Exited (0) 32 minutes ago hardcore_kare

Vamos a borrar los dos de ubuntu en los que su comando principal es la bash y vamos a dejar el que dejamos una no operación

	
!docker rm funny_mirzakhani
Copy
	
funny_mirzakhani
	
!docker rm hardcore_kare
Copy
	
hardcore_kare
	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ce4d60427dcd ubuntu "tail -f /dev/null" 27 minutes ago Exited (137) 14 minutes ago alwaysup

Vamos a volver a ejecutar el contenedor de ubuntu que hemos dejado, esto se hace mediante el comando start

	
!docker start alwaysup
Copy
	
alwaysup

Nos metemos otra vez dentro de el

	
!docker exec -it alwaysup bash
Copy
	
root@ce4d60427dcd:/#

En el contenedor, puedo crear una nueva carpeta que se llame dockerfolder

	
!mkdir dockerfolder
Copy
	

Si listamos los archivos aparecerá la nueva carpeta

	
!ls
Copy
	
bin boot dev dockerfolder etc home lib lib32 lib64 libx32 media mnt opt proc root run sbin srv sys tmp usr var

Si salimos del contenedor

	
!exit
Copy
	
exit

Y lo borramos

	
!docker rm -f alwaysup
Copy
	
alwaysup

Si listamos todos los contenedores ya no aparece el último que hemos creado

	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

Vamos a volver a hacer todo, pero primero vamos a crear una carpeta en el host en la que compartiremos los datos con el contenedor

	
!mkdir dockerHostFolder
Copy

Vemos que dentro de la carpeta no hay nada

	
!mkdir dockerHostFolder
!ls dockerHostFolder
Copy

Ahora obtenemos nuestra ruta absoluta

	
!mkdir dockerHostFolder
!ls dockerHostFolder
!pwd
Copy
	
/home/wallabot/Documentos/web/portafolio/posts

Volvemos a crear el contenedor pero añadiendo la opción -v (bind mount). A continuación se añade la ruta absoluta de la carpeta del host y la ruta absoluta de la carpeta en el contenedor, -v <host path>:<container path>

	
!docker run -d --name alwaysup -v ~/Documentos/web/portafolio/posts/dockerHostFolder:/dockerContainerFolder ubuntu tail -f /dev/null
Copy
	
4ede4512c293bdcc155e9c8e874dfb4a28e5163f4d5c7ddda24ad2863f28921b

Entramos al contenedor, listamos los archivos y ya aparece la carpeta que habíamos creado

	
!docker exec -it alwaysup bash
Copy
	
root@4ede4512c293:/#
	
root@4ede4512c293:/# ls
Copy
	
bin dev etc lib lib64 media opt root sbin sys usr
boot dockerContainerFolder home lib32 libx32 mnt proc run srv tmp var

Vamos al directorio del contenedor que hemos compartido, creamos un archivo y salimos del contenedor

	
root@4ede4512c293:/# cd dockerContainerFolder
Copy
	
	
root@4ede4512c293:/dockerContainerFolder# touch bindFile.txt
Copy
	
	
root@4ede4512c293:/dockerContainerFolder# exit
Copy
	
exit

Vemos qué hay dentro de la carpeta compartida

	
!ls dockerHostFolder
Copy
	
bindFile.txt

Pero es más, si borramos el contenedor, el archivo sigue ahí

	
!docker rm -f alwaysup
Copy
	
alwaysup
	
!ls dockerHostFolder
Copy
	
bindFile.txt

Si vuelvo a crear un contenedor compartiendo las carpetas, todos los archivos estarán en el contenedor

	
!docker run -d --name alwaysup -v ~/Documentos/web/portafolio/posts/dockerHostFolder:/dockerContainerFolder ubuntu tail -f /dev/null
Copy
	
6c021d37ea29d8b23fe5cd4968baa446085ae1756682f65340288b4c851c362d
	
!docker exec -it alwaysup bash
Copy
	
root@6c021d37ea29:/#
	
!root@6c021d37ea29:/# ls dockerContainerFolder/
Copy
	
bindFile.txt:/#

Eliminamos el contenedor

	
!docker rm -f alwaysup
Copy
	
alwaysup
	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

Volúmeneslink image 9

Los volúmenes se crearon como una evolución de los bind mounts para dar más seguridad. Podemos listar todos los volúmenes de docker mediante docker volume ls

	
!docker volume ls
Copy
	
DRIVER VOLUME NAME

Vamos a crear un nuevo volumen para el contenedor de ubuntu, para ello usamos el comando docker volume create <volume name>

	
!docker volume create ubuntuVolume
Copy
	
ubuntuVolume

Si volvemos a listar los volúmenes aparecerá el que acabamos de crear

	
!docker volume ls
Copy
	
DRIVER VOLUME NAME
local ubuntuVolume

Sin embargo no aparece como una carpeta en el sistema de archivos del host. Con ls -d */ listamos todas las carpetas

	
!ls -d */
Copy
	
dockerHostFolder/ __pycache__/

Vamos a volver a crear un contenedor, pero ahora lo creamos con el volumen que acabamos de crear con la opción --mount, indicando el volumen fuente mediante src=<volume name> (si el volumen no existiese, docker lo crearía), a continuación el destino separado por una ,, dst=<container path>, es decir --mount src=<volume name>,dst=<container path>

	
!docker run -d --name alwaysup --mount src=ubuntuVolume,dst=/dockerVolumeFolder ubuntu tail -f /dev/null
Copy
	
42cdcddf4e46dc298a87b0570115e0b2fc900cb4c6db5eea22a61409b8cb271d

Una vez creado podemos ver los volúmenes del contenedor mediante el comando inspect y filtrando por '{{.Mounts}}'

$ docker inspect --format '{{.Mounts}}' alwaysup 
      [
          {
              volume ubuntuVolume /var/lib/docker/volumes/ubuntuVolume/_data /dockerVolumeFolder local z true 
          }
      ]
      

Vemos que el volumen se llama ubuntuVolume y ademas podemos ver la ruta dónde está guardado, en este caso en /var/lib/docker/volumes/ubuntuVolume/_data. Hacemos lo mismo que antes, nos metemos en el contenedor, creamos un archivo en la ruta del volumen, salimos y vemos en el host si se ha creado

$ docker exec -it alwaysup bash
      root@42cdcddf4e46:/# touch dockerVolumeFolder/volumeFile.txt
      root@42cdcddf4e46:/# exit
      
$ sudo ls /var/lib/docker/volumes/ubuntuVolume/_data
      volumeFile.txt
      

Está el archivo creado

Insertar y extraer archivos de un contenedorlink image 10

Primero vamos a crear un archivo que queremos copiar dentro de un contenedor

	
!touch dockerHostFolder/text.txt
Copy

Entramos en el contenedor

$ docker exec -it alwaysup bash
      root@42cdcddf4e46:/#
      

Creamos una nueva carpeta donde vamos a copiar el archivo y salimos

root@42cdcddf4e46:/# mkdir folderToCopy
      root@42cdcddf4e46:/# ls
      bin  boot  dev  dockerVolumeFolder  etc  folderToCopy  home  lib  lib32  lib64  libx32  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
      root@42cdcddf4e46:/# exit
      exit
      

Copiamos dentro del contenedor el archivo mediante el comando cp, indicando el archivo que quiero copiar, el contenedor donde lo queremos copiar y la ruta dentro del contenedor, docker cp <file> <container>:<container path>

	
!touch dockerHostFolder/text.txt
!docker cp dockerHostFolder/text.txt alwaysup:/folderToCopy
Copy

Volvemos a entrar al contenedor y comprobamos que está el archivo

$ docker exec -it alwaysup bash
      root@42cdcddf4e46:/# ls folderToCopy/
      text.txt
      

Salimos del contenedor

/# exit
      exit
      

Ahora vamos a extraer el archivo del contenedor y lo vamos a guardar en el host con otro nombre, para ello usamos el comando otra vez el comando cp, pero indicando ahora el contenedor, la ruta del archivo en el contenedor y la ruta y nombre del que queremos que tenga el archivo en el host, docker cp <container>:<docker file path> <host file path>

	
!touch dockerHostFolder/text.txt
!docker cp dockerHostFolder/text.txt alwaysup:/folderToCopy
!docker cp alwaysup:/folderToCopy/text.txt dockerHostFolder/fileExtract.txt
Copy

Vemos que está en el host

	
!touch dockerHostFolder/text.txt
!docker cp dockerHostFolder/text.txt alwaysup:/folderToCopy
!docker cp alwaysup:/folderToCopy/text.txt dockerHostFolder/fileExtract.txt
!ls dockerHostFolder
Copy
	
bindFile.txt fileExtract.txt text.txt

Aunque el contenedor esté parado también se pueden copiar archivos

Por último borramos el contenedor

	
!docker rm -f alwaysup
Copy
	
alwaysup

Imágeneslink image 11

Conceptos fundamentaleslink image 12

Las imágenes son los archivos ("plantillas") con toda la configuración para crear un contenedor. Cada vez que creamos un contenedor se crea a partir de una imagen. Cuando creábamos contenedores nuevos, la primera vez salía un mensaje diciendo que no teníamos la imagen y que iba a descargarla. En docker hub existen multitud de imágenes con todo tipo de máquinas, pero para un entorno de desarrollo muy específico podemos crear nuestra propia plantilla para pasársela a alguien y trabaje en un contenedor con la misma configuración que el nuestro

Podemos ver todas las imágenes que tenemos guardadas en nuestro ordenador mediante el comand docker image ls

	
!docker image ls
Copy
	
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 2d389e545974 8 hours ago 142MB
ubuntu latest 2dc39ba059dc 11 days ago 77.8MB
hello-world latest feb5d9fea6a5 11 months ago 13.3kB

Podemos ver los tamaños, y podemos ver como la de nginx ocupa mucho y por eso tardó más en descargarse que el resto

Otra columna que podemos ver es la de TAG, esto indica la versión de la imagen. En todas pone latest, esto quiere decir que es la última. Es decir, en el momento de descargárnosla nos hemos descargado la última versión que hay en docker hub. Esto en un entorno de desarrollo no es óptimo, porque nosotros podemos descargarnos una imagen de ubuntu, y si no especificamos versión se baja la última, por ejemplo la 20.04. Pero después de un tiempo alguien puede querer desarrollar contigo y descargarse esa imagen, pero al no especificar la versión se descargará otra vez la última, que en su caso puede ser la 22.04. Esto puede dar lugar a problemas y a que cosas que a una de las personas le funcione y a la otra no

Podemos ver todas las imágenes que hay en docker hub llendo a https://hub.docker.com/. Ahí podrás buscar la imagen que mejor se adapte al proyecto que quieras hacer. Si navegamos a la image de Ubuntu, por ejemplo, podemos ver las versiones (tags) de las imágenes.

Vamos a descargarnos, pero no ejecutar una imagen. Para ello usamos el comando docker pull <hub> <image name>:<tag>. Si no indicamos el hub, lo descargará de docker hub por defecto, pero podemos indicar otro, por ejemplo uno privado de nuestra organización. También, si no indicamos el tag, por defecto bajará la última versión

	
!docker pull ubuntu:20.04
Copy
	
20.04: Pulling from library/ubuntu
Digest: sha256:35ab2bf57814e9ff49e365efd5a5935b6915eede5c7f8581e9e1b85e0eecbe16[1A
Status: Downloaded newer image for ubuntu:20.04
docker.io/library/ubuntu:20.04

Si volvemos a listar las imágenes, vemos que ahora tenemos dos imágenes de ubuntu, una con el tag 20.04 y otra con el tag latest

	
!docker image ls
Copy
	
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 2d389e545974 8 hours ago 142MB
ubuntu latest 2dc39ba059dc 11 days ago 77.8MB
ubuntu 20.04 a0ce5a295b63 11 days ago 72.8MB
hello-world latest feb5d9fea6a5 11 months ago 13.3kB

Crear imágenes mediante Dockerfilelink image 13

Creamos un directorio en el host llamado dockerImages para trabajar en el

	
!mkdir dockerImages
Copy

Creamos un archivo Dockerfile con el que crearemos una imagen

	
!mkdir dockerImages
!touch dockerImages/Dockerfile
Copy

Abrirmos el archivo creado con nuestro editor preferido y escribimos los siguiente:

FROM ubuntu:latest
      

Esto le dice a docker que cree la imagen a raiz de la imagen latest de ubuntu

A continuación escribimos un comando que se va a ejecutar en tiempo de compilación

RUN touch /test.txt
      

Esto quiere decir que cuando se compile el Dockefile se ejecutará ese comando, pero no cuando se corra el contenedor de la imagen

Al final el Dockerfile queda así:

FROM ubuntu:latest
          RUN touch /test.txt
      

Compilamos el Dockerfile mediante el comando build, con la opción -t podemos darle un tag. Por último hay que indicarle la ruta del contexto de build, más adelante explicaremos esto

	
!mkdir dockerImages
!touch dockerImages/Dockerfile
!docker build -t ubuntu:test ./dockerImages
Copy
	
Sending build context to Docker daemon 2.048kB
Step 1/2 : FROM ubuntu:latest
---> 2dc39ba059dc
Step 2/2 : RUN touch /test.txt
---> Using cache
---> a78cf3ea16d8
Successfully built a78cf3ea16d8
Successfully tagged ubuntu:test
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them

Como vemos se compila en 2 pasos, cada uno tiene una id, cada uno de esos ids son capas de la imagen, esto también lo veremos más adelante

Volvemos a ver las imágenes que tenemos guardadas en nuestro ordenador y aparece la que acabamos de crear

	
!docker image ls
Copy
	
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu test a78cf3ea16d8 8 minutes ago 77.8MB
nginx latest 2d389e545974 8 hours ago 142MB
ubuntu latest 2dc39ba059dc 11 days ago 77.8MB
ubuntu 20.04 a0ce5a295b63 11 days ago 72.8MB
hello-world latest feb5d9fea6a5 11 months ago 13.3kB

Corremos el contenedor a partir de la imagen que acabamos de crear

$ docker run -it ubuntu:test
      root@b57b9d4eedeb:/#
      

Entramos en el bash del contenedor. Como dijimos, el comando RUN se ejecutaba en tiempo de compilación de la imagen, por lo que el archivo que hemos pedido que se creara debería estar en nuestro contenedor

root@b57b9d4eedeb:/# ls
      bin  boot  dev  etc  home  lib  lib32  lib64  libx32  media  mnt  opt  proc  root  run  sbin  srv  sys  test.txt  tmp  usr  var
      

Es importante entender que ese archivo se creó cuando se construyó la imagen, es decir, la imagen del contenedor ya tiene ese archivo. No se crea cuando se lanza el contenedor

Salimos del contenedor

root@b57b9d4eedeb:/# exit
      exit
      

Como ya tenemos una imagen la podríamos subir al hub de docker, pero vamos a volver a listar las imágenes antes de eso

	
!docker image ls
Copy
	
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu test a78cf3ea16d8 20 minutes ago 77.8MB
nginx latest 2d389e545974 8 hours ago 142MB
ubuntu latest 2dc39ba059dc 11 days ago 77.8MB
ubuntu 20.04 a0ce5a295b63 11 days ago 72.8MB
hello-world latest feb5d9fea6a5 11 months ago 13.3kB

Si vemos nos está diciendo que la imagen que acabamos de crear pertenece al repositorio de ubuntu, pero nosotros no tenemos acceso al repositorio de ubuntu, por lo que en docker hub nos tenemos que hacer una cuenta para poder subir la imagen a nuestro repositorio. En mi caso, mi repositorio se llama maximofn, por lo que cambio el repositorio de la imagen mediante el comando tag, indicándole la imagen a la que queremos cambiar de repositorio y el nuevo repositorio. En el nuevo repositorio se suele indicar el nombre del repositorio seguido del tipo de imagen y el tag, en mi caso maximofn/ubuntu:test

	
!docker tag ubuntu:test maximofn/ubuntu:test
Copy

Si ahora volvemos a listar las imágenes

	
!docker tag ubuntu:test maximofn/ubuntu:test
!docker image ls
Copy
	
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu test a78cf3ea16d8 24 minutes ago 77.8MB
maximofn/ubuntu test a78cf3ea16d8 24 minutes ago 77.8MB
nginx latest 2d389e545974 8 hours ago 142MB
ubuntu latest 2dc39ba059dc 11 days ago 77.8MB
ubuntu 20.04 a0ce5a295b63 11 days ago 72.8MB
hello-world latest feb5d9fea6a5 11 months ago 13.3kB

Ahora debemos loggearnos dentro de docker hub para poder subir la imagen, para ello usamos el comando login

$ docker login
      Login with your Docker ID to push and pull images from Docker Hub. If you do not have a Docker ID, head over to https://hub.docker.com to create one.
      Username: maximofn
      Password:
      
      Login Succeeded
      

Ahora podemos subir la imagen mediante el comando push

	
!docker push maximofn/ubuntu:test
Copy
	
The push refers to repository [docker.io/maximofn/ubuntu]
06994357: Preparing
06994357: Pushed from library/ubuntu test: digest: sha256:318d83fc3c35ff930d695b0dc1c5ad1b0ea54e1ec6e3478b8ca85c05fd793c4e size: 735

Ha subido solo la primera capa, la segunda, como la use a raiz de la imagen de ubuntu, lo que hace es colocar un puntero a esa imagen para no tener capas subidas más de una vez

Hay que tener en cuenta que este repositorio es público, por lo que no debes subir imágenes con datos sensibles. Además, si una imagen no tiene uso en 6 meses será borrada

El sistema de capaslink image 14

Mediante el comando history podemos ver las capas de una imagen. Si vemos las capas de la imagen que acabamos de crear usamos docker history ubuntu:test

	
!docker history ubuntu:test
Copy
	
IMAGE CREATED CREATED BY SIZE COMMENT
a78cf3ea16d8 3 minutes ago /bin/sh -c touch /test.txt 0B
2dc39ba059dc 12 days ago /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> 12 days ago /bin/sh -c #(nop) ADD file:a7268f82a86219801… 77.8MB

Vemos que la primera capa tiene el comando que hemos introducido en el Dockerfile, además dice que ha sido creada hace 3 minutos. Sin embargo, el resto de capas fueron creadas hace 12 días, y son las capas de la imagen de ubuntu de la que nos hemos basado

Al Dockerfile que hemos creado antes le añadimos la línea

RUN rm /test.txt
      

Al final el Dockerfile queda así:

FROM ubuntu:latest
          RUN touch /test.txt
          RUN rm /test.txt
      

Si volvemos a compilar, vemos qué pasa

	
!docker build -t ubuntu:test ./dockerImages
Copy
	
Sending build context to Docker daemon 2.048kB
Step 1/3 : FROM ubuntu:latest
---> 2dc39ba059dc
Step 2/3 : RUN touch /test.txt
---> Using cache
---> a78cf3ea16d8
Step 3/3 : RUN rm /test.txt
---> Running in c2e6887f2025
Removing intermediate container c2e6887f2025
---> 313243a9b573
Successfully built 313243a9b573
Successfully tagged ubuntu:test
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them

Como vemos hay una capa más con la nueva línea que hemos agregado. Si volvemos a ver las capas de la imagen con history

	
!docker history ubuntu:test
Copy
	
IMAGE CREATED CREATED BY SIZE COMMENT
313243a9b573 About a minute ago /bin/sh -c rm /test.txt 0B
a78cf3ea16d8 3 minutes ago /bin/sh -c touch /test.txt 0B
2dc39ba059dc 12 days ago /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> 12 days ago /bin/sh -c #(nop) ADD file:a7268f82a86219801… 77.8MB

Vemos que las primeras capas son iguales que antes y ha añadido una nueva capa con el nuevo comando

Búsqueda en docker hublink image 15

No hace flata meterse en la página de docker hub para buscar imágenes, se puede hacer desde la terminal. Para ello usamos el comando docker search <image name>

	
!docker search ubuntu
Copy
	
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
ubuntu Ubuntu is a Debian-based Linux operating sys… 16425 [OK]
websphere-liberty WebSphere Liberty multi-architecture images … 297 [OK]
open-liberty Open Liberty multi-architecture images based… 62 [OK]
neurodebian NeuroDebian provides neuroscience research s… 104 [OK]
ubuntu-debootstrap DEPRECATED; use "ubuntu" instead 52 [OK]
ubuntu-upstart DEPRECATED, as is Upstart (find other proces… 115 [OK]
ubuntu/nginx Nginx, a high-performance reverse proxy & we… 98
ubuntu/squid Squid is a caching proxy for the Web. Long-t… 66
ubuntu/cortex Cortex provides storage for Prometheus. Long… 4
ubuntu/apache2 Apache, a secure & extensible open-source HT… 60
ubuntu/kafka Apache Kafka, a distributed event streaming … 35
ubuntu/mysql MySQL open source fast, stable, multi-thread… 53
ubuntu/bind9 BIND 9 is a very flexible, full-featured DNS… 62
ubuntu/prometheus Prometheus is a systems and service monitori… 51
ubuntu/zookeeper ZooKeeper maintains configuration informatio… 12
ubuntu/postgres PostgreSQL is an open source object-relation… 31
ubuntu/redis Redis, an open source key-value store. Long-… 19
ubuntu/grafana Grafana, a feature rich metrics dashboard & … 9
ubuntu/memcached Memcached, in-memory keyvalue store for smal… 5
ubuntu/dotnet-aspnet Chiselled Ubuntu runtime image for ASP.NET a… 11
ubuntu/dotnet-deps Chiselled Ubuntu for self-contained .NET & A… 11
ubuntu/prometheus-alertmanager Alertmanager handles client alerts from Prom… 9
ubuntu/dotnet-runtime Chiselled Ubuntu runtime image for .NET apps… 10
ubuntu/cassandra Cassandra, an open source NoSQL distributed … 2
ubuntu/telegraf Telegraf collects, processes, aggregates & w… 4

Uso de docker para crear aplicacioneslink image 16

Exposición de puertoslink image 17

Antes vimos como podíamos vincular un puerto de un contenedor a un puerto del ordenador (-p 8080:80). Pero para que eso sea posible, a la hora de crear la imagen hay que exponer el puerto, esto se hace añadiendo al Dockerfile la linea EXPOSE <port>, en el caso de antes

EXPOSE 80
      

O usar imágenes como base que ya tengan puertos expuestos

Reuso del caché de capas al compilarlink image 18

Cuando compilamos una imagen, si alguna de las capas que hemos definido ya han sido compiladas antes, docker lo detecta y las usa, no las vuelve a compilar. Si volvemos a compilar la imagen que hemos definido en el Dockerfile ahora tardará muy poco, porque todas las capas ya están compiladas y docker no las vuelve a compilar

	
!docker build -t ubuntu:test ./dockerImages
Copy
	
Sending build context to Docker daemon 2.048kB
Step 1/3 : FROM ubuntu:latest
---> 2dc39ba059dc
Step 2/3 : RUN touch /test.txt
---> Using cache
---> a78cf3ea16d8
Step 3/3 : RUN rm /test.txt
---> Using cache
---> 313243a9b573
Successfully built 313243a9b573
Successfully tagged ubuntu:test
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them

En la segunda y tercera capa aparece el texto Using cache

Como esto es un cuaderno Jupyter a la hora de ejecutar las celdas te da la información del tiempo que tardan en ejecutarse, la vez anterior que compilé la imagen tardó 1,4 segundos, mientras que ahora ha tardado 0,5 segundos

Pero si ahora cambio el Dockerfile, y en la primera línea, donde decía que nos basábamos en la última versión de ubuntu y cambiamos a la versión 20.04

FROM ubuntu:20.04
      

Al final el Dockerfile queda así:

FROM ubuntu:20.04
          RUN touch /test.txt
          RUN rm /test.txt
      

Si volvemos a compilar tardará mucho más

	
!docker build -t ubuntu:test ./dockerImages
Copy
	
Sending build context to Docker daemon 2.048kB
Step 1/3 : FROM ubuntu:20.04
---> a0ce5a295b63
Step 2/3 : RUN touch /test.txt
---> Running in a40fe8df2c0d
Removing intermediate container a40fe8df2c0d
---> 0bb9b452c11f
Step 3/3 : RUN rm /test.txt
---> Running in 2e14919f3685
Removing intermediate container 2e14919f3685
---> fdc248fa833b
Successfully built fdc248fa833b
Successfully tagged ubuntu:test
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them

Ha tardado 1,9 segundos y ya no aparece el texto Using cache

Al cambiar la primera capa, docker vuelve a compilar todas las capas. Esto puede ser un problema porque a la hora de desarrollar código se puede dar el siguiente caso

  • Desarrollamos el código en nuestro ordenador
  • Al construir la imagen copiamos todo el código de nuestro ordenador al contenedor
  • Luego le pedimos a la imagen que instale las librerías necesarias

Esto puede hacer que al cambiar cualquier parte del código, al tener que volver a compilar la imagen, la capa en la que se instalan las librerías se tenga que volver a compilar, ya que ha cambiado una capa anterior

Para solucionar esto la idea sería que a la hora de crear la imagen, primero pidamos que se instalen las librerías y luego que se copie el código de nuestro ordenador al contenedor. Así cada vez que cambiemos el código y volvamos a compilar la imagen, solo se recompilará la capa en la que se copia el código, por lo que la compilación será más rápida

Podrás pensar, que es mejor compartir una carpeta entre el host y el contenedor (bind mount) donde tendremos el código y así no hace falta volver a compilar la imagen cada vez que cambiemo el código. Y la respuesta es que es verdad, solo he puesto este ejemplo porque es muy fácil de entender, pero es para escenificar que a la hora de crear imágenes hay que pensar bien de manera que si hace falta volver a compilarla, recompile el mínimo número de capas

Escribir correctamente un Dockerfilelink image 19

Como hemos visto Docker no vuelve a compilar capas de un Dockerfile si ya las ha compilado antes, por lo que las carga de cache. Vamos a ver cómo tiene que ser la forma correcta de escribir un Dockerfile para aprovecharnos de esto

Vamos a partir de este Dockerfile para ir comentando posibles correcciones

FROM ubuntu
      COPY ./sourceCode /sourceCode
      RUN apt-get update
      RUN apt-get install -y python3 ssh
      CMD ["python3", "/sourceCode/sourceApp/app.py"]
      

Como se puede ver se parte de una imagen de ubuntu, se copia la carpeta con el código, se actualizan los repositorios, se instala python, se instala atmbién ssh y se corre la aplicación

Copiar el código antes de la ejecuciónlink image 20

Como hemos dicho antes, si primero copiamos el código y luego instalamos python, cada vez que hagamos un cambio en el código y compilemos la imagen la compilará entera, pero si copiamos el código después de instalar python, cada vez que cambiemos el código y compilemos la imagen, solo compilará desde la copia del código y no volverá a instalar python, por lo que el Dockerfile debería pasar a ser así

FROM ubuntu
      RUN apt-get update
      RUN apt-get install -y python3 ssh
      COPY ./sourceCode /sourceCode
      CMD ["python3", "/sourceCode/sourceApp/app.py"]
      

Copiar solo el código nesariolink image 21

Estamos copiando la carpeta con todo el código, pero a lo mejor dentro tenemos código que no necesitamos, por lo que hay que copiar solo el código que deverdad necesitemos para aplicación, de esta manera la imagen ocupará menos memoria. De modo que el Dockerfile quedaría así

FROM ubuntu
      RUN apt-get update
      RUN apt-get install -y python3 ssh
      COPY ./sourceCode/sourceApp /sourceCode/sourceApp
      CMD ["python3", "/sourceCode/sourceApp/app.py"]
      

Actualizar repositorios e instalar python en la misma línealink image 22

Estamos actualizando los repositorios en una líea y en otra instalando python3. Pero puede darse el caso que en la linea en la que instalamos python3 además añadamos pip3, sin embargo, como la línea anterior, en la que se actualizan los repositorios, como no cambia, no se volverá a compilar, al estar cacheada. Por lo que puede pasar que en los repositorios que habíamos actualizado no estén los necesarios para pip3. Por lo que hay que poner las dos acciones an una sola línea

FROM ubuntu
      RUN apt-get update && apt-get install -y python3 ssh
      COPY ./sourceCode/sourceApp /sourceCode/sourceApp
      CMD ["python3", "/sourceCode/sourceApp/app.py"]
      

No instalar sshlink image 23

Habíamos instalado ssh en la imagen para poder debuggear en caso de necesitarlo, pero eso hace que la imagen ocupe más memoria. En caso de necesitar debuggear, deberíamos entrar en el contenedor, instalar ssh y a continuación debuggear. Por lo que quitamos las instalación de ssh

FROM ubuntu
      RUN apt-get update && apt-get install -y python3
      COPY ./sourceCode/sourceApp /sourceCode/sourceApp
      CMD ["python3", "/sourceCode/sourceApp/app.py"]
      

Usar --no-install-recommendslink image 24

Cuando instalamos algo en Ubuntu te instala paquetes recomendados, pero que no necesitamos, por lo que la imagen ocupa más espacio. Así que para evitarlo añadimos a la instalación --no-install-recommends

FROM ubuntu
      RUN apt-get update && apt-get install -y python3 --no-install-recommends
      COPY ./sourceCode/sourceApp /sourceCode/sourceApp
      CMD ["python3", "/sourceCode/sourceApp/app.py"]
      

Borrar lista de repositorios actualizadoslink image 25

Hemos actualizado la lista de repositorios y hemos instalado python, pero una vez hecho ya no necesitamos la lista de repositorios actualizados, porque lo único que harán será que la imagen ocupe más, de modo que los eliminamos después de instaar python y en su misma línea

FROM ubuntu
      RUN apt-get update && apt-get install -y python3 --no-install-recommends && rm -rf /var/lib/apt/lists/*
      COPY ./sourceCode/sourceApp /sourceCode/sourceApp
      CMD ["python3", "/sourceCode/sourceApp/app.py"]
      

Usar una imagen de Pythonlink image 26

Todo lo que hemos hecho de actualizar la lista de paquetes e instalar python no es necesario, ya que ya existen imágenes de python sobre Ubuntu, que seguramente también han seguido buenas prácticas, que incluso lo hayan hecho mejor que nosotros y que ha sido escaneada en busca de vulnerabilidades por Docker Hub. Por lo que quitamos todo eso y partimos de una imagen de Python

FROM python
      COPY ./sourceCode/sourceApp /sourceCode/sourceApp
      CMD ["python3", "/sourceCode/sourceApp/app.py"]
      

Especificar la imagen de Pythonlink image 27

Al no especificar la imagen de python se está bajando la última, pero en función de cuando construyas el contenedor se puede bajar una u otra, por lo que hay que añadir el tag con la versión de Python que se quiere

FROM python:3.9.18
      COPY ./sourceCode/sourceApp /sourceCode/sourceApp
      CMD ["python3", "/sourceCode/sourceApp/app.py"]
      

Elegir un tag pequeñolink image 28

Hemos elegido el tag 3.9.18, pero esa versión de python tiene un montón de librerías que posiblemente no las necesitemos, por lo que podemos usar las versiones 3.9.18-slim que tiene muchas menos librerías instaladas, o la verisón 3.9.18-alphine que es una versión de python sobre alpine y no sobre Ubuntu. Alpine es una distribución de Linux muy ligera que tiene muy pocos paquetes instalados y que se suele usar mucho en contenedores Docker para que ocupen muy poco espacio

La imagen de python 3.9.18 ocupa 997 MB, la 3.9.18-slim ocupa 126 MB y la 3.9.18-alpine ocupa 47.8 MB

FROM python:3.9.18-alpine
      COPY ./sourceCode/sourceApp /sourceCode/sourceApp
      CMD ["python3", "/sourceCode/sourceApp/app.py"]
      

Indicar el workspacelink image 29

En vez de indicar la ruta de la imagen /sourceCode/sourceApp establecemos que dicha ruta sea el workspace de la imagen. Así cuando copiemos el código o ejecutemos la aplicación no hace falta indicar la ruta

FROM python:3.9.18-alpine
      WORKDIR /sourceCode/sourceApp
      COPY ./sourceCode/sourceApp .
      CMD ["python3", "app.py"]
      

Indicar el workspacelink image 30

En vez de indicar la ruta de la imagen /sourceCode/sourceApp establecemos que dicha ruta sea el workspace de la imagen. Así cuando copiemos el código o ejecutemos la aplicación no hace falta indicar la ruta

FROM python:3.9.18-alpine
      WORKDIR /sourceCode/sourceApp
      COPY ./sourceCode/sourceApp .
      CMD ["python3", "app.py"]
      

Código compartido en una carpeta bind mountlink image 31

Habíamos creado una carpeta llamada dockerHostFolder en la que habíamos compartido archivos entre el host y un contenedor. Dentro además debería haber tres archivos

	
!ls dockerHostFolder
Copy
	
bindFile.txt fileExtract.txt text.txt

Vamos a aprovechar el archivo text.txt para ver eso. Vamos a ver qué hay dentro de text.txt

	
!cat dockerHostFolder/text.txt
Copy

No hay salida, el archivo está vacío. Vamos a crear otra vez un contenedor de ubuntu compartiendo la carpeta dockerHostFolder

	
!cat dockerHostFolder/text.txt
!docker run --name alwaysup -d -v ~/Documentos/web/portafolio/posts/dockerHostFolder:/dockerContainerFolder ubuntu tail -f /dev/null
Copy
	
24adbded61f507cdf7f192eb5e246e43ee3ffafc9944b7c57918eb2d547dff19

Vemos que el contenedor está corriendo

	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
24adbded61f5 ubuntu "tail -f /dev/null" 16 seconds ago Up 15 seconds alwaysup

Entramos en el contenedor, vemos que está text.txt y que está vacío

$ docker exec -it alwaysup bash
      root@24adbded61f5:/# ls dockerContainerFolder/
      bindFile.txt  fileExtract.txt  text.txt
      root@24adbded61f5:/# cat dockerContainerFolder/text.txt 
      root@24adbded61f5:/#
      

Ahora abrimos en el host el archivo text.txt con el editor de textos que queramos, escribimos Hola mundo y guardamos. Si ahora vemos qué hay dentro del archivo en el contenedor veremos el mismo texto

root@24adbded61f5:/# cat dockerContainerFolder/text.txt 
      Hola mundo
      

Ahora editamos el archivo en el contenedor y salimos del contenedor

root@24adbded61f5:/# echo hola contenedor > dockerContainerFolder/text.txt 
      root@24adbded61f5:/# cat dockerContainerFolder/text.txt 
      hola contenedor
      root@24adbded61f5:/# exit
      exit
      

Si miramos el archivo en el host veremos el texto que escribimos en el contenedor

	
!cat dockerHostFolder/text.txt
Copy
	
hola contenedor

Borramos el contenedor

	
!docker rm -f alwaysup
Copy
	
alwaysup

Conectar contenedores por redlink image 32

En caso de que queramos tener varios contenedores corriendo y queremos que se comuniquen, podemos hacer que se comuniquen por red. Docker nos da la posibilidad de hacer eso mediante sus redes virtuales

Vamos a ver qué redes tiene docker mediante el comando docker network ls

	
!docker network ls
Copy
	
NETWORK ID NAME DRIVER SCOPE
de6e8b7b737e bridge bridge local
da1f5f6fccc0 host host local
d3b0d93993c0 none null local

Vemos que por defecto docker tiene tres redes

  • bridge: Está por retrocompatibilidad con versiones anteriores pero no deberíamos usarla ya
  • host: Es la red del host
  • none: Esta es la que debemos usar si queremos que un contenedor no tenga acceso a internet

Podemos crear redes nuevas que otros contenedores se puedan conectar a ella, para ello usamos el comando docker network create <name>, para que otros contenedores se puedan conectar además debemos añadir la opción --attachable

	
!docker network create --attachable myNetwork
Copy
	
2f6f3ddbfa8642e9f6819aa0965c16339e9e910be7bcf56ebb718fcac324cc27

Podemos inspeccionarla mediante el comando docker network inspect <name>

	
!docker network inspect myNetwork
Copy
	
[
{
"Name": "myNetwork",
"Id": "2f6f3ddbfa8642e9f6819aa0965c16339e9e910be7bcf56ebb718fcac324cc27",
"Created": "2022-09-14T15:20:08.539830161+02:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
]
},
"Internal": false,
"Attachable": true,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]

Ahora tenemos que crear dos contenedores para que se puedan comunicar.

Vamos a crear un nuevo contenedor, que llamaremos container1, con una carpeta compartida y que en su interior se va a llamar folder1

	
!docker run --name container1 -d -v ~/Documentos/web/portafolio/posts/dockerHostFolder:/folder1 ubuntu tail -f /dev/null
Copy
	
a5fca8ba1e4ff0a67002f8f1b8cc3cd43185373c2a7e295546f774059ad8dd1a

Ahora creamos otro contenedor, llamado container2, con otra carpeta compartida, pero que se llame folder2

	
!docker run --name container2 -d -v ~/Documentos/web/portafolio/posts/dockerHostFolder:/folder2 ubuntu tail -f /dev/null
Copy
	
6c8dc18315488ef686f7548516c19b3d716728dd8a173cdb889ec0dd082232f9

Vemos los contenedores corriendo y vemos que están los dos

	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6c8dc1831548 ubuntu "tail -f /dev/null" 3 seconds ago Up 2 seconds container2
a5fca8ba1e4f ubuntu "tail -f /dev/null" 4 seconds ago Up 3 seconds container1

Ahora tenemos que conectar los contenedores a la red, para ello usamos el comando docker network connect <network name> <container name>

	
!docker network connect myNetwork container1
Copy
	
!docker network connect myNetwork container1
!docker network connect myNetwork container2
Copy

Para comprobar que se han conectado bien podemos inspeccionar la red, pero filtrando por los contenedores conectados

$ docker network inspect --format '{{.Containers}}' myNetwork 
      map
      [
          6c8dc18315488ef686f7548516c19b3d716728dd8a173cdb889ec0dd082232f9:
          {
              container2 
              f828d211e894f7a5a992ce41a2a0def8e2424e9737fb4e1485fc09cc2d607b69 
              02:42:ac:12:00:03 
              172.18.0.3/16 
          }
          a5fca8ba1e4ff0a67002f8f1b8cc3cd43185373c2a7e295546f774059ad8dd1a:
          {
              container1 
              cff762e6286ebc169804b2a675bbff904102de796751d367c18d4b490c994c45 
              02:42:ac:12:00:02 
              172.18.0.2/16 
          }
      ]
      

Como podemos ver el contenedor container1 tiene la IP 172.18.0.2 y el contenedor container2 tiene la IP 172.18.0.3

Nos metemos dentro del contenedor container1 e instalamos ping

$ docker exec -it container1 bash
      root@a5fca8ba1e4f:/# apt update
          ...
      root@a5fca8ba1e4f:/# apt install iputils-ping
          ...
      root@a5fca8ba1e4f:/#
      

Nos metemos dentro del contenedor container2 e instalamos ping

$ docker exec -it container2 bash
      root@a5fca8ba1e4f:/# apt update
          ...
      root@a5fca8ba1e4f:/# apt install iputils-ping
          ...
      root@a5fca8ba1e4f:/#
      

Ahora desde el contenedor container1 hacemos un ping a la IP 172.18.0.3, que pertenece al contenedor container2

root@a5fca8ba1e4f:/# ping 172.18.0.3
      PING 172.18.0.3 (172.18.0.3) 56(84) bytes of data.
      64 bytes from 172.18.0.3: icmp_seq=1 ttl=64 time=0.115 ms
      64 bytes from 172.18.0.3: icmp_seq=2 ttl=64 time=0.049 ms
      64 bytes from 172.18.0.3: icmp_seq=3 ttl=64 time=0.056 ms
      64 bytes from 172.18.0.3: icmp_seq=4 ttl=64 time=0.060 ms
      ^C
      --- 172.18.0.3 ping statistics ---
      4 packets transmitted, 4 received, 0% packet loss, time 3068ms
      rtt min/avg/max/mdev = 0.049/0.070/0.115/0.026 ms
      

Y desde el contenedor container2 hacemos un ping a la IP 172.18.0.2, que pertenece al contenedor container1

root@6c8dc1831548:/# ping 172.18.0.2
      PING 172.18.0.2 (172.18.0.2) 56(84) bytes of data.
      64 bytes from 172.18.0.2: icmp_seq=1 ttl=64 time=0.076 ms
      64 bytes from 172.18.0.2: icmp_seq=2 ttl=64 time=0.045 ms
      64 bytes from 172.18.0.2: icmp_seq=3 ttl=64 time=0.049 ms
      64 bytes from 172.18.0.2: icmp_seq=4 ttl=64 time=0.051 ms
      ^C
      --- 172.18.0.2 ping statistics ---
      4 packets transmitted, 4 received, 0% packet loss, time 3074ms
      rtt min/avg/max/mdev = 0.045/0.055/0.076/0.012 ms
      

Pero hay una cosa mejor que nos permite hacer docker, si no me sé la IP del contenedor al que me quiero conectar, en vez de escribir su IP puedo escribir su nombre

Ahora desde el contenedor container1 hacemos un ping a la IP container2

root@a5fca8ba1e4f:/# ping container2
      PING container2 (172.18.0.3) 56(84) bytes of data.
      64 bytes from container2.myNetwork (172.18.0.3): icmp_seq=1 ttl=64 time=0.048 ms
      64 bytes from container2.myNetwork (172.18.0.3): icmp_seq=2 ttl=64 time=0.050 ms
      64 bytes from container2.myNetwork (172.18.0.3): icmp_seq=3 ttl=64 time=0.052 ms
      64 bytes from container2.myNetwork (172.18.0.3): icmp_seq=4 ttl=64 time=0.053 ms
      ^C
      --- container2 ping statistics ---
      4 packets transmitted, 4 received, 0% packet loss, time 3071ms
      rtt min/avg/max/mdev = 0.048/0.050/0.053/0.002 ms
      

Como vemos docker sabe que la IP del contenedor conteiner2 es la 172.18.0.3

Y desde el contenedor container2 hacemos un ping a la IP container1

root@6c8dc1831548:/# ping container1
      PING container1 (172.18.0.2) 56(84) bytes of data.
      64 bytes from container1.myNetwork (172.18.0.2): icmp_seq=1 ttl=64 time=0.051 ms
      64 bytes from container1.myNetwork (172.18.0.2): icmp_seq=2 ttl=64 time=0.058 ms
      64 bytes from container1.myNetwork (172.18.0.2): icmp_seq=3 ttl=64 time=0.052 ms
      64 bytes from container1.myNetwork (172.18.0.2): icmp_seq=4 ttl=64 time=0.056 ms
      ^C
      --- container1 ping statistics ---
      4 packets transmitted, 4 received, 0% packet loss, time 3057ms
      rtt min/avg/max/mdev = 0.051/0.054/0.058/0.003 ms
      

Como vemos docker sabe que la IP del contenedor conteiner1 es la 172.18.0.2

Salimos de los contenedores y los borramos

	
!docker network connect myNetwork container1
!docker network connect myNetwork container2
!docker rm -f container1 container2
Copy
	
container1
container2

Borramos también la red que hemos creado

	
!docker network rm myNetwork
Copy
	
myNetwork

Uso de GPUslink image 33

Para poder usar las GPUs del host dentro de los contenedores docker es necesario realizar los pasos descritos en la página de instalación de Nvidia container toolkit

Configurar el repositorio y la llave GPGlink image 34

Tenemos que configurar el repositorio de nvidia container toolkit y la llave GPG, para ello ejecutamos el siguiente comando en la consola

distribution=$(. /etc/os-release;echo $ID$VERSION_ID) \
            && curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
            && curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | \
                  sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
                  sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
      

Instalación de nvidia container toolkitlink image 35

Una vez hemos actualizado el repositorio y la llave actualizamos los repositorios mediante el comando

sudo apt update
      

E instalamos nvidia container toolkit

sudo apt install -y nvidia-docker2
      

Reinicio de dockerlink image 36

Una vez hemos terminado tenemos que reiniciar el demonio de docker mediante

sudo systemctl restart docker
      

Uso de GPUslink image 37

Ahora que hemos configurado docker para poder usar las GPUs del host dentro de los contenedores lo podemos probar mediante la opción --gpus all. Si se tiene más de una GPU y solo se quiere usar 1 habría que especificarlo, pero de momento aquí solo explicamos como usar todas

Creamos un contenedor que no se va a ejecutar en segundo plano, sino que lo que va a hecer es ejecutar el comando nvidia-smi para que podamos ver si tiene acceso a las GPUs

	
!docker run --name container_gpus --gpus all ubuntu nvidia-smi
Copy
	
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
6a12be2b: Pull complete .54MB/29.54MBBDigest: sha256:aabed3296a3d45cede1dc866a24476c4d7e093aa806263c27ddaadbdce3c1054
Status: Downloaded newer image for ubuntu:latest
Mon Sep 4 07:10:36 2023
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 510.39.01 Driver Version: 510.39.01 CUDA Version: 11.6 |
|-------------------------------+----------------------+----------------------+
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|===============================+======================+======================|
| 0 Quadro T1000 On | 00000000:01:00.0 Off | N/A |
| N/A 44C P0 15W / N/A | 9MiB / 4096MiB | 0% Default |
| | | N/A |
+-------------------------------+----------------------+----------------------+
+-----------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=============================================================================|
| 0 N/A N/A 2545 G 4MiB |
| 0 N/A N/A 3421 G 4MiB |
+-----------------------------------------------------------------------------+

Borramos el contenedor

	
!doker rm container_gpus
Copy

Docker composelink image 38

Docker compose vs docker-composelink image 39

docker-compose fue una herramienta que se creó para ayudar al mantenimiento de imágenes y contenedores y había que instalarla a parte de docker. Sin embargo docker lo incorporó en sus últimas versiones y ya no es necesario instalarla, sin embargo, para usarla, en vez de usar el comando docker-compose hay que usar el comando docker compose. En muchos sitios encontrarás información con docker-compose, pero al instalar docker ya te vendrá instalado docker compose por lo que todo lo que se podía hacer con docker-compose es compatible con docker compose

Docker composelink image 40

Docker compose es una herramienta de docker que hace todo lo que hemos visto hasta ahora, pero ahorrándonos tiempo y esfuerzo. Editando un archivo .yml podemos decirle a docker compose que cree todos los contenedores que queramos.

Para usarlo una vez no habrá mucha diferencia de escribir todos los comandos que vimos antes o escribir el archivo .yml pero cuando quires volver a tener la misma configuración de contenedores trabajando, solo con llamar al archivo .yml volverá a crear toda la configuración

Vamos a crear una carpeta donde guardaremos los archivos de docker compose

	
!doker rm container_gpus
!mkdir dockerComposeFiles
Copy

Creamos dentro el archivo .yml

	
!doker rm container_gpus
!mkdir dockerComposeFiles
!touch dockerComposeFiles/docker-compose.yml
Copy

Un archivo docker compose tiene que empezar por la versión

version: "<v.v>"
      

En el momento de escribir esto, la última versión es la 3.8 así que escribimos esa

docker-compose.yml:

version: "3.8"
      

A continuación se indican los servicios, que son los contenedores, en cada servicio hay que especificar la imagen y además se pueden añadir otros parámetros como puertos, variables de entorno, etc

services:
          container1:
              image: ubuntu
      
          container2:
              image: ubuntu
      

El docker-compose.yml quedaría así:

version: "3.8"
      
          services:
          container1:
              image: ubuntu
      
          container2:
              image: ubuntu
      

Una vez que hemos creado el archivo, en su path, podemos ejecutar todo mediante el comando docker compose up, pero además añadiendo la opción -d haremos que corra en segundo plano

	
!doker rm container_gpus
!mkdir dockerComposeFiles
!touch dockerComposeFiles/docker-compose.yml
!cd dockerComposeFiles && docker compose up -d
Copy
	
[+] Running 1/0
⠿ Network dockercomposefiles_default Created 0.1s
⠋ Container dockercomposefiles-container2-1 Creating 0.0s
⠋ Container dockercomposefiles-container1-1 Creating 0.0s
[+] Running 1/3
⠿ Network dockercomposefiles_default Created 0.1s
⠙ Container dockercomposefiles-container2-1 Creating 0.1s
⠙ Container dockercomposefiles-container1-1 Creating 0.1s
[+] Running 1/3
⠿ Network dockercomposefiles_default Created 0.1s
⠿ Container dockercomposefiles-container2-1 Starting 0.2s
⠿ Container dockercomposefiles-container1-1 Starting 0.2s
[+] Running 1/3
⠿ Network dockercomposefiles_default Created 0.1s
⠿ Container dockercomposefiles-container2-1 Starting 0.3s
⠿ Container dockercomposefiles-container1-1 Starting 0.3s
[+] Running 1/3
⠿ Network dockercomposefiles_default Created 0.1s
⠿ Container dockercomposefiles-container2-1 Starting 0.4s
⠿ Container dockercomposefiles-container1-1 Starting 0.4s
[+] Running 1/3
⠿ Network dockercomposefiles_default Created 0.1s
⠿ Container dockercomposefiles-container2-1 Starting 0.5s
⠿ Container dockercomposefiles-container1-1 Starting 0.5s
[+] Running 2/3
⠿ Network dockercomposefiles_default Created 0.1s
⠿ Container dockercomposefiles-container2-1 Started 0.5s
⠿ Container dockercomposefiles-container1-1 Starting 0.6s
[+] Running 3/3
⠿ Network dockercomposefiles_default Created 0.1s
⠿ Container dockercomposefiles-container2-1 Started 0.5s
⠿ Container dockercomposefiles-container1-1 Started 0.7s

Si nos fijamos ha creado dos contenedores dockercomposefiles-container1-1 y dockercomposefiles-container2-1 y la red que los une dockercomposefiles_default

Vamos a borrar los dos contenedores

	
!docker rm -f dockercomposefiles-container1-1 dockercomposefiles-container2-1
Copy
	
dockercomposefiles-container1-1
dockercomposefiles-container2-1

Y borramos la red que ha creado

	
!docker network rm dockercomposefiles_default
Copy
	
dockercomposefiles_default

Vamos a intentar hacer lo que hicimos antes con lo que sabemos hasta ahora. Creamos una nueva imagen que venga con ping instalado

Dockerfile:

FROM ubuntu:20.04
          RUN apt update
          RUN apt install iputils-ping -y
      

Y la compilamos

	
!docker build -t ubuntu:ping ./dockerImages
Copy
	
Sending build context to Docker daemon 2.048kB
Step 1/3 : FROM ubuntu:20.04
---> a0ce5a295b63
Step 2/3 : RUN apt update
---> Running in 3bd5278d39b4
WARNING: apt does not have a stable CLI interface. Use with caution in scripts.
Get:1 http://security.ubuntu.com/ubuntu focal-security InRelease [114 kB]
Get:2 http://archive.ubuntu.com/ubuntu focal InRelease [265 kB]
Get:3 http://security.ubuntu.com/ubuntu focal-security/universe amd64 Packages [898 kB]
Get:4 http://archive.ubuntu.com/ubuntu focal-updates InRelease [114 kB]
Get:5 http://archive.ubuntu.com/ubuntu focal-backports InRelease [108 kB]
Get:6 http://archive.ubuntu.com/ubuntu focal/universe amd64 Packages [11.3 MB]
Get:7 http://security.ubuntu.com/ubuntu focal-security/main amd64 Packages [2133 kB]
Get:8 http://security.ubuntu.com/ubuntu focal-security/multiverse amd64 Packages [27.5 kB]
Get:9 http://security.ubuntu.com/ubuntu focal-security/restricted amd64 Packages [1501 kB]
Get:10 http://archive.ubuntu.com/ubuntu focal/main amd64 Packages [1275 kB]
Get:11 http://archive.ubuntu.com/ubuntu focal/restricted amd64 Packages [33.4 kB]
Get:12 http://archive.ubuntu.com/ubuntu focal/multiverse amd64 Packages [177 kB]
Get:13 http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages [2594 kB]
Get:14 http://archive.ubuntu.com/ubuntu focal-updates/restricted amd64 Packages [1613 kB]
Get:15 http://archive.ubuntu.com/ubuntu focal-updates/multiverse amd64 Packages [30.2 kB]
Get:16 http://archive.ubuntu.com/ubuntu focal-updates/universe amd64 Packages [1200 kB]
Get:17 http://archive.ubuntu.com/ubuntu focal-backports/universe amd64 Packages [27.4 kB]
...
Successfully built c3d32aa9de02
Successfully tagged ubuntu:ping
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them

Comprobamos que se ha creado

	
!docker image ls
Copy
	
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu ping c3d32aa9de02 About a minute ago 112MB
maximofn/ubuntu test a78cf3ea16d8 25 hours ago 77.8MB
nginx latest 2d389e545974 33 hours ago 142MB
ubuntu latest 2dc39ba059dc 12 days ago 77.8MB
ubuntu 20.04 a0ce5a295b63 12 days ago 72.8MB
hello-world latest feb5d9fea6a5 11 months ago 13.3kB

Le cambiamos el tag

	
!docker tag ubuntu:ping maximofn/ubuntu:ping
Copy
	
!docker tag ubuntu:ping maximofn/ubuntu:ping
!docker image ls
Copy
	
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu ping c3d32aa9de02 About a minute ago 112MB
maximofn/ubuntu ping c3d32aa9de02 About a minute ago 112MB
maximofn/ubuntu test c3d32aa9de02 About a minute ago 112MB
nginx latest 2d389e545974 33 hours ago 142MB
ubuntu latest 2dc39ba059dc 12 days ago 77.8MB
ubuntu 20.04 a0ce5a295b63 12 days ago 72.8MB
hello-world latest feb5d9fea6a5 11 months ago 13.3kB

Editamos el archivo docker compose para que coja las imágenes que acabamos de crear

docker-compose.yml:

version: "3.8"
      
          services:
            container1:
              image: maximofn/ubuntu:ping
      
          container2:
              image: maximofn/ubuntu:ping
      

Y además le decimos que ejecute una no operación

El docker-compose.yml quedaría así:

version: "3.8"
      
          services:
            container1:
              image: ubuntu
              command: tail -f /dev/null
      
          container2:
              image: ubuntu
              command: tail -f /dev/null
      

Lo levantamos

	
!cd dockerComposeFiles && docker compose up -d
Copy
	
[+] Running 0/0
⠋ Container dockercomposefiles-container1-1 Recreate 0.1s
⠋ Container dockercomposefiles-container2-1 Recreate 0.1s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Recreated 0.1s
⠙ Container dockercomposefiles-container2-1 Recreate 0.2s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Recreated 0.1s
⠹ Container dockercomposefiles-container2-1 Recreate 0.3s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Recreated 0.1s
⠸ Container dockercomposefiles-container2-1 Recreate 0.4s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Recreated 0.1s
⠼ Container dockercomposefiles-container2-1 Recreate 0.5s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Recreated 0.1s
⠴ Container dockercomposefiles-container2-1 Recreate 0.6s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Recreated 0.1s
⠦ Container dockercomposefiles-container2-1 Recreate 0.7s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Recreated 0.1s
⠧ Container dockercomposefiles-container2-1 Recreate 0.8s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Recreated 0.1s
⠇ Container dockercomposefiles-container2-1 Recreate 0.9s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Recreated 0.1s
⠏ Container dockercomposefiles-container2-1 Recreate 1.0s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Recreated 0.1s
⠋ Container dockercomposefiles-container2-1 Recreate 1.1s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Recreated 0.1s
⠙ Container dockercomposefiles-container2-1 Recreate 1.2s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Recreated 0.1s
⠹ Container dockercomposefiles-container2-1 Recreate 1.3s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Recreated 0.1s
⠸ Container dockercomposefiles-container2-1 Recreate 1.4s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Recreated 0.1s
...
[+] Running 2/2
⠿ Container dockercomposefiles-container1-1 Started 10.8s
⠿ Container dockercomposefiles-container2-1 Started 10.9s
[+] Running 2/2
⠿ Container dockercomposefiles-container1-1 Started 10.8s
⠿ Container dockercomposefiles-container2-1 Started 10.9s

Vemos los contenedores que están corriendo

	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
935939e5a75d maximofn/ubuntu:ping "tail -f /dev/null" 15 seconds ago Up 13 seconds dockercomposefiles-container2-1
f9138d7064dd maximofn/ubuntu:ping "tail -f /dev/null" 25 seconds ago Up 13 seconds dockercomposefiles-container1-1

Están los dos contenedores corriendo, ahora nos metemos en uno e intentamos hacer ping al otro

$ docker exec -it dockercomposefiles-container1-1 bash
      root@f9138d7064dd:/# ping dockercomposefiles-container2-1
      PING dockercomposefiles-container2-1 (172.21.0.3) 56(84) bytes of data.
      64 bytes from dockercomposefiles-container2-1.dockercomposefiles_default (172.21.0.3): icmp_seq=1 ttl=64 time=0.110 ms
      64 bytes from dockercomposefiles-container2-1.dockercomposefiles_default (172.21.0.3): icmp_seq=2 ttl=64 time=0.049 ms
      64 bytes from dockercomposefiles-container2-1.dockercomposefiles_default (172.21.0.3): icmp_seq=3 ttl=64 time=0.049 ms
      64 bytes from dockercomposefiles-container2-1.dockercomposefiles_default (172.21.0.3): icmp_seq=4 ttl=64 time=0.075 ms
      ^C
      --- dockercomposefiles-container2-1 ping statistics ---
      4 packets transmitted, 4 received, 0% packet loss, time 3068ms
      rtt min/avg/max/mdev = 0.049/0.070/0.110/0.025 ms
      

Como vemos podemos hacer ping, hemos creado bien la imagen con ping instalado. Además en el docker compose hemos hecho que se ejecute una no operación para que los contenedores estén corriendo

Borramos los dos contenedores y la red que ha creado

	
!docker rm -f dockercomposefiles-container1-1 dockercomposefiles-container2-1
Copy
	
dockercomposefiles-container1-1
dockercomposefiles-container2-1
	
!docker network rm dockercomposefiles_default
Copy
	
dockercomposefiles_default

Como nombra docker compose los contenedoreslink image 41

Si nos fijamos los contenedores que creo docker se llamaban dockercomposefiles-container1-1 y dockercomposefiles-container1-1. Esto es porque la carpeta en la que está el archivo de docker compose está en una carpeta llamada dockerComposeFiles, por eso la primera parte del nombre de los contenedores es dockercomposefiles, a continuación aparece el nombre del servicio que le hemos dado en el archivo docker compose (container1 y container2) y por último un numero para poder crear más si es necesario

Similar ocurre con el nombre de la red que ha creado dockercomposefiles_default

Logs en docker composelink image 42

Vamos ahora a cambiar el archivo docker compose, en las líneas en las que teníamos command: tail -f /dev/null, vamos a poner command: ping 0.0.0.0

Y además le decimos que ejecute una no operación

El docker-compose.yml quedaría así:

version: "3.8"
      
          services:
            container1:
              image: ubuntu
              command: ping 0.0.0.0
      
          container2:
              image: ubuntu
              command: ping 0.0.0.0
      

Esto lo hacemos para que cada contenedor esté escupiendo el ping constantemente, así simulamos unos logs

Si ejecutamos otra vez el docker compose

	
!cd dockerComposeFiles && docker compose up -d
Copy
	
[+] Running 0/0
⠋ Container dockercomposefiles-container1-1 Recreate 0.1s
⠋ Container dockercomposefiles-container2-1 Recreate 0.1s
[+] Running 0/2
⠙ Container dockercomposefiles-container1-1 Recreate 0.2s
⠙ Container dockercomposefiles-container2-1 Recreate 0.2s
[+] Running 0/2
⠹ Container dockercomposefiles-container1-1 Recreate 0.3s
⠹ Container dockercomposefiles-container2-1 Recreate 0.3s
[+] Running 0/2
⠸ Container dockercomposefiles-container1-1 Recreate 0.4s
⠸ Container dockercomposefiles-container2-1 Recreate 0.4s
[+] Running 0/2
⠼ Container dockercomposefiles-container1-1 Recreate 0.5s
⠼ Container dockercomposefiles-container2-1 Recreate 0.5s
[+] Running 0/2
⠴ Container dockercomposefiles-container1-1 Recreate 0.6s
⠴ Container dockercomposefiles-container2-1 Recreate 0.6s
[+] Running 0/2
⠦ Container dockercomposefiles-container1-1 Recreate 0.7s
⠦ Container dockercomposefiles-container2-1 Recreate 0.7s
[+] Running 0/2
⠧ Container dockercomposefiles-container1-1 Recreate 0.8s
⠧ Container dockercomposefiles-container2-1 Recreate 0.8s
[+] Running 0/2
...
⠿ Container dockercomposefiles-container1-1 Starting 11.0s
⠿ Container dockercomposefiles-container2-1 Started 11.0s
[+] Running 2/2
⠿ Container dockercomposefiles-container1-1 Started 11.1s
⠿ Container dockercomposefiles-container2-1 Started 11.0s

Ahora podemos ver los logs de los dos contenedores mediante el comando docker compose logs

	
!cd dockerComposeFiles && docker compose logs
Copy
	
dockercomposefiles-container2-1 | PING 0.0.0.0 (127.0.0.1) 56(84) bytes of data.
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.042 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.025 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.022 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.030 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=5 ttl=64 time=0.021 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=6 ttl=64 time=0.021 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=7 ttl=64 time=0.030 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=8 ttl=64 time=0.028 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=9 ttl=64 time=0.028 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=10 ttl=64 time=0.026 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=11 ttl=64 time=0.028 ms
dockercomposefiles-container1-1 | PING 0.0.0.0 (127.0.0.1) 56(84) bytes of data.
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=12 ttl=64 time=0.027 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=13 ttl=64 time=0.039 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=14 ttl=64 time=0.035 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=15 ttl=64 time=0.034 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=16 ttl=64 time=0.036 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=17 ttl=64 time=0.034 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=18 ttl=64 time=0.036 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=19 ttl=64 time=0.032 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=20 ttl=64 time=0.032 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=21 ttl=64 time=0.033 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=22 ttl=64 time=0.034 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.037 ms
...
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=214 ttl=64 time=0.015 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=215 ttl=64 time=0.021 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=216 ttl=64 time=0.020 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=217 ttl=64 time=0.049 ms

Como vemos podemos ver los logs de los dos contenedores, pero en el caso de querer ver solo los de uno, podemos especificar el nombre del servicio

	
!cd dockerComposeFiles && docker compose logs container1
Copy
	
dockercomposefiles-container1-1 | PING 0.0.0.0 (127.0.0.1) 56(84) bytes of data.
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.037 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.025 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.023 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.031 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=5 ttl=64 time=0.034 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=6 ttl=64 time=0.033 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=7 ttl=64 time=0.034 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=8 ttl=64 time=0.022 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=9 ttl=64 time=0.032 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=10 ttl=64 time=0.029 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=11 ttl=64 time=0.031 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=12 ttl=64 time=0.024 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=13 ttl=64 time=0.029 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=14 ttl=64 time=0.032 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=15 ttl=64 time=0.033 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=16 ttl=64 time=0.034 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=17 ttl=64 time=0.028 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=18 ttl=64 time=0.034 ms
...
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=332 ttl=64 time=0.027 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=333 ttl=64 time=0.030 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=334 ttl=64 time=0.033 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=335 ttl=64 time=0.036 ms
	
!cd dockerComposeFiles && docker compose logs container2
Copy
	
dockercomposefiles-container2-1 | PING 0.0.0.0 (127.0.0.1) 56(84) bytes of data.
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.042 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.025 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.022 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.030 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=5 ttl=64 time=0.021 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=6 ttl=64 time=0.021 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=7 ttl=64 time=0.030 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=8 ttl=64 time=0.028 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=9 ttl=64 time=0.028 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=10 ttl=64 time=0.026 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=11 ttl=64 time=0.028 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=12 ttl=64 time=0.027 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=13 ttl=64 time=0.039 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=14 ttl=64 time=0.035 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=15 ttl=64 time=0.034 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=16 ttl=64 time=0.036 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=17 ttl=64 time=0.034 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=18 ttl=64 time=0.036 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=19 ttl=64 time=0.032 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=20 ttl=64 time=0.032 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=21 ttl=64 time=0.033 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=22 ttl=64 time=0.034 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=23 ttl=64 time=0.035 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=24 ttl=64 time=0.037 ms
...
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=340 ttl=64 time=0.034 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=341 ttl=64 time=0.033 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=342 ttl=64 time=0.034 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=343 ttl=64 time=0.036 ms

Si queremos ver los logs continuamente podemos añadir la opción -f, docker compose logs - <service name>

Si he hecho un docker compose con más de dos servición, cuando se quiera ver los logs de varios servicios solo hay ue añadir más nombres al comando, docker compose logs <name service 1> <name service 2> ...

Exec servicioslink image 43

Como hemos visto, mediante el comando exec podemos entrar a un contenedor indicando el nombre del contenedor, el comando que se quiere ejecutar y la opción -it. Con docker compose esto es más sencillo, ya que solamente es necesario el nombre del servicio y el comando, pero no es necesaria la opción -it ya que docker compose lo da por supuesto

$ docker compose exec container1 bash
      root@a7cf282fe66c:/#
      

Parando docker composelink image 44

Cuando hemos terminado de trabajar, con un solo comando (stop), docker compose para todo, no hace falta ir parando uno a uno cada contenedor

	
!cd dockerComposeFiles && docker compose stop
Copy
	
[+] Running 0/0
⠋ Container dockercomposefiles-container2-1 Stopping 0.1s
⠋ Container dockercomposefiles-container1-1 Stopping 0.1s
[+] Running 0/2
⠙ Container dockercomposefiles-container2-1 Stopping 0.2s
⠙ Container dockercomposefiles-container1-1 Stopping 0.2s
[+] Running 0/2
⠹ Container dockercomposefiles-container2-1 Stopping 0.3s
⠹ Container dockercomposefiles-container1-1 Stopping 0.3s
[+] Running 0/2
⠸ Container dockercomposefiles-container2-1 Stopping 0.4s
⠸ Container dockercomposefiles-container1-1 Stopping 0.4s
[+] Running 0/2
⠼ Container dockercomposefiles-container2-1 Stopping 0.5s
⠼ Container dockercomposefiles-container1-1 Stopping 0.5s
[+] Running 0/2
⠴ Container dockercomposefiles-container2-1 Stopping 0.6s
⠴ Container dockercomposefiles-container1-1 Stopping 0.6s
[+] Running 0/2
⠦ Container dockercomposefiles-container2-1 Stopping 0.7s
⠦ Container dockercomposefiles-container1-1 Stopping 0.7s
[+] Running 0/2
⠧ Container dockercomposefiles-container2-1 Stopping 0.8s
⠧ Container dockercomposefiles-container1-1 Stopping 0.8s
...
[+] Running 1/2
⠿ Container dockercomposefiles-container2-1 Stopped 10.4s
⠸ Container dockercomposefiles-container1-1 Stopping 10.4s
[+] Running 2/2
⠿ Container dockercomposefiles-container2-1 Stopped 10.4s
⠿ Container dockercomposefiles-container1-1 Stopped 10.4s

Como se puede ver docker compose ha parado los dos contenedores, pero no los ha borrado, ni ha borrado la red

	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1e6c1dd9adb2 maximofn/ubuntu:ping "ping 0.0.0.0" 16 minutes ago Exited (137) 25 seconds ago dockercomposefiles-container2-1
a7cf282fe66c maximofn/ubuntu:ping "ping 0.0.0.0" 16 minutes ago Exited (137) 25 seconds ago dockercomposefiles-container1-1
	
!docker network ls
Copy
	
NETWORK ID NAME DRIVER SCOPE
13cc632147f3 bridge bridge local
d4a2f718cd83 dockercomposefiles_default bridge local
da1f5f6fccc0 host host local
d3b0d93993c0 none null local

Docker compose como herramienta de desarrollolink image 45

Al igual que vimos antes, para poder desarrollar, lo ideal sería compartir la carpeta que tiene el código con el servicio. Esto con docker compose se hace añadiendo la etiqueta volumes al archivo docker compose. Primero tenemos que añadirle la ruta de la carpeta donde está el codigo en el host y luego la ruta en el contenedor.

docker-compose.yml:

version: "3.8"
      
          services:
            container1:
              image: ubuntu
              command: ping 0.0.0.0
              volumes:
                  - ../dockerHostFolder/:/dockerContainerFolder
      
          container2:
              image: ubuntu
              command: ping 0.0.0.0
      

Como se puede ver, la ruta de la carpeta del host la he puesto relativa

Si levantamos el docker compose

	
!cd dockerComposeFiles && docker compose up -d
Copy
	
[+] Running 1/0
⠋ Container dockercomposefiles-container1-1 Recreate 0.1s
⠿ Container dockercomposefiles-container2-1 Created 0.0s
[+] Running 0/2
⠿ Container dockercomposefiles-container1-1 Starting 0.2s
⠿ Container dockercomposefiles-container2-1 Starting 0.2s
[+] Running 0/2
⠿ Container dockercomposefiles-container1-1 Starting 0.3s
⠿ Container dockercomposefiles-container2-1 Starting 0.3s
[+] Running 0/2
⠿ Container dockercomposefiles-container1-1 Starting 0.4s
⠿ Container dockercomposefiles-container2-1 Starting 0.4s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Started 0.5s
⠿ Container dockercomposefiles-container2-1 Starting 0.5s
[+] Running 2/2
⠿ Container dockercomposefiles-container1-1 Started 0.5s
⠿ Container dockercomposefiles-container2-1 Started 0.6s

Si entramos dentro del contenedor, podemos ver qué hay dentro del archivo text.txt

$ docker compose exec container1 bash
      root@c8aae9d619d3:/# ls dockerContainerFolder/
      bindFile.txt  fileExtract.txt  text.txt
      root@c8aae9d619d3:/# cat dockerContainerFolder/text.txt 
      hola contenedor
      

Si ahora lo abrimos en el host, escribimos hola host y volvemos a ver en el contenedor

root@c8aae9d619d3:/# cat dockerContainerFolder/text.txt 
      hola host
      

Y ahora al revés, si lo modificamos en el contenedor

root@c8aae9d619d3:/# echo hola compose > dockerContainerFolder/text.txt
      root@c8aae9d619d3:/# exit
      exit
      

Si lo vemos desde el host debemos obtener hola compose

	
!cat dockerHostFolder/text.txt
Copy
	
hola compose

Exposición de puertos en docker composelink image 46

También podemos configurar los puertos en el archivo de docker compose, mediante la etiqueta ports, indicando el puerto del host y a continuación la ip del servicio

ports:
          - <host port>:<service port>
      

Docker compose en equipo - docker overridelink image 47

Si somos un grupo de personas desarrollando sobre docker con docker compose, es probable que muchas personas anden cambiando el archivo docker compose, lo cual puede hacer que no se sincronicen bien y hayan conflictos.

Para solucionar esto docker ofrece una herramienta llamada docker override. De esta manera puede haber un archivo docker compose base y que cada uno lo modifique mediante docker override.

Para hacer esto, ahora tenemos que crear un archivo llamado docker-compose.override.yml que será el que podremos editar

	
!touch dockerComposeFiles/docker-compose.override.yml
Copy

Si ahora intentamos levantar el docker compose vamos a recibir un error

	
!touch dockerComposeFiles/docker-compose.override.yml
!cd dockerComposeFiles && docker compose up -d
Copy
	
Top-level object must be a mapping

Y esto es porque docker compose ha detectado que hay un archivo llamado docker-compose.override.yml y que está vacío, por lo que vamos a editarlo. El archivo docker-compose.override.yml lo que hace es editar el archivo docker-compose.yml, por lo que si por ejemplo queremos hacer un cambio en el servicio container2 para añadirle un volumen escribiríamos así el archivo docker-compose.override.yml

docker-compose.override.yml:

version: "3.8"
      
          services:
          container2:
              volumes:
              - ../dockerHostFolder/:/dockerOverrideFolder
      

Date cuenta que la carpeta compartida en el servicio la he llamado dockerOverrideFolder, por lo que vamos a levantar el docker compose y ver si vemos esa carpeta en el contenedor container2

	
!cd dockerComposeFiles && docker compose up -d
Copy
	
[+] Running 1/0
⠋ Container dockercomposefiles-container2-1 Recreate 0.1s
⠿ Container dockercomposefiles-container1-1 Running 0.0s
[+] Running 1/2
⠙ Container dockercomposefiles-container2-1 Recreate 0.2s
⠿ Container dockercomposefiles-container1-1 Running 0.0s
[+] Running 1/2
⠹ Container dockercomposefiles-container2-1 Recreate 0.3s
⠿ Container dockercomposefiles-container1-1 Running 0.0s
[+] Running 1/2
⠸ Container dockercomposefiles-container2-1 Recreate 0.4s
⠿ Container dockercomposefiles-container1-1 Running 0.0s
[+] Running 1/2
⠼ Container dockercomposefiles-container2-1 Recreate 0.5s
⠿ Container dockercomposefiles-container1-1 Running 0.0s
[+] Running 1/2
⠴ Container dockercomposefiles-container2-1 Recreate 0.6s
⠿ Container dockercomposefiles-container1-1 Running 0.0s
[+] Running 1/2
⠦ Container dockercomposefiles-container2-1 Recreate 0.7s
⠿ Container dockercomposefiles-container1-1 Running 0.0s
[+] Running 1/2
⠧ Container dockercomposefiles-container2-1 Recreate 0.8s
⠿ Container dockercomposefiles-container1-1 Running 0.0s
...
[+] Running 1/2
⠿ Container dockercomposefiles-container2-1 Starting 10.8s
⠿ Container dockercomposefiles-container1-1 Running 0.0s
[+] Running 2/2
⠿ Container dockercomposefiles-container2-1 Started 10.8s
⠿ Container dockercomposefiles-container1-1 Running 0.0s

Vemos que ha tardado 10 segundos en montar el servicio container2, eso es porque ha estado aplicando los cambios

$ docker compose exec container2 bash
      root@d8777a4e611a:/# ls dockerOverrideFolder/
      bindFile.txt  fileExtract.txt  text.txt
      root@d8777a4e611a:/# cat dockerOverrideFolder/text.txt 
      hola compose
      root@d8777a4e611a:/# exit
      exit
      

Bajamos el compose y borramos los contenedores y la red creada

	
!cd dockerComposeFiles && docker compose down
Copy
	
[+] Running 0/0
⠋ Container dockercomposefiles-container2-1 Stopping 0.1s
⠋ Container dockercomposefiles-container1-1 Stopping 0.1s
[+] Running 0/2
⠙ Container dockercomposefiles-container2-1 Stopping 0.2s
⠙ Container dockercomposefiles-container1-1 Stopping 0.2s
[+] Running 0/2
⠹ Container dockercomposefiles-container2-1 Stopping 0.3s
⠹ Container dockercomposefiles-container1-1 Stopping 0.3s
[+] Running 0/2
⠸ Container dockercomposefiles-container2-1 Stopping 0.4s
⠸ Container dockercomposefiles-container1-1 Stopping 0.4s
[+] Running 0/2
⠼ Container dockercomposefiles-container2-1 Stopping 0.5s
⠼ Container dockercomposefiles-container1-1 Stopping 0.5s
[+] Running 0/2
⠴ Container dockercomposefiles-container2-1 Stopping 0.6s
⠴ Container dockercomposefiles-container1-1 Stopping 0.6s
[+] Running 0/2
⠦ Container dockercomposefiles-container2-1 Stopping 0.7s
⠦ Container dockercomposefiles-container1-1 Stopping 0.7s
[+] Running 0/2
⠧ Container dockercomposefiles-container2-1 Stopping 0.8s
⠧ Container dockercomposefiles-container1-1 Stopping 0.8s
...
⠸ Container dockercomposefiles-container2-1 Stopping 10.4s
⠸ Container dockercomposefiles-container1-1 Stopping 10.4s
[+] Running 1/2
⠿ Container dockercomposefiles-container2-1 Removed 10.4s
⠿ Container dockercomposefiles-container1-1 Removing 10.5s
[+] Running 2/2
⠿ Container dockercomposefiles-container2-1 Removed 10.4s
⠿ Container dockercomposefiles-container1-1 Removed 10.5s
⠋ Network dockercomposefiles_default Removing 0.1s
[+] Running 3/3
⠿ Container dockercomposefiles-container2-1 Removed 10.4s
⠿ Container dockercomposefiles-container1-1 Removed 10.5s
⠿ Network dockercomposefiles_default Removed 0.2s

En este caso, solo con down docker compose ha parado y borrado todo, ya que, como vemos en los contenedores y en la red pone Removed

Docker compose restartlink image 48

A la hora de escribir un docker compose, podemos añadir la etiqueta restart para que si el contenedor se cae, se reinicie automáticamente

restart: always
      

De esta manera, si el contenedor se cae, se reiniciará automáticamente. Si queremos que se reinicie solo un número de veces, podemos añadirle la opción on-failure

restart: on-failure:<number>
      

Ahora el contenedor se reiniciará un número de veces, pero si se cae más veces, no se reiniciará. Si queremos que se reinicie siempre, podemos añadirle la opción unless-stopped

restart: unless-stopped
      

Ahora el contenedor se reiniciará siempre, a no ser que se pare manualmente

Docker avanzadolink image 49

Administrar ambiente de trabajolink image 50

Borrado de contenedores apagadoslink image 51

Después de estar un tiempo desarrollando, podemos tener varios contenedores apagados, pero guardados en el ordenador. Esto al final ocupa memoria, así que con docker contanier prune podemos eliminar todos los que están parados

	
!docker run ubuntu
Copy
	
!docker run ubuntu
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
effcee24f54a ubuntu "bash" 37 seconds ago Exited (0) 36 seconds ago musing_rosalind
$ docker container prune 
      WARNING! This will remove all stopped containers.
      Are you sure you want to continue? [y/N] y
      Deleted Containers:
      effcee24f54aab22e34fdea2465b3b7af132d8c627e5432ba3e915a370876977
      
      Total reclaimed space: 0B
      

En este caso hemos ahorrado 0 bytes, pero en el caso de dejar contenedores apagados después de mucho desarrollo, seguro que el ahorro de memoria será mayor

Borrado de todos los contenedoreslink image 52

En caso de tener contenedores corriendo, podemos matar todos los contenedores mediante otro comando

El comando docker ps -q nos devuelve la id de todos los contenedores, por lo que con el comando docker rm -f $(docker ps -aq) pararemos y borraremos todos

	
!docker run -d ubuntu tail -f /dev/null
Copy
	
c22516186ef7e3561fb1ad0d508a914857dbc61274a218f297c4d80b1fc33863
	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c22516186ef7 ubuntu "tail -f /dev/null" About a minute ago Up About a minute agitated_knuth
	
!docker rm -f $(docker ps -aq)
Copy
	
c22516186ef7
	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

Borrado de todolink image 53

Como hemos visto docker también crea redes, imágenes, volúmenes, etc, así que con el comando docker system prune podemos borrar todos los contenedores parados, todas las redes que no estén usadas por al menos un contenedor, las imágenes repetidas, y lo que haya repetido en la caché de compilación

$ docker system prune 
      WARNING! This will remove:
        - all stopped containers
        - all networks not used by at least one container
        - all dangling images
        - all dangling build cache
      
      Are you sure you want to continue? [y/N] y
      Total reclaimed space: 0B
      

Al igual que antes, no se ha ahorrado mucho espacio, pero después de mucho tiempo desarrollando, el ahorro será considerable

Uso de recursos del host por parte de contenedoreslink image 54

Por ejemplo a la hora de crear un contenedor, podemos limitar la RAM del host que puede usar mediante la opción --memory

	
!docker run -d --memory 1g ubuntu tail -f /dev/null
Copy
	
d84888eafe531831ef8915d2270422365adec02678122bf59580e2da782e6972

Pero con docker ps no tenemos acceso a los recursos que está consumiendo el contenedor

	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d84888eafe53 ubuntu "tail -f /dev/null" 35 seconds ago Up 34 seconds musing_ritchie

Para ello tenemos el comando docker stats

$ docker stats
      CONTAINER ID   NAME             CPU %     MEM USAGE / LIMIT   MEM %     NET I/O       BLOCK I/O   PIDS
      d84888eafe53   musing_ritchie   0.00%     540KiB / 1GiB       0.05%     5.62kB / 0B   0B / 0B     1
      

Esto es muy util si queremos simular un entorno con un límite de RAM

Deteniendo contenedores correctamente: SHELL vs EXEClink image 55

Como hemos explicado, cuando asignamos un proceso a un contenedor, cuando ese proceso termina, el contenedor se para, pero a veces podemos encontrarnos con problemas con esto. Vamos a crear una nueva carpeta llamada Dockerfile_loop

	
!mkdir Dockerfile_loop
Copy

Ahora vamos a crear un archivo llamado loop.sh dentro de Dockerfile_loop

	
!mkdir Dockerfile_loop
!touch Dockerfile_loop/loop.sh
Copy

Y vamos a escribir lo siguiente dentro de loop.sh

#!/usr/bin/env bash
      trap "exit 0" SIGTERM
      while true; do :; done
      

Si yo ejecuto este script en el host se ejcuta hasta que introduzca CTRL+C

./loop
       ^C
      

Ahora vamos a crear un archivo Dockerfile dentro de Dockerfile_loop

	
!mkdir Dockerfile_loop
!touch Dockerfile_loop/loop.sh
!touch Dockerfile_loop/Dockerfile
Copy

Dockerfile:

FROM ubuntu:trusty
      COPY ["loop.sh", "/"]
      CMD /loop.sh
      

Vamos a crear una imagen basada en ubuntu que copia el script dentro y lo ejecuta, y el script se ejecuta hasta que recibe la señal SIGTERM del sistema operativo. Compilamos la imagen

	
!mkdir Dockerfile_loop
!touch Dockerfile_loop/loop.sh
!touch Dockerfile_loop/Dockerfile
!docker build -t ubuntu:loop ./Dockerfile_loop
Copy
	
Sending build context to Docker daemon 3.072kB
Step 1/3 : FROM ubuntu:trusty
---> 13b66b487594
Step 2/3 : COPY ["loop.sh", "/"]
---> 89f2bbd25a88
Step 3/3 : CMD /loop.sh
---> Running in ff52569c35fd
Removing intermediate container ff52569c35fd
---> feb091e4efa3
Successfully built feb091e4efa3
Successfully tagged ubuntu:loop
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them

Ejecutamos el contenedor

docker run -d --name looper ubuntu:loop bash
      
	
!docker run -d --name looper ubuntu:loop
Copy
	
8a28f8cc9892213c4e0603dfdde320edf52c091b82c60510083549a391cd6645

Comprobamos y vemos que el contenedor está corriendo

	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8a28f8cc9892 ubuntu:loop "/bin/sh -c /loop.sh" 4 seconds ago Up 3 seconds looper

Intentamos parar el contenedor con docker stop looper. Docker stop trata de parar el contenedor enviándole la señal SIGTERM.

	
%%time
!docker stop looper
Copy
	
looper
CPU times: user 89.2 ms, sys: 21.7 ms, total: 111 ms
Wall time: 10.6 s

Esto ha tardado unos 10 segundos en detenerse, cuando tendría que ser inmediato. Esto es porque stop ha mandado la orden SIGTERM para que se parara el contenedor, pero como no paraba, al rato le ha mandado un SIGKILL para forzar que se detenga. Vemos aver que pasa, si listamos los contenedores

	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8a28f8cc9892 ubuntu:loop "/bin/sh -c /loop.sh" 23 seconds ago Exited (137) 2 seconds ago looper

Podemos ver que la señal de Exited es 137, eso quivale a SIGKILL, es decir, docker tuvo que forzar el apagado.

Vamos a borrar el contenedor y a volverlo a correr

	
!docker rm looper
Copy
	
looper
	
!docker run -d --name looper ubuntu:loop
Copy
	
84bc37f944d270be5f84a952968db2b8cf5372c61146d29383468198ceed18fd

Si ahora intentamos parar el contenedor con docker kill looper

	
%%time
!docker kill looper
Copy
	
looper
CPU times: user 9.1 ms, sys: 857 µs, total: 9.96 ms
Wall time: 545 ms

Vemos que el tiempo son unos 500 ms, es decir, docker lo ha parado en un momento enviándole la orde SIGKILL. Porque kill no manda SIGTERM y si en un tiempo no se ha parado el contenedor manda SIGKILL, lo que hace es mandar SIGKILL desde el inicio.

Si vemos los contenedores, vemos que la señal de salida es la misma, 137

	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
84bc37f944d2 ubuntu:loop "/bin/sh -c /loop.sh" 6 seconds ago Exited (137) 2 seconds ago looper

Esta no es la manera correcta de apagar un contenedor, porque cuando queramos apagar el contenedor habría que hacerlo mediante la señal SIGTERM, para que este termine de procesar lo que estuviese haciendo y luego se apague

Si borramos el contenedor y lo volvemos a correr

	
!docker rm looper
Copy
	
looper
	
!docker run -d --name looper ubuntu:loop
Copy
	
b9d9f370cc0de7569eb09d0a85cd67e8ea6babc0754a517ccba5c5057f5cc50e

Si ahora vemos los procesos que se están ejecutando dentro del contenedor

	
!docker exec looper ps -ef
Copy
	
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 14:05 ? 00:00:00 /bin/sh -c /loop.sh
root 7 1 93 14:05 ? 00:00:02 bash /loop.sh
root 8 0 0 14:05 ? 00:00:00 ps -ef

En realidad el proceso principal, el 1, no es /loop.sh sino que es /bin/sh -c /loop.sh, es decir, es un proceso hijo del shell. Por lo que cuando llegaba la señal SIGTERM le llegaba a shell, pero este no se lo manda a sus procesos hijos, por eso no le llegaba a loop.sh

Para que no pase esto hay que cambiar el Dockerfile a lo siguiente

Dockerfile:

FROM ubuntu:trusty
      COPY ["loop.sh", "/"]
      CMD ["/loop.sh"]    # antes era CMD /loop.sh
      

Esta forma se llama exec form, mientras que la anterior se llama shell form, de manera que de la anterior forma corre el proceso como un hijo del shell, mientras que de la forma exec form ejecuta el proceso que le digamos. Así que borramos el contenedor, volvemos a compilar y volvemos a hacer correr el contenedor con la imagen

	
!docker rm -f looper
Copy
	
looper
	
!docker build -t ubuntu:loop ./Dockerfile_loop
Copy
	
Sending build context to Docker daemon 3.072kB
Step 1/3 : FROM ubuntu:trusty
---> 13b66b487594
Step 2/3 : COPY ["loop.sh", "/"]
---> Using cache
---> 89f2bbd25a88
Step 3/3 : CMD ["/loop.sh"]
---> Running in 6b8d92fcd57c
Removing intermediate container 6b8d92fcd57c
---> 35a7bb2b1892
Successfully built 35a7bb2b1892
Successfully tagged ubuntu:loop
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
	
!docker run -d --name looper ubuntu:loop
Copy
	
850ae70c071426850b28428ac60dcbf875c6d35d9b7cc66c17cf391a23392965

Si ahora veo los procesos dentro del contenedor

	
!docker exec looper ps -ef
Copy
	
UID PID PPID C STIME TTY TIME CMD
root 1 0 88 14:14 ? 00:00:02 bash /loop.sh
root 7 0 0 14:14 ? 00:00:00 ps -ef

Ahora sí veo que el proceso principal, el 1, es /loop.sh

Si ahora pruebo a parar el contenedor

	
%%time
!docker stop looper
Copy
	
looper
CPU times: user 989 µs, sys: 7.55 ms, total: 8.54 ms
Wall time: 529 ms

Vemos que tarda ms. Vémos el código con el que paró

	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
850ae70c0714 ubuntu:loop "/loop.sh" About a minute ago Exited (0) 33 seconds ago looper

Contenedores ejecutableslink image 56

Si queremos un binario que corra como un ejecutable, en el dockerfile hay que especificar el comando en ENTRYPOINT y los parámetros del comando en CMD, vamos a verlo

Vamos a crear una nueva carpeta donde guardaremos el Dockerfile

	
!mkdir dockerfile_ping
Copy

Ahora creamos un Dockerfile dentro

	
!mkdir dockerfile_ping
!touch dockerfile_ping/Dockerfile
Copy

Escribimos dentro del Dockerfile lo siguiente

FROM ubuntu:trusty
      ENTRYPOINT [ "/bin/ping", "-c", "3" ]
      CMD [ "localhost" ]
      

Compilamos la imagen

	
!mkdir dockerfile_ping
!touch dockerfile_ping/Dockerfile
!docker build -t ubuntu:ping ./dockerfile_ping
Copy
	
Sending build context to Docker daemon 3.072kB
Step 1/3 : FROM ubuntu:trusty
---> 13b66b487594
Step 2/3 : ENTRYPOINT [ "/bin/ping", "-c", "3" ]
---> Using cache
---> 1cebcfb542b1
Step 3/3 : CMD [ "localhost" ]
---> Using cache
---> 04ddc3de52a2
Successfully built 04ddc3de52a2
Successfully tagged ubuntu:ping
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them

Si ahora corremos la imagen sin pasarle un parámetro, el contenedor se hará un ping a sí mismo

	
!docker run --name ping_localhost ubuntu:ping
Copy
	
PING localhost (127.0.0.1) 56(84) bytes of data.
64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.041 ms
64 bytes from localhost (127.0.0.1): icmp_seq=2 ttl=64 time=0.058 ms
64 bytes from localhost (127.0.0.1): icmp_seq=3 ttl=64 time=0.054 ms
--- localhost ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2027ms
rtt min/avg/max/mdev = 0.041/0.051/0.058/0.007 ms

Pero si ahora le pasamos un parámetro, hará ping a la dirección que le digamos

	
!docker run --name ping_google ubuntu:ping google.com
Copy
	
PING google.com (216.58.209.78) 56(84) bytes of data.
64 bytes from waw02s06-in-f14.1e100.net (216.58.209.78): icmp_seq=1 ttl=111 time=3.93 ms
64 bytes from waw02s06-in-f14.1e100.net (216.58.209.78): icmp_seq=2 ttl=111 time=6.80 ms
64 bytes from waw02s06-in-f14.1e100.net (216.58.209.78): icmp_seq=3 ttl=111 time=6.92 ms
--- google.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2002ms
rtt min/avg/max/mdev = 3.930/5.886/6.920/1.383 ms

Borramos los contenedores

	
!docker rm ping_localhost ping_google
Copy
	
ping_localhost
ping_google

El contexto de buildlink image 57

Vamos a crear una carpeta llamada dockerfile_contexto

	
!mkdir dokerfile_contexto
Copy

Ahora creamos en ella dos archivos, un test.txt y el Dockerfile

	
!mkdir dokerfile_contexto
!touch dokerfile_contexto/Dockerfile dokerfile_contexto/text.txt
Copy

Modificamos el Dockerfile y ponemos lo siguiente

FROM ubuntu:trusty
      COPY [".", "/"]
      

Esto lo que va a hacer es que va a copiar dentro de la imagen, todo lo que tenga en la carpeta en la que se encuentra el Dockerfile. Compilamos la imagen

	
!mkdir dokerfile_contexto
!touch dokerfile_contexto/Dockerfile dokerfile_contexto/text.txt
!docker build -t ubuntu:contexto ./dokerfile_contexto
Copy
	
Sending build context to Docker daemon 2.56kB
Step 1/2 : FROM ubuntu:trusty
---> 13b66b487594
Step 2/2 : COPY [".", "/"]
---> 3ab79fdce389
Successfully built 3ab79fdce389
Successfully tagged ubuntu:contexto
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them

Vamos a ver qué hay dentro del contenedor

	
!docker run --name ls ubuntu:contexto ls
Copy
	
Dockerfile
bin
boot
dev
etc
home
lib
lib64
media
mnt
opt
proc
root
run
sbin
srv
sys
text.txt
tmp
usr
var

Como vemos está el archivo text.txt. Pero al puede que dentro de la carpeta que está en el mismo directorio que el Dockerfile haya archivos o carpetas que no queremos que se copien en la imagen, por la razón que sea, así que al igual que en git tenemos el .gitignore, en docker tenemos el .dockerignore, donde metemos los archivos o carpetas que no queremos que a la hora de compilar se tengan en cuenta

Así que creamos un archivo .dockerignore

	
!touch dokerfile_contexto/.dockerignore
Copy

Y dentro añadimos el text.txt, y de paso el Dockerfile que no lo necesitamos dentro de la imagen

.dockerignore:

Dockerfile
      text.txt

Borramos el contenedor que habíamos creado, volvemos a compilar y vemos qué hay dentro del contenedor

	
!touch dokerfile_contexto/.dockerignore
!docker rm ls
Copy
	
ls
	
!docker build -t ubuntu:contexto ./dokerfile_contexto
Copy
	
Sending build context to Docker daemon 3.072kB
Step 1/2 : FROM ubuntu:trusty
---> 13b66b487594
Step 2/2 : COPY [".", "/"]
---> 7a6689546da4
Successfully built 7a6689546da4
Successfully tagged ubuntu:contexto
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
	
!docker run --name ls ubuntu:contexto ls
Copy
	
bin
boot
dev
etc
home
lib
lib64
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var

Vemos que ahora no están ni Dockerfile, ni text.txt. Borramos el contenedor

	
!docker rm ls
Copy
	
ls

Multi-stage buildlink image 58

Al final de un desarrollo no queremos que todo el código esté en la imagen que se va a mandar a producción.

Podemos dividir el dockerfile en dos, por ejemplo, el developer.Dockerfile y el production.Dockerfile, donde en desarrollo habrá más cosas que en el de producción. A la hora de compilarlos, mediante la opción -f elegimos el dockerfile que queremos usar

docker build -t <tag> -f developer.Dockerfile
      docker build -t <tag> -f production.Dockerfile
      

Pero para no tener que crear dos archivos Dockerfile, docker creó el multi stage buils. Con un solo Dockerfile vamos a solucionar el problema

Creamos la carpeta donde vamos a guardar el Dockerfile

	
!mkdir docker_multi_stage
Copy

Y dentro creamos el archivo Dockerfile

	
!mkdir docker_multi_stage
!cd docker_multi_stage && touch Dockerfile
Copy

Editamos el archivo metiendo lo siguiente

# Etapa 1: Generar el ejecutable con Python basado en Alpine
      FROM python:3.9-alpine as build-stage
      WORKDIR /app
      # Instalar dependencias para PyInstaller
      RUN apk add --no-cache gcc musl-dev libc-dev
      # Generar hello.py
      RUN echo 'print("Hello from Alpine!")' > hello.py
      # Instalar PyInstaller
      RUN pip install pyinstaller
      # Usar PyInstaller para crear un ejecutable independiente
      RUN pyinstaller --onefile hello.py
      
      # Etapa 2: Ejecutar el ejecutable en una imagen de Alpine
      FROM alpine:latest
      WORKDIR /app
      # Copiar el ejecutable desde la etapa de build
      COPY --from=build-stage /app/dist/hello .
      # Comando por defecto para ejecutar el ejecutable
      CMD ["./hello"]
      

Como se puede ver el Dockerfile está dividido en dos, por un lado se trabaja sobre la imagen python:3.9-alpine que se llama como build-stage. Y por otro lado trabajamos sobre la imagen alpine:latest, que es una imagen de linux muy ligera y se utiliza mucho en producción

Lo compilamos

!docker build -t maximofn/multistagebuild:latest ./docker_multi_stage
      
[+] Building 0.0s (0/2)                                          docker:default
      
[+] Building 0.2s (4/6)                                          docker:default
       => [internal] load build definition from Dockerfile                       0.0s
       => => transferring dockerfile: 722B                                       0.0s
       => [internal] load .dockerignore                                          0.0s
       => => transferring context: 2B                                            0.0s
       => [internal] load metadata for docker.io/library/alpine:latest           0.1s
       => [internal] load metadata for docker.io/library/python:3.9-alpine       0.1s
      ...
       => CACHED [stage-1 3/3] COPY --from=build-stage /app/dist/hello .         0.0s
       => exporting to image                                                     0.0s
       => => exporting layers                                                    0.0s
       => => writing image sha256:7fb090d1495d00e892118b6bc3c03400b63a435fd4703  0.0s
       => => naming to docker.io/maximofn/multistagebuild:latest                 0.0s
      

Si ahora hacemos vemos las imágenes que tenemos

	
!mkdir docker_multi_stage
!cd docker_multi_stage && touch Dockerfile
!docker build -t maximofn/multistagebuild:latest ./docker_multi_stage
!docker image ls
Copy
	
[+] Building 0.0s (0/2) docker:default
REPOSITORY TAG IMAGE ID CREATED SIZE
maximofn/multistagebuild latest 7fb090d1495d 8 minutes ago 13.6MB

Podemos ver la imagen que acabamos de crear y que solo pesa 13.6 MB. Vamos a bajarnos la imagen de Python para ver cuanto pesa

	
!docker pull python:3.9-alpine
Copy
	
3.9-alpine: Pulling from library/python
a8db6415: Already exists
d5e70e42: Already exists
3fe96417: Already exists
aa4dddbb: Already exists
518be9f7: Already exists Digest: sha256:6e508b43604ff9a81907ec17405c9ad5c13664e45a5affa2206af128818c7486
Status: Downloaded newer image for python:3.9-alpine
docker.io/library/python:3.9-alpine
	
!docker image ls
Copy
	
REPOSITORY TAG IMAGE ID CREATED SIZE
maximofn/multistagebuild latest 7fb090d1495d 9 minutes ago 13.6MB
python 3.9-alpine 6946662f018b 9 days ago 47.8MB

Podemos ver que mientras nuestra imagen pesa solo 13.6 MB, la de python con la que ha construido la aplicación pesa 47.8 MB. Por lo que podemos sacar dos conclusiones, con la primera imagen, la de python, ha construido la aplicación, ha generado el ejecutable y ese ejecutable es el que usamos en la segunda imagen, la de alpine. Además podemos ver que aunque la primera imagen que usa es la de python no se descarga en nuestro sistema, ya que la hemos tenido que descargar nosotros

Pues ya solo queda probarlo

	
!docker run --rm --name multi_stage_build maximofn/multistagebuild
Copy
	
Hello from Alpine!

Funciona!

Multi arch buildslink image 59

Supongamos que queremos hacer una imagen que queremos que se pueda ejecutar en un ordenador y en una raspberry, el ordenador probablemente tenga un micro con arquitectura AMD64, mientras que la raspberry tiene un micro con arquitectura ARM. Por lo que no podemos crear la misma imagen para los dos, es decir, cuando creamos una imagen la creamos con un Dockerfile que suele empezar así

FROM ...
      

Por lo que el Dockerfile de la imagen del ordenador podría empezar así

FROM ubuntu:latest
      

Mientras que el de la raspberry podría empezar así

FROM arm64v8/ubuntu:latest
      

Tendríamos que crear dos archivos Dockerfile compilarlos y en el ordenador usar una imagen y en la raspebrry usar otra

Para evitar tener que ver la arquitectura del ordenador y ver qué imagen tenemos que usar Docker creo los manifest, que como su nombre indica es un manifiesto que indica en función de qué arquitectura de micro tengamos usa una imagen u otra

Así que vamos a ver cómo hacer esto

En primer lugar creamos una carpeta donde vamos a crear nuestros archivos Dockerfile

	
!mkdir docker_multi_arch
Copy

Ahora creamos los dos dockerfiles

	
!mkdir docker_multi_arch
!cd docker_multi_arch && touch Dockerfile_arm64 Dockerfile_amd64
Copy

Escribimos del Dockerfile para AMD64

	
!mkdir docker_multi_arch
!cd docker_multi_arch && touch Dockerfile_arm64 Dockerfile_amd64
!cd docker_multi_arch && echo "FROM ubuntu:20.04" >> Dockerfile_amd64 && echo "CMD echo 'Hello from amd64'" >> Dockerfile_amd64
Copy
	
!mkdir docker_multi_arch
!cd docker_multi_arch && touch Dockerfile_arm64 Dockerfile_amd64
!cd docker_multi_arch && echo "FROM ubuntu:20.04" >> Dockerfile_amd64 && echo "CMD echo 'Hello from amd64'" >> Dockerfile_amd64
!cd docker_multi_arch && echo "FROM arm64v8/ubuntu:latest" >> Dockerfile_arm && echo "CMD echo 'Hello from ARM'" >> Dockerfile_arm
Copy

Ahora compilamos las dos imágenes

	
!mkdir docker_multi_arch
!cd docker_multi_arch && touch Dockerfile_arm64 Dockerfile_amd64
!cd docker_multi_arch && echo "FROM ubuntu:20.04" >> Dockerfile_amd64 && echo "CMD echo 'Hello from amd64'" >> Dockerfile_amd64
!cd docker_multi_arch && echo "FROM arm64v8/ubuntu:latest" >> Dockerfile_arm && echo "CMD echo 'Hello from ARM'" >> Dockerfile_arm
!cd docker_multi_arch && docker build -t maximofn/multiarch:arm -f Dockerfile_arm .
Copy
	
[+] Building 0.0s (0/1) docker:default
[+] Building 0.2s (2/3) docker:default
=> [internal] load build definition from Dockerfile_amd64 0.1s
=> => transferring dockerfile: 89B 0.0s
=> [internal] load .dockerignore 0.1s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/ubuntu:20.04 0.1s
[+] Building 0.3s (2/3) docker:default
=> [internal] load build definition from Dockerfile_amd64 0.1s
=> => transferring dockerfile: 89B 0.0s
=> [internal] load .dockerignore 0.1s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/ubuntu:20.04 0.2s
[+] Building 0.5s (2/3) docker:default
=> [internal] load build definition from Dockerfile_amd64 0.1s
=> => transferring dockerfile: 89B 0.0s
=> [internal] load .dockerignore 0.1s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/ubuntu:20.04 0.4s
[+] Building 0.6s (2/3) docker:default
=> [internal] load build definition from Dockerfile_amd64 0.1s
=> => transferring dockerfile: 89B 0.0s
=> [internal] load .dockerignore 0.1s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/ubuntu:20.04 0.5s
...
=> => transferring context: 2B 0.0s
=> [internal] load build definition from Dockerfile_arm 0.0s
=> => transferring dockerfile: 94B 0.0s
=> [internal] load metadata for docker.io/arm64v8/ubuntu:latest 1.8s
=> [auth] arm64v8/ubuntu:pull token for registry-1.docker.io 0.0s
=> CACHED [1/1] FROM docker.io/arm64v8/ubuntu:latest@sha256:94d12db896d0 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:a9732c1988756dc8e836fd96e5c9512e349c97ea5af46 0.0s
=> => naming to docker.io/maximofn/multiarch:arm 0.0s

Vamos a ver que tenemos las ods imágenes compiladas

	
!docker image ls
Copy
	
REPOSITORY TAG IMAGE ID CREATED SIZE
maximofn/multiarch arm a9732c198875 4 weeks ago 69.2MB
maximofn/multiarch amd64 5b612c83025f 6 weeks ago 72.8MB

Vemos que hemos compilado las dos imágenes. Para poder crear un manifest, primero tenemos que subir las imágenes a docker hub, así que las subimos

	
!docker push maximofn/multiarch:amd64
Copy
	
The push refers to repository [docker.io/maximofn/multiarch]
82bdeb5f: Mounted from library/ubuntu amd64: digest: sha256:30e820f2a11a24ad4d8fb624ae485f7c1bcc299e8cfc72c88adce1acd0447e1d size: 529
!docker push maximofn/multiarch:arm
      
The push refers to repository [docker.io/maximofn/multiarch]
      
      eda53374: Layer already exists arm: digest: sha256:6ec5a0752d49d3805061314147761bf25b5ff7430ce143adf34b70d4eda15fb8 size: 529
      

Si me voy a mi docker hub puedo ver que mi imagen maximofn/multiarch tiene los tags amd64 y arm

docker_multi_arch_tags

Ahora vamos a crear el manifest basado en estas dos imágenes

	
!docker push maximofn/multiarch:arm
!docker manifest create maximofn/multiarch:latest maximofn/multiarch:amd64 maximofn/multiarch:arm
Copy
	
The push refers to repository [docker.io/maximofn/multiarch]
Created manifest list docker.io/maximofn/multiarch:latest

Una vez creado, tenemos que indicar las arquitecturas de las CPUs a las que corresponde cada uno

	
!docker manifest annotate maximofn/multiarch:latest maximofn/multiarch:amd64 --os linux --arch amd64
Copy
	
!docker manifest annotate maximofn/multiarch:latest maximofn/multiarch:amd64 --os linux --arch amd64
!docker manifest annotate maximofn/multiarch:latest maximofn/multiarch:arm64 --os linux --arch arm64
Copy
	
manifest for image maximofn/multiarch:arm64 does not exist in maximofn/multiarch:latest

Una vez creado y anotado podemos subir el manifest a docker hub

	
!docker manifest push maximofn/multiarch:latest
Copy
	
sha256:1ea28e9a04867fe0e0d8b0efa455ce8e4e29e7d9fd4531412b75dbd0325e9304

Si ahora vuelvo a mirar los tags que tiene mi imagen maximofn/multiarch veo también la de latest

docker_multi_arch_tags_manifest

Ahora, tanto si quiero usar mi imagen desde una máquina con CPU AMD64 o CPU ARM con hacer FROM maximofn/multiarch:latest, docker comprobará la arquitectura de la CPU y bajará el tag amd64 o el tag arm. Vamos a verlo, si desde mi ordenador ejecuto la imagen obtengo

!docker run maximofn/multiarch:latest
      
Unable to find image 'maximofn/multiarch:latest' locally
      
latest: Pulling from maximofn/multiarch
      Digest: sha256:7cef0de10f7fa2b3b0dca0fbf398d1f48af17a0bbc5b9beca701d7c427c9fd84
      Status: Downloaded newer image for maximofn/multiarch:latest
      Hello from amd64
      

Como no la tiene se la baja y luego aparece el texto Hello from amd64, ya que la CPU de mi ordenador tiene una arquitectura AMD64

Si ahora me conecto por ssh a una raspberry pi y pruebo lo mismo obtengo

raspiberry@raspberrypi:~ $ docker run maximofn/multiarch:latest
          Unable to find image 'maximofn/multiarch:latest' locally
          latest: Pulling from maximofn/multiarch
          Digest: sha256:1ea28e9a04867fe0e0d8b0efa455ce8e4e29e7d9fd4531412b75dbd0325e9304
          Status: Downloaded newer image for maximofn/multiarch:latest
          Hello from ARM
      

Aparece Hello from ARM ya que la raspberry tiene un micro con arquitectura ARM

Como se puede ver cada máquina se ha bajado la imagen que necesitaba

Escritura correcta de Dockerfiles avanzadolink image 60

Ya vimos la manera de escribir correctamente dockerfiles, pero hay una cosa más que podemos hacer ahora que conocemos el multi-stage build y es crear un contenedor para crear el ejecutable y otro más pequeño para ejecutarlo

Llegamos a la conclusión de que un buen dockerfile podía ser este

FROM python:3.9.18-alpine
      WORKDIR /sourceCode/sourceApp
      COPY ./sourceCode/sourceApp .
      CMD ["python3", "app.py"]
      

Vamos a crear ahora un ejecutable en un contenedor builder y en otro más pequeño lo ejecutamos

FROM python:3.9.18-alpine as builder
      WORKDIR /sourceCode/sourceApp
      RUN apk add --no-cache gcc musl-dev libc-dev && pip install pyinstaller
      COPY ./sourceCode/sourceApp .
      RUN pyinstaller --onefile app.py
      
      FROM alpine:3.18.3
      WORKDIR /sourceCode/sourceApp
      COPY --from=builder /sourceCode/sourceApp/dist/app .
      CMD ["./app"]
      

Creamos el código de python en la ruta necesaria

	
!docker run maximofn/multiarch:latest
!mkdir multistagebuild/sourceCode
!mkdir multistagebuild/sourceCode/sourceApp
!touch multistagebuild/sourceCode/sourceApp/app.py
!echo 'print("Hello from Alpine!")' > multistagebuild/sourceCode/sourceApp/app.py
Copy

Ahora compilamos la imagen

	
!docker run maximofn/multiarch:latest
!mkdir multistagebuild/sourceCode
!mkdir multistagebuild/sourceCode/sourceApp
!touch multistagebuild/sourceCode/sourceApp/app.py
!echo 'print("Hello from Alpine!")' > multistagebuild/sourceCode/sourceApp/app.py
!docker build -t maximofn/multistagebuild:alpine-3.18.3 ./multistagebuild
Copy
	
Unable to find image 'maximofn/multiarch:latest' locally
[+] Building 0.0s (0/0) docker:default
[+] Building 0.0s (0/1) docker:default
[+] Building 0.2s (3/5) docker:default
=> [internal] load build definition from Dockerfile 0.1s
=> => transferring dockerfile: 357B 0.0s
=> [internal] load .dockerignore 0.1s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/alpine:3.18.3 0.1s
=> [internal] load metadata for docker.io/library/python:3.9.18-alpine 0.1s
=> [auth] library/alpine:pull token for registry-1.docker.io 0.0s
[+] Building 0.3s (3/5) docker:default
=> [internal] load build definition from Dockerfile 0.1s
=> => transferring dockerfile: 357B 0.0s
=> [internal] load .dockerignore 0.1s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/alpine:3.18.3 0.2s
=> [internal] load metadata for docker.io/library/python:3.9.18-alpine 0.2s
=> [auth] library/alpine:pull token for registry-1.docker.io 0.0s
[+] Building 0.5s (4/6) docker:default
=> [internal] load build definition from Dockerfile 0.1s
=> => transferring dockerfile: 357B 0.0s
=> [internal] load .dockerignore 0.1s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/alpine:3.18.3 0.4s
...
=> exporting to image 0.1s
=> => exporting layers 0.1s
=> => writing image sha256:8a22819145c6fee17e138e818610ccf46d7e13c786825 0.0s
=> => naming to docker.io/maximofn/multistagebuild:alpine-3.18.3 0.0s

La ejecutamos

	
!docker run --rm --name multi_stage_build maximofn/multistagebuild:alpine-3.18.3
Copy
	
Hello from Alpine!

La imagen maximofn/multistagebuild:alpine-3.18.3 solo pesa 13.6 MB

Diferencia entre RUN, CMD y ENTRYPOINTlink image 61

RUNlink image 62

El comando RUN es el más sencillo, simplemente ejecuta un comando en el momento de la compilación de la imagen. Por ejemplo, si queremos instalar un paquete en la imagen, lo hacemos mediante RUN.

Por tanto, importante, RUN se ejecuta en el momento de la compilación de la imagen, no cuando se ejecuta el contenedor

CMDlink image 63

El comando CMD es el comando que se ejecuta cuando se ejecuta el contenedor. Por ejemplo, si queremos que el contenedor ejecute un comando cuando se ejecute, lo hacemos mediante CMD. Por ejemplo, si tenemos una aplicación de python en un contenedor, con CMD podemos indicarle que cuando se ejecute el contenedor ejecute la aplicación de python.

De esta manera, cuando se levante el contenedor, se ejecutará la aplicación de python. Es decir si hacemos docker run <image> se ejecutará la aplicación de python. Pero CMD nos permite sobreescribir el comando que se ejecuta cuando se levanta el contenedor, por ejemplo, si hacemos docker run <image> bash se ejecutará bash en vez de la aplicación de python

ENTRYPOINTlink image 64

El comando ENTRYPOINT es similar al comando CMD, pero con una diferencia, y es que ENTRYPOINT no está pensado para sobreescribirse. Es decir, si tenemos una aplicación de python en un contenedor, con ENTRYPOINT podemos indicarle que cuando se ejecute el contenedor ejecute la aplicación de python. Pero si hacemos docker run <image> bash se ejecutará la aplicación de python, no bash.

Un uso muy común de ENTRYPOINT es cuando queremos que el contenedor sea un ejecutable, por ejemplo, si queremos que el contenedor sea un ejecutable de una versión de python que no tenemos en nuestro host, porque por ejemplo queremos probar la nueva versión de python que ha salido podemos hacer

FROM python:3.9.18-alpine
      ENTRYPOINT ["python3"]
      

De esta manera, cuando se levante el contenedor, se ejecutará python. Es decir si hacemos docker run <image> se ejecutará python. Pero ENTRYPOINT nos permite sobreescribir el comando que se ejecuta cuando se levanta el contenedor, por ejemplo, si hacemos docker run <image> myapp.py se ejecutará python3 myapp.py dentro del contenedor. Así podemos probar nuestra aplicación de python en la nueva versión de python

Cambios en un contenedorlink image 65

Con docker diff podemos ver las diferencias que hay entre el contenedor y la imagen, lo que es lo mismo la diferencia en el contenedor de cuando se creó y ahora

Vamos a correr un contenedor y dentro creamos un archivo

	
!docker run --rm -it --name ubuntu-20.04 ubuntu:20.04 bash
Copy
	
root@895a19aef124:/# touch file.txt

Ahora podemos ver la diferencia

	
!docker diff ubuntu-20.04
Copy
	
C /root
A /root/.bash_history
A /file.txt

A significa que se ha añadido, C significa que se ha cambiado y D significa que se ha borrado

Docker en dockerlink image 66

Supongamos que tenemos contenedores que necesitan levantar o apagar otros contenedores. Esto se logra de la siguiente manera

Dado que el linux todo es un archivo y el host se comunica con docker mediante un socket. Por lo que para linux, ese socket es un archivo. Así que un contenedor le montamos ese socket como un archivo podrá hablar con docker

Primero vamos a montar un contenedor con ubuntu

	
!docker run -d --name ubuntu ubuntu:latest tail -f /dev/null
Copy
	
144091e4a3325c9068064ff438f8865b40f944af5ce649c7156ca55a3453e423

Vamos a montar el contenedor que va a poder hablar con docker montando la carpeta /var/run/docker.sock

$ docker run -it --rm --name main -v /var/run/docker.sock:/var/run/docker.sock docker:19.03.12
      / #
      

Nos hemos metido dentro de un contenedor, y si dentro ejecutamos docker ps

# docker ps
      CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
      9afb778d6c20        docker:19.03.12     "docker-entrypoint.s…"   3 seconds ago       Up 2 seconds                            main
      144091e4a332        ubuntu:latest       "tail -f /dev/null"      19 seconds ago      Up 18 seconds                           ubuntu
      

Como podemos ver, dentro del docker podemos ver los contenedores del host

Podemos correr un nuevo contenedor

# docker run -d --name ubuntu_from_main ubuntu:latest tail -f /dev/null
      362654a72bb0fb047c13968707a6f16b87fed7ce051eb5c1a146b15828589a1a
      / #
      

Y si volvemos a ver los contenedores

# docker ps
      CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS               NAMES
      362654a72bb0        ubuntu:latest       "tail -f /dev/null"      3 seconds ago        Up 3 seconds                            ubuntu_from_main
      9afb778d6c20        docker:19.03.12     "docker-entrypoint.s…"   About a minute ago   Up About a minute                       main
      144091e4a332        ubuntu:latest       "tail -f /dev/null"      2 minutes ago        Up About a minute                       ubuntu
      

Pero si ahora ejecutamos una nueva terminal del host veremos el contenedor creado desde dentro del contenedor

	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
362654a72bb0 ubuntu:latest "tail -f /dev/null" About a minute ago Up About a minute ubuntu_from_main
9afb778d6c20 docker:19.03.12 "docker-entrypoint.s…" 3 minutes ago Up 3 minutes main
144091e4a332 ubuntu:latest "tail -f /dev/null" 3 minutes ago Up 3 minutes ubuntu

Todo lo que hagamos desde el contenedor main se verá reflejado en el host

Esto tiene la ventaja de que podemos instalar programas en un contenedor que tiene acceso al host para no tener que instalarlos en el host. Por ejemplo dive es una herramienta para explorar contenedores, pero si no la quieres instalar en el host la puedes instalar en un contenedor con acceso al host, así desde ese contenedro main puedes explorar el resto de contenedores sin tener que instalarla en el host

Seguir leyendo

DoLa – Decoding by Contrasting Layers Improves Factuality in Large Language Models

DoLa – Decoding by Contrasting Layers Improves Factuality in Large Language Models

¿Alguna vez has hablado con un LLM y te ha respondido algo que suena como si hubiera estado bebiendo café de máquina durante toda la noche? 😂 ¡Eso es lo que llamamos una alucinación en el mundo de los LLMs! Pero no te preocupes, porque no es que tu modelo de lenguaje esté loco (aunque a veces puede parecerlo 🤪). La verdad es que los LLMs pueden ser un poco... creativos cuando se trata de generar texto. Pero gracias a DoLa, un método que utiliza capas de contraste para mejorar la factibilidad de los LLMs, podemos evitar que nuestros modelos de lenguaje se conviertan en escritores de ciencia ficción 😂. En este post, te explicaré cómo funciona DoLa y te mostraré un ejemplo de código para que puedas entender mejor cómo hacer que tus LLMs sean más fiables y menos propensos a inventar historias. ¡Vamos a salvar a nuestros LLMs de la locura y hacer que sean más útiles! 🚀

Últimos posts -->

¿Has visto estos proyectos?

Subtify

Subtify Subtify

Generador de subtítulos para videos en el idioma que desees. Además a cada persona le pone su subtítulo de un color

Ver todos los proyectos -->

¿Quieres aplicar la IA en tu proyecto? Contactame!

¿Quieres mejorar con estos tips?

Últimos tips -->

Usa esto en local

Los espacios de Hugging Face nos permite ejecutar modelos con demos muy sencillas, pero ¿qué pasa si la demo se rompe? O si el usuario la elimina? Por ello he creado contenedores docker con algunos espacios interesantes, para poder usarlos de manera local, pase lo que pase. De hecho, es posible que si pinchas en alún botón de ver proyecto te lleve a un espacio que no funciona.

Ver todos los contenedores -->

¿Quieres aplicar la IA en tu proyecto? Contactame!

¿Quieres entrenar tu modelo con estos datasets?

short-jokes-dataset

Dataset de chistes en inglés

opus100

Dataset con traducciones de inglés a español

netflix_titles

Dataset con películas y series de Netflix

Ver más datasets -->