Docker

Tools

Las tres áreas problemáticas del software

Algunos problemas que influyeron para la creación de Docker

Problemas del desarrollo de software

Promesa de Docker:
Construir, distribuir y ejecutar cualquier aplicación en cualquier lado.
Suena a java.

Virtualización

Una forma de resolver los problemas antes mencionados, es la emulación de un recurso computacional, en el caso de virtualización se emula computadoras completas dentro de una computadora más grande.

Problemas de la virtualización

Contenedores

docker info
Client:
 Context:    default
 Debug Mode: false
 Plugins:
  buildx: Build with BuildKit (Docker Inc., v0.5.1-docker)
  compose: Docker Compose (Docker Inc., v2.0.0-beta.6)
  scan: Docker Scan (Docker Inc., v0.8.0)

Server:
 Containers: 2
  Running: 1
  Paused: 0
  Stopped: 1
 Images: 3
 Server Version: 20.10.7
 Storage Driver: overlay2
  Backing Filesystem: extfs
  Supports d_type: true
  Native Overlay Diff: true
  userxattr: false
 Logging Driver: json-file
 Cgroup Driver: cgroupfs
 Cgroup Version: 1
 Plugins:
  Volume: local
  Network: bridge host ipvlan macvlan null overlay
  Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
 Swarm: inactive
 Runtimes: io.containerd.runc.v2 io.containerd.runtime.v1.linux runc
 Default Runtime: runc
 Init Binary: docker-init
 containerd version: d71fcd7d8303cbf684402823e425e9dd2e99285d
 runc version: b9ee9c6314599f1b4a7f497e1f1f856fe433d3b7
 init version: de40ad0
 Security Options:
  seccomp
   Profile: default
 Kernel Version: 5.10.16.3-microsoft-standard-WSL2
 Operating System: Docker Desktop
 OSType: linux
 Architecture: x86_64
 CPUs: 16
 Total Memory: 11.95GiB
 Name: docker-desktop
 ID: WZLE:RNY4:ZHJQ:OGFA:IEU2:I3W3:MBE6:PUVM:I6EU:4GCE:R7SP:C7ZL
 Docker Root Dir: /var/lib/docker
 Debug Mode: false
 Registry: https://index.docker.io/v1/
 Labels:
 Experimental: false
 Insecure Registries:
  127.0.0.0/8
 Live Restore Enabled: false

WARNING: No blkio throttle.read_bps_device support
WARNING: No blkio throttle.write_bps_device support
WARNING: No blkio throttle.read_iops_device support
WARNING: No blkio throttle.write_iops_device support

Docker Architecture

Nomenclatura para especificar una imagen:

El comando más usado

Listar contenedores en ejecución

docker ps

Listar contenedores incluidos los detenidos

docker ps -a

El proceso principal y parada de contenedor

Para crear e iniciar (create and start) un contenedor se puede usar el método run

docker run $IMAGE $COMMAND

A menudo un problema típico cuando uno empieza con Docker es que arrancamos un contenedor y este arranca y se detiene justo después de arrancar.
Esto se debe a que a diferencia de una VM, docker corre el contenedor como un árbol de procesos, a partir de un proceso principal.
Al momento que termina el proceso principal, el contenedor es terminado.
El proceso principal se especifica con el parametro COMMAND al hacer create o run.
Como se verá más adelante también se puede indicar mediante una combinación de ENTRYPOINT y COMMAND.

Ejemplos de inicio de contenedores con terminación inmediata

docker run debian
docker run debian /bin/bash

La opción -t permite asignar una terminal al contenedor, la cual será asociada a la terminal donde lo arrancamos. Sin embargo al no ser interactiva no se envian nuestras pulsaciones de tecla, por lo que la terminal queda como congelada (no recibe entrada del teclado).

docker run -t debian bash
root@04a9fa415379:/#

La opción -i nos permite indicar que el contenedor corre en modo interactivo, es decir recibe entrada del teclado, sin embargo al no asociar a una tty no se puede interactuar correctamente.

docker run -i debian bash
oeauoeuoeu
bash: line 1: $'oeauoeuoeu\r': command not found
ls
bash: line 2: $'ls\r': command not found
who
bash: line 3: $'who\r': command not found
echo
bash: line 4: $'echo\r': command not found
/bin/bash
: No such file or directory
bash: line 6: $'\r': command not found

Al arrancar un contenedor podemos solicitar que sea interactivo y que se genere y asocie una tty con la terminal que estamos utilizando, combinando los dos modificadores anteriormente explicados.
La terminal queda asociada y al terminar el proceso bash, con exit el contenedor es terminado.

docker run -it debian bash
root@742234a35dc9:/# ls -lh
total 64K
drwxr-xr-x   2 root root 4.0K Aug 16 00:00 bin
drwxr-xr-x   2 root root 4.0K Apr 10 20:15 boot
drwxr-xr-x   5 root root  360 Aug 20 01:23 dev
drwxr-xr-x   1 root root 4.0K Aug 20 01:23 etc
drwxr-xr-x   2 root root 4.0K Apr 10 20:15 home
drwxr-xr-x   8 root root 4.0K Aug 16 00:00 lib
drwxr-xr-x   2 root root 4.0K Aug 16 00:00 lib64
drwxr-xr-x   2 root root 4.0K Aug 16 00:00 media
drwxr-xr-x   2 root root 4.0K Aug 16 00:00 mnt
drwxr-xr-x   2 root root 4.0K Aug 16 00:00 opt
dr-xr-xr-x 288 root root    0 Aug 20 01:23 proc
drwx------   2 root root 4.0K Aug 16 00:00 root
drwxr-xr-x   3 root root 4.0K Aug 16 00:00 run
drwxr-xr-x   2 root root 4.0K Aug 16 00:00 sbin
drwxr-xr-x   2 root root 4.0K Aug 16 00:00 srv
dr-xr-xr-x  11 root root    0 Aug 20 01:23 sys
drwxrwxrwt   2 root root 4.0K Aug 16 00:00 tmp
drwxr-xr-x  11 root root 4.0K Aug 16 00:00 usr
drwxr-xr-x  11 root root 4.0K Aug 16 00:00 var
root@742234a35dc9:/# exit
exit

alfonso.baqueiro@laptop ~

Para no bloquear la terminal es necesario desasociarlo con la opción -d, de igual forma se requiere -it para que bash se quede a la espera de comando y no sea terminado.

docker run -d -it debian bash
89d65fbbd8f2c22d03105c90fb6ec89a13420a869a9f72f0e8dbbada0f502342
$ docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED         STATUS         PORTS     NAMES
89d65fbbd8f2   debian    "bash"    6 seconds ago   Up 5 seconds             affectionate_heisenberg

Nombrar el contenedor

Como se puede ver docker nombra al contenedor con un nombre aleatorio, si queremos nombrarlo usamos la opción --name

docker run --name ponche -it -d debian
694af83771e38cb00b8426bbd89ac1a9135b05ecc53292ba7af4419c74780b99
$ docker ps -a
CONTAINER ID   IMAGE                               COMMAND                  CREATED         STATUS                     PORTS                               NAMES
694af83771e3   debian                              "bash"                   4 seconds ago   Up 3 seconds                                                   ponche
$

bash es el COMMAND por defecto, como podemos comprobar el contenedor permanece corriendo y la terminal es liberada
Para acceder al bash del proceso principal podemos usar attach.
Si salimos del bash, al ser el proceso main, se detiene el contenedor.

docker container attach ponche
root@694af83771e3:/# exit
exit
$ docker ps -a
CONTAINER ID   IMAGE                               COMMAND                  CREATED          STATUS                      PORTS                               NAMES
694af83771e3   debian                              "bash"                   10 minutes ago   Exited (0) 14 seconds ago                                       ponche

Para volver a arrancar el contenedor usamos start

docker start ponche
ponche
$ docker ps -a
CONTAINER ID   IMAGE                               COMMAND                  CREATED          STATUS                     PORTS                               NAMES
694af83771e3   debian                              "bash"                   15 minutes ago   Up 4 seconds                                                   ponche

Para ejecutar otro comando en un contenedorexec si el comando termina regresa el control al anfitrión.

docker exec ponche cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin

Se puede usar para obtener un shell bash adicional, pero para salga de inmediato es necesario crear un tty en modo interactivo.

docker exec -it ponche bash
root@694af83771e3:/# apt update
Get:1 http://deb.debian.org/debian bullseye InRelease [113 kB]
Get:2 http://security.debian.org/debian-security bullseye-security InRelease [44.1 kB]
Get:3 http://security.debian.org/debian-security bullseye-security/main amd64 Packages [25.8 kB]
Get:4 http://deb.debian.org/debian bullseye-updates InRelease [36.8 kB]
Get:5 http://deb.debian.org/debian bullseye/main amd64 Packages [8178 kB]
Fetched 8397 kB in 7s (1120 kB/s)
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
All packages are up to date.
root@694af83771e3:/# apt install htop
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following additional packages will be installed:
  libgpm2 libncursesw6 libnl-3-200 libnl-genl-3-200
Suggested packages:
  lm-sensors lsof strace gpm
The following NEW packages will be installed:
  htop libgpm2 libncursesw6 libnl-3-200 libnl-genl-3-200
0 upgraded, 5 newly installed, 0 to remove and 0 not upgraded.
Need to get 379 kB of archives.
After this operation, 1070 kB of additional disk space will be used.
Do you want to continue? [Y/n]
Get:1 http://deb.debian.org/debian bullseye/main amd64 libncursesw6 amd64 6.2+20201114-2 [132 kB]
Get:2 http://deb.debian.org/debian bullseye/main amd64 libnl-3-200 amd64 3.4.0-1+b1 [63.6 kB]
Get:3 http://deb.debian.org/debian bullseye/main amd64 libnl-genl-3-200 amd64 3.4.0-1+b1 [21.2 kB]
Get:4 http://deb.debian.org/debian bullseye/main amd64 htop amd64 3.0.5-7 [127 kB]
Get:5 http://deb.debian.org/debian bullseye/main amd64 libgpm2 amd64 1.20.7-8 [35.6 kB]
Fetched 379 kB in 1s (632 kB/s)
debconf: delaying package configuration, since apt-utils is not installed
Selecting previously unselected package libncursesw6:amd64.
(Reading database ... 6653 files and directories currently installed.)
Preparing to unpack .../libncursesw6_6.2+20201114-2_amd64.deb ...
Unpacking libncursesw6:amd64 (6.2+20201114-2) ...
Selecting previously unselected package libnl-3-200:amd64.
Preparing to unpack .../libnl-3-200_3.4.0-1+b1_amd64.deb ...
Unpacking libnl-3-200:amd64 (3.4.0-1+b1) ...
Selecting previously unselected package libnl-genl-3-200:amd64.
Preparing to unpack .../libnl-genl-3-200_3.4.0-1+b1_amd64.deb ...
Unpacking libnl-genl-3-200:amd64 (3.4.0-1+b1) ...
Selecting previously unselected package htop.
Preparing to unpack .../htop_3.0.5-7_amd64.deb ...
Unpacking htop (3.0.5-7) ...
Selecting previously unselected package libgpm2:amd64.
Preparing to unpack .../libgpm2_1.20.7-8_amd64.deb ...
Unpacking libgpm2:amd64 (1.20.7-8) ...
Setting up libgpm2:amd64 (1.20.7-8) ...
Setting up libncursesw6:amd64 (6.2+20201114-2) ...
Setting up libnl-3-200:amd64 (3.4.0-1+b1) ...
Setting up libnl-genl-3-200:amd64 (3.4.0-1+b1) ...
Setting up htop (3.0.5-7) ...
Processing triggers for libc-bin (2.31-13) ...
root@694af83771e3:/# exit

Correr por ejemplo htop dentro del contenedor

docker exec ponche htop
Error opening terminal: unknown.

Hay que recordar que por eso es necesario usar -it. (Para salir de htop presionar la tecla q).

docker exec -it ponche htop
    0[                                    0.0%]    4[                                    0.0%]     8[                                    0.0%]   12[                                    0.0%]
    1[                                    0.0%]    5[                                    0.0%]     9[                                    0.0%]   13[                                    0.0%]
    2[|                                   0.7%]    6[|                                   0.7%]    10[                                    0.0%]   14[                                    0.0%]
    3[                                    0.0%]    7[                                    0.0%]    11[                                    0.0%]   15[                                    0.0%]
  Mem[||||||||||||||||||||||||||||                                                1.11G/12.0G]   Tasks: 2, 0 thr; 1 running
  Swp[                                                                               0K/4.00G]   Load average: 0.02 0.03 0.48
                                                                                                 Uptime: 3 days, 15:15:55

  PID USER      PRI  NI  VIRT   RES   SHR S CPU%-MEM%   TIME+  Command
    1 root       20   0  3964  3284  2852 S  0.0  0.0  0:00.02 bash
  343 root       20   0  5636  3988  2964 R  0.0  0.0  0:00.02 htop

Para detener un contenedor usamos stop

docker stop ponche
ponche
$ docker ps -a
CONTAINER ID   IMAGE                               COMMAND                  CREATED          STATUS                       PORTS                               NAMES
694af83771e3   debian                              "bash"                   37 minutes ago   Exited (137) 4 seconds ago                                       ponche

Matar un contenedor por la mala SIGKILL kill, check exit code 137 - 128 = 9, o sea recibio la SIGKILL que es 9

docker kill ponche
ponche
$ docker ps -a
CONTAINER ID   IMAGE                               COMMAND                  CREATED          STATUS                       PORTS                               NAMES
694af83771e3   debian                              "bash"                   37 minutes ago   Exited (137) 4 seconds ago                                       ponche

Para borrar un contenedor que se encuentra ejecutandose

docker rm -f $CONTAINER

Para borrar un contenedor que esta parado

docker rm $CONTAINER

Ejecutar un comando en un contenedor en ejecución

docker exec -it $CONTAINER $COMMAND

Algunos trucos para arrancar contenedores

Crear un contenedor que se detenga después de N segundos usando como proceso sleep N ej. de un contenedor que se detiene tras 600 segundos

docker run -d debian sleep 600

Podemos hacer que se autodestruya si incluimos --rm

docker run --rm -d debian sleep 600

Usar como proceso principal un proceso que no termine para que el contenedor siga ejecutandose ej.

docker run -d debian tail -f /dev/null
docker run -d debian tail -f -n0 /etc/passwd

Por supuesto que lo más simple es correr un terminal interactivo detachado -ditque se mantenga como proceso main (bash).

docker run -dit debian

Exponiendo contenedores

docker run -d --name proxy nginx
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS     NAMES
c2e3294cc58f   nginx     "/docker-entrypoint.…"   3 minutes ago    Up 3 minutes    80/tcp    proxy

# delete running container
docker rm -f proxy

# export a port
docker run --name proxy -p 8080:80 nginx

$ docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS                  NAMES
e6c96e20008b   nginx     "/docker-entrypoint.…"   2 seconds ago    Up 2 seconds    0.0.0.0:8080->80/tcp   proxy

# mirar logs
docker logs proxy
proxy container logs
...

# dar seguimiento a logs
docker logs -f proxy

# solo las ultimas lineas
docker logs --tail $N_LINES -f proxy

Bind mounts

docker run -d --name mungus mongo docker exec -it mungus bash # inside run mongo and run mongo show dbs use platzi db.user.insert({"name":"Poncho"}) db.user.insert({"name":"Felipe"}) db.user.insert({"name":"Susan"}) db.user.insert({"name":"Poncho"}) db.user.find() # en el host mkdir docker-data docker cp mungus:/data/db docker-data/data/ # destruir el contenedor docker rm -f mungus # montar con un bind docker run -d --name mungus -v /root/docker-data/data/db:/data/db mongo

Volumes

docker stop mungus docker volume list DRIVER VOLUME NAME local 8a3afbb0a223223fc8fdfdc980f784cf979f361967db34317264dfadd90a9ff1 local 269081a9cc1eef4fd84abbfc4fd4edced7af6e7da77e56c688b02c64e3e1d2e4 local e0ea55d7558955046e3cf446ceeaa7181543e3a88a4b50dabb4dd43294425445 docker volume create mungus-data-db RIVER VOLUME NAME local 8a3afbb0a223223fc8fdfdc980f784cf979f361967db34317264dfadd90a9ff1 local 269081a9cc1eef4fd84abbfc4fd4edced7af6e7da77e56c688b02c64e3e1d2e4 local e0ea55d7558955046e3cf446ceeaa7181543e3a88a4b50dabb4dd43294425445 local mungus-data-db # creamos el contenedor con un volumen docker -d --name mungus --mount src=mungus-data-db,dst=/data/db mongo # checando la config del contenedor docker inspect mungus # si observamos podemos encontrar donde coloca los archivos "Mounts": [ { "Type": "volume", "Name": "mungus-data-db", "Source": "/var/lib/docker/volumes/mungus-data-db/_data", "Destination": "/data/db", "Driver": "local", "Mode": "z", "RW": true, "Propagation": "" }, { "Type": "volume", "Name": "d803888dd915be4a5c9f932d7e0e75bbf1a0ede9d4b21173c9658c0d1bd08c36", "Source": "/var/lib/docker/volumes/d803888dd915be4a5c9f932d7e0e75bbf1a0ede9d4b21173c9658c0d1bd08c36/_data", "Destination": "/data/configdb", "Driver": "local", "Mode": "", "RW": true, "Propagation": "" } ],

Si inspeccionamos un volume, vemos que contiene la ruta donde se guardan los datos, ej: /var/lib/docker/volumes/mungus-data-db/_data en windows se guarda en
C:\Users\$USER\AppData\Local\Docker\wsl\data\
al parecer dentro de un archivo ext4.vhdx

Hay otro tipo de mount tmpfs mount, el cúal es volatil pero solo funciona en linux

Copiar archivos de y hacia un contenedor

Se puede usar para copiar archivos de o hacía un contenedor, util para inyectar o extraer datos

docker cp $SOURCE $DESTINATION

# $SOURCE and $DESTINATION pueden ser
# CONTAINER:PATH or PATH
# se puede usar aún si el contenedor esta parado

Crear contenedor pero no arrancarlo

docker create --name mungus --mount src=mungus-data-db,dst=/data/db mongo
533d964c1c49b15507711f2df80b94bdd950069cab87f5fa9f5bf765b720e6a6

docker ps -a
CONTAINER ID   IMAGE                               COMMAND                  CREATED              STATUS                      PORTS                               NAMES
533d964c1c49   mongo                               "docker-entrypoint.s…"   About a minute ago   Created                                                         mungus

Images

https://hub.docker.com/ es el registry oficial de imagenes, aúnque es posible añadir otros registry

TAG es la versión, si no ponemos usa latest por omisión

docker image list
REPOSITORY   TAG                       IMAGE ID       CREATED        SIZE
debian       latest                    fe3c5de03486   20 hours ago   124MB
mongo        latest                    269b735e72cb   12 days ago    682MB
abaqueiro    jessie-apache-php5.6.40   4fd786ff7d56   13 days ago    500MB
ubuntu       latest                    1318b700e415   3 weeks ago    72.8MB
php          5.6.40-apache-jessie      d34f09f63596   2 years ago    374MB
docker image pull debian:buster
buster: Pulling from library/debian
1cfaf5c6f756: Pull complete
Digest: sha256:e2fe52e17d649812bddcac07faf16f33542129a59b2c1c59b39a436754b7f146
Status: Downloaded newer image for debian:buster
docker.io/library/debian:buster

docker image list
REPOSITORY   TAG                       IMAGE ID       CREATED        SIZE
debian       buster                    63652705977d   20 hours ago   114MB
debian       latest                    fe3c5de03486   20 hours ago   124MB
mongo        latest                    269b735e72cb   12 days ago    682MB
abaqueiro    jessie-apache-php5.6.40   4fd786ff7d56   13 days ago    500MB
ubuntu       latest                    1318b700e415   3 weeks ago    72.8MB
php          5.6.40-apache-jessie      d34f09f63596   2 years ago    374MB

Particularidades de Docker Images

Una imagen es un conjunto de capas ordenado o apilado

Listar las imagenes actualmente disponibles

docker image list
REPOSITORY       TAG                       IMAGE ID       CREATED        SIZE
debian           buster                    63652705977d   25 hours ago   114MB
debian           latest                    fe3c5de03486   25 hours ago   124MB
mongo            latest                    269b735e72cb   12 days ago    682MB
abaqueiro        jessie-apache-php5.6.40   4fd786ff7d56   2 weeks ago    500MB
ubuntu           latest                    1318b700e415   3 weeks ago    72.8MB
wagoodman/dive   latest                    822b23d200a3   5 months ago   82.4MB
php              5.6.40-apache-jessie      d34f09f63596   2 years ago    374MB

Traer una imagen al cache de imagenes

docker image pull $IMAGE
docker image pull ubuntu:18.04
18.04: Pulling from library/ubuntu
feac53061382: Pull complete
Digest: sha256:7bd7a9ca99f868bf69c4b6212f64f2af8e243f97ba13abb3e641e03a7ceb59e8
Status: Downloaded newer image for ubuntu:18.04
docker.io/library/ubuntu:18.04

Podemos mostrar la historia de una imagen para ver como ha sido construida

docker image history $IMAGE
docker image history httpd
IMAGE          CREATED      CREATED BY                                      SIZE      COMMENT
c8ca530172a8   2 days ago   /bin/sh -c #(nop)  CMD ["httpd-foreground"]     0B
      2 days ago   /bin/sh -c #(nop)  EXPOSE 80                    0B
      2 days ago   /bin/sh -c #(nop) COPY file:c432ff61c4993ecd…   138B
      2 days ago   /bin/sh -c #(nop)  STOPSIGNAL SIGWINCH          0B
      2 days ago   /bin/sh -c set -eux;   savedAptMark="$(apt-m…   61.2MB
      2 days ago   /bin/sh -c #(nop)  ENV HTTPD_PATCHES=           0B
      2 days ago   /bin/sh -c #(nop)  ENV HTTPD_SHA256=1bc826e7…   0B
      2 days ago   /bin/sh -c #(nop)  ENV HTTPD_VERSION=2.4.48     0B
      2 days ago   /bin/sh -c set -eux;  apt-get update;  apt-g…   7.38MB
      2 days ago   /bin/sh -c #(nop) WORKDIR /usr/local/apache2    0B
      2 days ago   /bin/sh -c mkdir -p "$HTTPD_PREFIX"  && chow…   0B
      2 days ago   /bin/sh -c #(nop)  ENV PATH=/usr/local/apach…   0B
      2 days ago   /bin/sh -c #(nop)  ENV HTTPD_PREFIX=/usr/loc…   0B
      3 days ago   /bin/sh -c #(nop)  CMD ["bash"]                 0B
      3 days ago   /bin/sh -c #(nop) ADD file:87b4e60fe3af680c6…   69.3MB

DIVE: Herramienta de exploracion de docker images

https://github.com/wagoodman/dive Esta herramienta es un programa escrito en GO que permite explorar como esta construida una imagen $IMAGE

Podemos correrla como imagen de docker:

docker run --rm -it -v /var/run/docker.sock:/var/run/docker.sock wagoodman/dive:latest $IMAGE

Docker avanzado

Sacando la basura

Listar ids de contenedores

docker ps -aq
7ca393266942
533d964c1c49
6103e041fff3
fa82169f5e33
8214592c1bd7
452a600e3c5e

Para borrar un contenedor que se encuentra corriendo podemos usar

docker rm -f $CONTAINER

Podemos hacer una combinacion para borrar todo aún cuando estén corriendo

docker rm -f $(docker ps -aq)

El comando prune funciona con cualquier elemento

docker image prune
docker network prune
docker volume prune

Incluso si queremos hacer una limpieza total

docker system prune

Restringiendo RAM y recursos

consultar estadisticas de uso de recursos, (Ctrl+C para terminar)

docker stats
CONTAINER ID   NAME          CPU %     MEM USAGE / LIMIT     MEM %     NET I/O          BLOCK I/O   PIDS
533d964c1c49   mungus        0.62%     208.3MiB / 11.95GiB   1.70%     2.41kB / 0B      0B / 0B     34
6103e041fff3   debeianita    0.00%     26.26MiB / 11.95GiB   0.21%     16.8MB / 300kB   0B / 0B     2
fa82169f5e33   soyloquesoy   0.00%     876KiB / 11.95GiB     0.01%     4.14kB / 0B      0B / 0B     1

restringir la memoria a 1 Gb

docker run --memory 1g $IMAGE

Construyendo imagenes

Para ilustrar este punto, vamos a crear una instancia de debian versión 10

docker run -itd --name try-debian-10 debian:10

Nos agenciamos una shell a la instancia corriendo

docker exec -it try-debian-10 bash

¿como nos ayuda docker a entender que hace el software tras bambalinas

Corremos comandos normales de administración de paquetes en debian

root@63e736f5cb4d:/# apt-get update
Get:1 http://deb.debian.org/debian buster InRelease [122 kB]
Get:2 http://security.debian.org/debian-security buster/updates InRelease [65.4 kB]
Get:3 http://deb.debian.org/debian buster-updates InRelease [51.9 kB]
Get:4 http://deb.debian.org/debian buster/main amd64 Packages [7907 kB]
Get:5 http://security.debian.org/debian-security buster/updates/main amd64 Packages [301 kB]
Get:6 http://deb.debian.org/debian buster-updates/main amd64 Packages [15.2 kB]
Fetched 8463 kB in 3s (2541 kB/s)
Reading package lists... Done

Podemos determinar exactamente que archivos genera o afecta dicho comando, lo que nos permite identificar que archivos se pueden borrar para optimizar el tamaño de nuestras imagenes, y también para aprender que sucede tras bambalinas

$ docker diff try-debian-10
C /root
A /root/.bash_history
C /var
C /var/lib
C /var/lib/apt
C /var/lib/apt/lists
A /var/lib/apt/lists/auxfiles
A /var/lib/apt/lists/deb.debian.org_debian_dists_buster_InRelease
A /var/lib/apt/lists/deb.debian.org_debian_dists_buster_main_binary-amd64_Packages.lz4
A /var/lib/apt/lists/partial
A /var/lib/apt/lists/security.debian.org_debian-security_dists_buster_updates_main_binary-amd64_Packages.lz4
A /var/lib/apt/lists/deb.debian.org_debian_dists_buster-updates_InRelease
A /var/lib/apt/lists/deb.debian.org_debian_dists_buster-updates_main_binary-amd64_Packages.lz4
A /var/lib/apt/lists/lock
A /var/lib/apt/lists/security.debian.org_debian-security_dists_buster_updates_InRelease

Como es fácil observar, se afectan archivos en /var/lib/apt/lists que como es muy probable son archivos de cache, por lo que probablemente se puedan remover para generar una imagen compacta.

root@63e736f5cb4d:/# du -hs /var/lib/apt/lists
17M     /var/lib/apt/lists

Podemos por ejemplo lanzar una imagen efímera para saber cuanto espacio ocupa /var/lib/apt/lists antes del apt-get update

docker run --rm -it debian:10 du -hs /var/lib/apt/lists
4.0K    /var/lib/apt/lists

Lo anterior nos confirma que ese directorio se encuentra vació en un estado inicial, y por tanto se puede generar mediante apt-get update.
Cabe destacar el derroche de poder, somos como dioses de la creación y destrucción, creando o destruyendo galaxias completas.
En este caso creamos un sistema debian solo para saber cuando espacio ocupaba uno de los directorios, y luego de saber, destruimos la instancia.

Continuando con el plan, la idea es instalar aplicaciones de uso común para desarrollo y testing como son:

setup:

root@63e736f5cb4d:/# apt-get update && apt-get install -y \
apt-file \
curl \
dnsutils \
git \
htop \
less \
man \
net-tools \
netcat \
nmap \
pv \
tree \
unzip \
vim \
wget \
zip

crear la imagen de la instancia modificada con commit

docker commit -a "Alfonso Baqueiro" try-debian-10 debian-tools:10
sha256:f3429b20f86e006d39fcb99cb13ee98802396be76029b372617b418afe5257f7
docker image list
REPOSITORY       TAG                       IMAGE ID       CREATED          SIZE
debian-tools     10                        f3429b20f86e   26 seconds ago   466MB

REPLICABILIDAD, documentar mediante el Dockerfile los cambios

Todas estas moficicaciones manuales que hicimos a la instancia, se pueden hacer replicables y prácticamente como una receta de construcción mediante el Dockerfile

FROM debian:10 RUN apt-get update && apt-get install -y \ apt-file \ curl \ dnsutils \ git \ htop \ less \ man \ net-tools \ netcat \ nmap \ pv \ tree \ unzip \ vim \ wget \ zip

Creamos la imagen mediante build

docker build -t debian-tools:10 .
[+] Building 44.4s (6/6) FINISHED
 => [internal] load build definition from Dockerfile                                                                                                                                         0.0s
 => => transferring dockerfile: 251B                                                                                                                                                         0.0s
 => [internal] load .dockerignore                                                                                                                                                            0.0s
 => => transferring context: 2B                                                                                                                                                              0.0s
 => [internal] load metadata for docker.io/library/debian:10                                                                                                                                 0.0s
 => CACHED [1/2] FROM docker.io/library/debian:10                                                                                                                                            0.0s
 => [2/2] RUN apt-get update && apt-get install -y  apt-file  curl  dnsutils  git  htop  less  man  net-tools  netcat  nmap  pv  tree  unzip  vim  wget  zip                                42.5s
 => exporting to image                                                                                                                                                                       1.8s
 => => exporting layers                                                                                                                                                                      1.8s
 => => writing image sha256:42522ffc8a73ba0d41e76cd6f1b881e1f5d668e32057ca2f7eb19b684cd91300                                                                                                 0.0s
 => => naming to docker.io/library/debian-tools:10                                                                                                                                           0.0s

Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them

Verificamos de que tamaño ha quedado la imagen, como podemos ver ocupa 367MB vs 466MB de la imagen que hicimos usando commit , esto seguramente debido a archivos cache, cosa que evitamos al hacer limpia.

docker image list
REPOSITORY       TAG                       IMAGE ID       CREATED        SIZE
debian-tools     10                        42522ffc8a73   38 hours ago   367MB
<none>           <none>                    f3429b20f86e   39 hours ago   466MB

Un aspecto valioso es poder compartir esta imagen, cosa que podemos hacer subiendola a docker hub mediante push

Para ello necesitamos tener una cuenta, que podemos crear en hub.docker.com
Y luego hacer login mediante

docker login
docker push debian-tools:10
The push refers to repository [docker.io/library/debian-tools]
40efc50d7e6b: Preparing
c2ddc1bc2645: Preparing
denied: requested access to the resource is denied

Esto se debe a que estamos tratando de ponerlo en el library principal, nos ha faltado especificar la cuenta de usuario donde se debe poner la imagen.

docker push abaqueiro/debian-tools:10
The push refers to repository [docker.io/abaqueiro/debian-tools]
An image does not exist locally with the tag: abaqueiro/debian-tools

No encuentra la imagen, por lo que es necesario etiquetarla

docker tag debian-tools:10 abaqueiro/debian-tools:10

docker image list
REPOSITORY               TAG                       IMAGE ID       CREATED        SIZE
abaqueiro/debian-tools   10                        42522ffc8a73   39 hours ago   367MB

Reintentamos el push

docker push abaqueiro/debian-tools:10
The push refers to repository [docker.io/abaqueiro/debian-tools]
40efc50d7e6b: Pushed
c2ddc1bc2645: Pushed
10: digest: sha256:d18a263a3b0ad52f335f59cd63bfcc9d0d2e6c1526d1b6deec32a0eff75805db size: 741

Docker networking

docker network list
NETWORK ID     NAME      DRIVER    SCOPE
610dcd163dd2   bridge    bridge    local
3ab918f7294b   host      host      local
89ccac87dd2e   none      null      local
docker network inspect bridge
[
    {
        "Name": "bridge",
        "Id": "610dcd163dd2f3391d56b1e51724f464307d2a13d9c1cdc49a263257456056a1",
        "Created": "2021-08-22T12:44:55.04629582Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16",
                    "Gateway": "172.17.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "45a111c7e4ab2301d8f4fa222cdac7a4a6bc2a837c9a4d8de44ef154065324b8": {
                "Name": "platziapp",
                "EndpointID": "6c67031fd5a66e6ca71827bf8b313c035ae3d8a5d468130d3901de5ec378c5a4",
                "MacAddress": "02:42:ac:11:00:04",
                "IPv4Address": "172.17.0.4/16",
                "IPv6Address": ""
            },
            "533d964c1c49b15507711f2df80b94bdd950069cab87f5fa9f5bf765b720e6a6": {
                "Name": "mungus",
                "EndpointID": "9953358bc0e00ba0be67f34ccb54f21261618fc5e2eb65cf76484e3425d3fb65",
                "MacAddress": "02:42:ac:11:00:03",
                "IPv4Address": "172.17.0.3/16",
                "IPv6Address": ""
            },
            "63e736f5cb4d1246984e1ffdcb0cdcbd56f8af9e53ebc900899146abda1b0e97": {
                "Name": "try-debian-10",
                "EndpointID": "979b35dc1821e54fa704575d09c4145d3e103da5adb65337e86f2ce4e229fbc2",
                "MacAddress": "02:42:ac:11:00:02",
                "IPv4Address": "172.17.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {}
    }
]

Crear una red extra

docker network create --attachable --driver=bridge --subnet=172.23.0.0/16 --ip-range=172.23.5.0/24 --gateway=172.23.0.1 skynet
31a8e6735c835159fcab3dd5a33baa4fa9cd4a52eda2067f467ab3f6c2d12f34

docker network list
NETWORK ID     NAME      DRIVER    SCOPE
610dcd163dd2   bridge    bridge    local
3ab918f7294b   host      host      local
89ccac87dd2e   none      null      local
31a8e6735c83   skynet    bridge    local

docker network inspect skynet
[
    {
        "Name": "skynet",
        "Id": "31a8e6735c835159fcab3dd5a33baa4fa9cd4a52eda2067f467ab3f6c2d12f34",
        "Created": "2021-08-24T18:10:48.581489897Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.23.0.0/16",
                    "IPRange": "172.23.5.0/24",
                    "Gateway": "172.23.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": true,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {},
        "Labels": {}
    }
]

docker network connect skynet mungus

docker network inspect skynet
[
    {
        "Name": "skynet",
        "Id": "31a8e6735c835159fcab3dd5a33baa4fa9cd4a52eda2067f467ab3f6c2d12f34",
        "Created": "2021-08-24T18:10:48.581489897Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.23.0.0/16",
                    "IPRange": "172.23.5.0/24",
                    "Gateway": "172.23.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": true,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "45a111c7e4ab2301d8f4fa222cdac7a4a6bc2a837c9a4d8de44ef154065324b8": {
                "Name": "platziapp",
                "EndpointID": "24f2ba319e5bef98ae3db52873e8868e7f9a8f4872066948cdf7081c7f5f5734",
                "MacAddress": "02:42:ac:17:05:00",
                "IPv4Address": "172.23.5.0/16",
                "IPv6Address": ""
            },
            "533d964c1c49b15507711f2df80b94bdd950069cab87f5fa9f5bf765b720e6a6": {
                "Name": "mungus",
                "EndpointID": "f21693a794e89cbe6601f68d54b86d3db793ad062d1606b4bd9d777548607d6c",
                "MacAddress": "02:42:ac:17:05:01",
                "IPv4Address": "172.23.5.1/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]

Cabe destacar que si analizamos como ven la red los contenedores encontramos que se crea una interface de red adicional eth1

docker exec -it mungus ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.3  netmask 255.255.0.0  broadcast 172.17.255.255
        ether 02:42:ac:11:00:03  txqueuelen 0  (Ethernet)
        RX packets 15045  bytes 19908156 (19.9 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 8085  bytes 442611 (442.6 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

eth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.23.5.1  netmask 255.255.0.0  broadcast 172.23.255.255
        ether 02:42:ac:17:05:01  txqueuelen 0  (Ethernet)
        RX packets 12  bytes 936 (936.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

docker exec -it platziapp ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.4  netmask 255.255.0.0  broadcast 172.17.255.255
        ether 02:42:ac:11:00:04  txqueuelen 0  (Ethernet)
        RX packets 6070  bytes 8638989 (8.2 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 2498  bytes 137181 (133.9 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

eth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.23.5.0  netmask 255.255.0.0  broadcast 172.23.255.255
        ether 02:42:ac:17:05:00  txqueuelen 0  (Ethernet)
        RX packets 19  bytes 1522 (1.4 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

Un aspecto interesante es que cuando creamos una red y añadimos un contenedor, docker maneja y configura un servicio de DNS para los contenedores y resuelve a IP los nombres de los contenedores
* Para correr nslookup o dig es necesario instalar en el contenedor dnsutils mediante el gestor de paquetes apt.

docker exec -it platziapp nslookup mungus
Server:         127.0.0.11
Address:        127.0.0.11#53

Non-authoritative answer:
Name:   mungus
Address: 172.23.5.1

docker exec -it platziapp ping -c 5 mungus
PING mungus (172.23.5.1) 56(84) bytes of data.
64 bytes from mungus.skynet (172.23.5.1): icmp_seq=1 ttl=64 time=0.076 ms
64 bytes from mungus.skynet (172.23.5.1): icmp_seq=2 ttl=64 time=0.073 ms
64 bytes from mungus.skynet (172.23.5.1): icmp_seq=3 ttl=64 time=0.164 ms
64 bytes from mungus.skynet (172.23.5.1): icmp_seq=4 ttl=64 time=0.101 ms
64 bytes from mungus.skynet (172.23.5.1): icmp_seq=5 ttl=64 time=0.162 ms

Una última prueba será destruir la red skynet

docker network rm skynet
Error response from daemon: error while removing network: network skynet id 31a8e6735c835159fcab3dd5a33baa4fa9cd4a52eda2067f467ab3f6c2d12f34 has active endpoints

docker network disconnect skynet mungus

docker network disconnect skynet platziapp

docker network rm skynet
skynet

Docker Compose

Ej. de archivo docker-compose.yml donde se declaran los servicios, en este caso app y db

version: "3.8" services: app: image: platziapp environment: MONGO_URL: "mongodb://db:27017/test" depends_on: - db ports: - "3000:3000" db: image: mongo

Levantar servicios

docker-compose up

Revisamos los efectos generados por docker-compose, es decir que contenedores fueron levantados y sus recursos

docker ps -a
CONTAINER ID   IMAGE                               COMMAND                  CREATED       STATUS                    PORTS                                       NAMES
d06a807d84de   platziapp                           "docker-entrypoint.s…"   3 hours ago   Up 3 hours                0.0.0.0:3000->3000/tcp, :::3000->3000/tcp   014-docker_app_1
49cfe2494613   mongo                               "docker-entrypoint.s…"   3 hours ago   Up 3 hours                27017/tcp                                   014-docker_db_1

docker network list
NETWORK ID     NAME                 DRIVER    SCOPE
309a7fc12128   014-docker_default   bridge    local

docker network inspect 014-docker_default
[
    {
        "Name": "014-docker_default",
        "Id": "309a7fc1212892eae515a836ad8b33401f0d57f06f0c6d7fe45f04e6bfd2d01f",
        "Created": "2021-08-24T19:30:30.013063875Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": true,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "49cfe24946135f9af38daa4159ef741640fd2fa165062217f5fb30b3d1f1294b": {
                "Name": "014-docker_db_1",
                "EndpointID": "b0f6c0b06089066c692ed603b487679cc353f367f865baaf9fb7acca7f2ddebb",
                "MacAddress": "02:42:ac:12:00:02",
                "IPv4Address": "172.18.0.2/16",
                "IPv6Address": ""
            },
            "d06a807d84deade968aacd66acfec128e69b80afd63bdbab7cc5cc8683aec846": {
                "Name": "014-docker_app_1",
                "EndpointID": "0860f7b81488d18dc9e789539aed1cebad541e91c8e28be098dabcdaca97ff3d",
                "MacAddress": "02:42:ac:12:00:03",
                "IPv4Address": "172.18.0.3/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {
            "com.docker.compose.network": "default",
            "com.docker.compose.project": "014-docker",
            "com.docker.compose.version": "1.29.2"
        }
    }
]

Como ejecutamos el up attached, podemos detener los contenedores con Ctrl+C

Ctrl+C
CHECKPOINT_PROGRESS] saving checkpoint snapshot min: 67, snapshot max: 67 snapshot count: 0, oldest timestamp: (0, 0) , meta checkpoint timestamp: (0, 0) base write gen: 1"}}
db_1   | {"t":{"$date":"2021-08-24T22:28:34.729+00:00"},"s":"I",  "c":"STORAGE",  "id":22430,   "ctx":"Checkpointer","msg":"WiredTiger message","attr":{"message":"[1629844114:729347][1:0x7fd48afab700], WT_SESSION.checkpoint: [WT_VERB_
CHECKPOINT_PROGRESS] saving checkpoint snapshot min: 68, snapshot max: 68 snapshot count: 0, oldest timestamp: (0, 0) , meta checkpoint timestamp: (0, 0) base write gen: 1"}}
db_1   | {"t":{"$date":"2021-08-24T22:29:34.769+00:00"},"s":"I",  "c":"STORAGE",  "id":22430,   "ctx":"Checkpointer","msg":"WiredTiger message","attr":{"message":"[1629844174:769250][1:0x7fd48afab700], WT_SESSION.checkpoint: [WT_VERB_
CHECKPOINT_PROGRESS] saving checkpoint snapshot min: 69, snapshot max: 69 snapshot count: 0, oldest timestamp: (0, 0) , meta checkpoint timestamp: (0, 0) base write gen: 1"}}
Gracefully stopping... (press Ctrl+C again to force)
Stopping 014-docker_app_1 ... done
Stopping 014-docker_db_1  ... done

docker ps -a
CONTAINER ID   IMAGE                               COMMAND                  CREATED       STATUS                        PORTS                               NAMES
d06a807d84de   platziapp                           "docker-entrypoint.s…"   3 hours ago   Exited (137) 17 seconds ago                                       014-docker_app_1
49cfe2494613   mongo                               "docker-entrypoint.s…"   3 hours ago   Exited (0) 16 seconds ago                                         014-docker_db_1

Es posible correr los comandos de docker sin necesidad de siempre estar en el directorio donde se encuentran el archivo docker-compose.yml, lo vamos a hacer para ver como vuelve a levantar en modo dettached los contenedores

cd ../../

docker-compose -f platzi/014-docker/docker-compose.yml up -d
Starting 014-docker_db_1 ... done
Starting 014-docker_app_1 ... done

docker ps -a
CONTAINER ID   IMAGE                               COMMAND                  CREATED       STATUS                    PORTS                                       NAMES
d06a807d84de   platziapp                           "docker-entrypoint.s…"   3 hours ago   Up 5 minutes              0.0.0.0:3000->3000/tcp, :::3000->3000/tcp   014-docker_app_1
49cfe2494613   mongo                               "docker-entrypoint.s…"   3 hours ago   Up 5 minutes              27017/tcp                                   014-docker_db_1

Mirar los logs

De todos los contenedores

docker-compose logs

De un solo contenedor y con seguimiento

docker-compose logs -f app

Conseguir un shell en un contenedor

docker-compose exec app bash

Detener los servicios

docker-compose stop

Detener y eliminar los servicios

docker-compose down
Stopping 014-docker_app_1 ... done
Stopping 014-docker_db_1  ... done
Removing 014-docker_app_1 ... done
Removing 014-docker_db_1  ... done
Removing network 014-docker_default

Docker compose y build

Modificamos el Dockerfile para tener capas, una base para las dependencias y otra para nuestro código

FROM node:12 COPY ["package.json", "package-lock.json", "/usr/src/"] WORKDIR /usr/src RUN npm install EXPOSE 3000 COPY ["index.js","/usr/src/index.js"] CMD ["node", "index.js"]

De igual manera indicamos en el docker-compose.yml que para app se tiene que construir una imagen y no tomar una.

version: "3.8" services: app: build: . environment: MONGO_URL: "mongodb://db:27017/test" depends_on: - db ports: - "3000:3000" db: image: mongo

Ahora si le pedimos a docker compose hacer el build

docker compose build
db uses an image, skipping
Building app
[+] Building 8.4s (11/11) FINISHED
 => [internal] load build definition from Dockerfile                                                                                                                                                                                 0.0s
 => => transferring dockerfile: 210B                                                                                                                                                                                                 0.0s
 => [internal] load .dockerignore                                                                                                                                                                                                    0.0s
 => => transferring context: 133B                                                                                                                                                                                                    0.0s
 => [internal] load metadata for docker.io/library/node:12                                                                                                                                                                           1.2s
 => [auth] library/node:pull token for registry-1.docker.io                                                                                                                                                                          0.0s
 => [internal] load build context                                                                                                                                                                                                    0.0s
 => => transferring context: 103.03kB                                                                                                                                                                                                0.0s
 => CACHED [1/5] FROM docker.io/library/node:12@sha256:61748e56917a2f65621d68c9d9497c7bc420551a29f4be3543a373665eafd6e3                                                                                                              0.0s
 => [2/5] COPY [package.json, package-lock.json, /usr/src/]                                                                                                                                                                          0.1s
 => [3/5] WORKDIR /usr/src                                                                                                                                                                                                           0.0s
 => [4/5] RUN npm install                                                                                                                                                                                                            6.4s
 => [5/5] COPY [index.js,/usr/src/]                                                                                                                                                                                                  0.0s
 => exporting to image                                                                                                                                                                                                               0.5s
 => => exporting layers                                                                                                                                                                                                              0.4s
 => => writing image sha256:e10443abd65d82d5404bba0662e48fd99c8e0769a7f0285901677fb80dc7a805                                                                                                                                         0.0s
 => => naming to docker.io/library/014-docker_app

Lo anterior crea una imagen y la nombre de forma automática de acuerdo al nombre del directorio

docker image list
REPOSITORY               TAG                       IMAGE ID       CREATED        SIZE
014-docker_app           latest                    0fc3e2326451   25 hours ago   931MB

Configurando como entorno de desarrollo con bind mounts para que podamos cambiar en caliente los archivos

La idea sería poder levantar un entorno más adecuado para desarrollo, destacando
No lleva image, indica que se tiene que hacer un build
En el caso de bind mounts, se pueden indicar mediante volumes
En este Dockerfile se monta el contexto pero ignorando lo que está en /usr/src/node_modules
El cambio en el command es para que node monitore cambios en index.js y reinicie el servicio cuando se requiera (por desgracia no funciona correctamente en windows)

version: "3.8" services: app: build: . environment: MONGO_URL: "mongodb://db:27017/test" depends_on: - db ports: - "3000:3000" volumes: - .:/usr/src - /usr/src/node_modules command: npx nodemon index.js db: image: mongo

Normalmente ocurre que queremos usar configuraciones diferentes para nuestro entorno de desarrollo , pero no queremos modificar el docker-compose.yml original.
Para ello contamos con el archivo docker-compose.override.yml
Docker va a hacer una mezcla de las directivas en ambos archivos.
Ejemplo de docker-compose.override.yml

version: "3.8" services: app: image: my-app-image
docker compose build
[+] Building 1.8s (10/10) FINISHED
 => [internal] load build definition from Dockerfile                                                                                                                                                                  0.0s
 => => transferring dockerfile: 32B                                                                                                                                                                                   0.0s
 => [internal] load .dockerignore                                                                                                                                                                                     0.0s
 => => transferring context: 34B                                                                                                                                                                                      0.0s
 => [internal] load metadata for docker.io/library/node:12                                                                                                                                                            1.7s
 => [internal] load build context                                                                                                                                                                                     0.0s
 => => transferring context: 100B                                                                                                                                                                                     0.0s
 => [1/5] FROM docker.io/library/node:12@sha256:61748e56917a2f65621d68c9d9497c7bc420551a29f4be3543a373665eafd6e3                                                                                                      0.0s
 => CACHED [2/5] COPY [package.json, package-lock.json, /usr/src/]                                                                                                                                                    0.0s
 => CACHED [3/5] WORKDIR /usr/src                                                                                                                                                                                     0.0s
 => CACHED [4/5] RUN npm install                                                                                                                                                                                      0.0s
 => CACHED [5/5] COPY [index.js,/usr/src/index.js]                                                                                                                                                                    0.0s
 => exporting to image                                                                                                                                                                                                0.0s
 => => exporting layers                                                                                                                                                                                               0.0s
 => => writing image sha256:c5ce3d88b9c0b45d95aa2973011e88a90f8fe1098c322bf48187506c37deb479                                                                                                                          0.0s
 => => naming to docker.io/library/my-app-image

docker image ls
REPOSITORY               TAG                       IMAGE ID       CREATED         SIZE
my-app-image             latest                    c5ce3d88b9c0   2 minutes ago   931MB

Como se puede ver, se combinan ambos, y las directivas del .override sobre escriben las del docker-compose.yml

Escalando un servicio

La idea es por ejemplo levantar dos instancias de app

docker compose up -d --scale app=2
[+] Running 2/4
 - Network 014-docker_default  Created                                                                                                                                                                                0.8s
 - Container 014-docker_db_1   Started                                                                                                                                                                                3.6s
 - Container 014-docker_app_2  Starting                                                                                                                                                                               4.7s
 - Container 014-docker_app_1  Starting                                                                                                                                                                               4.7s
Error response from daemon: driver failed programming external connectivity on endpoint 014-docker_app_2 (5564ae0d218fa49057b0926045db8ac79965d6c047716e9ba28bb1baeff4fa69): Bind for 0.0.0.0:3000 failed: port is already
allocated

docker ps -a
CONTAINER ID   IMAGE                               COMMAND                  CREATED          STATUS                    PORTS                                       NAMES
042772e51f1b   c5ce3d88b9c0                        "docker-entrypoint.s…"   20 seconds ago   Created                                                               014-docker_app_2
00e18fc49cf6   c5ce3d88b9c0                        "docker-entrypoint.s…"   21 seconds ago   Up 15 seconds             0.0.0.0:3000->3000/tcp, :::3000->3000/tcp   014-docker_app_1
37f6f651df2a   269b735e72cb                        "docker-entrypoint.s…"   21 seconds ago   Up 17 seconds             27017/tcp                                   014-docker_db_1

Como se puede ver la segunda instancia genera un error al no poder asociarse al puerto 3000, y no arranca, aparece su status como created

Para especificar un rango de puertos que se puedan mapear en el afitrion podemos hacerlo en el docker-compose.yml mediante un rango de puertos, ej. del 2000 al 2001

version: "3.8" services: app: build: . environment: MONGO_URL: "mongodb://db:27017/test" depends_on: - db ports: - "2000-2001:3000" volumes: - .:/usr/src - /usr/src/node_modules command: npx nodemon index.js db: image: mongo
docker compose up -d --scale app=2
Creating network "014-docker_default" with the default driver
Creating 014-docker_db_1 ... done
WARNING: The "app" service specifies a port on the host. If multiple containers for this service are created on a single host, the port will clash.
Creating 014-docker_app_1 ... done
Creating 014-docker_app_2 ... done

docker ps -a
CONTAINER ID   IMAGE                               COMMAND                  CREATED          STATUS                    PORTS                                       NAMES
125bfdf7c244   my-app-image                        "docker-entrypoint.s…"   9 seconds ago    Up 6 seconds              0.0.0.0:2001->3000/tcp, :::2001->3000/tcp   014-docker_app_2
e90532bb1187   my-app-image                        "docker-entrypoint.s…"   10 seconds ago   Up 7 seconds              0.0.0.0:2000->3000/tcp, :::2000->3000/tcp   014-docker_app_1
52778a603da6   mongo                               "docker-entrypoint.s…"   11 seconds ago   Up 10 seconds             27017/tcp                                   014-docker_db_1

Aspectos finos

Shell form vs Exec form and signaling

Este tema es para observar la diferencia en la forma de especificar el CMD en el Dockerfile

loop.sh

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

Dockerfile.shell-form

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

Dockerfile.exec-form

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

Construimos las imagenes necesarias

docker build -f Dockerfile.shell-form -t loop-shell-form .
[+] Building 0.2s (7/7) FINISHED

docker build -f Dockerfile.exec-form -t loop-exec-form .
[+] Building 0.1s (7/7) FINISHED

docker image list
REPOSITORY               TAG                       IMAGE ID       CREATED             SIZE
loop-exec-form           latest                    c433c20457b1   49 seconds ago      72.8MB
loop-shell-form          latest                    85af1449a747   49 seconds ago      72.8MB

docker run -d --name pillo-shell-form loop-shell-form
10641c52b1119cbc064ba7385fd06f433fb049d2344070479697a60b6663fa68

docker run -d --name pillo-exec-form loop-exec-form
2ea8acd3565b445892476ae1da7488cf64735225f6baccfd14b1ffb93dcd9621

Notar la diferencia en el arbol de procesos entre ambas formas:

docker exec -it pillo-shell-form ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 03:25 ?        00:00:00 /bin/sh -c /loop.sh
root         8     1 99 03:25 ?        00:02:24 bash /loop.sh
root        18     0  0 03:27 pts/0    00:00:00 ps -ef

docker exec -it pillo-exec-form ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0 99 03:28 ?        00:01:35 bash /loop.sh
root         7     0  0 03:29 pts/0    00:00:00 ps -ef

Paramos los contenedores, notar el exit code en cada caso:

docker stop pillo-exec-form

docker stop pillo-shell-form

docker ps -a
CONTAINER ID   IMAGE                               COMMAND                  CREATED         STATUS                        PORTS                               NAMES
2ea8acd3565b   loop-exec-form                      "/loop.sh"               3 minutes ago   Exited (0) 42 seconds ago                                         pillo-exec-form
10641c52b111   loop-shell-form                     "/bin/sh -c /loop.sh"    6 minutes ago   Exited (137) 12 seconds ago                                       pillo-shell-form

Docker envia la señal SIGTERM al proceso con PID 1, pero este no necesariamente la reenvia a los procesos hijos , lo que se requiere para un graceful shutdown.

Haciendo limpieza

docker rm pillo-exec-form pillo-shell-form
docker image rm loop-exec-form loop-shell-form

Contenedores ejecutables - ENTRYPOINT y CMD

Vamos a construir una imagen que ejecute el comando ping en un contenedor

Para ello vamos a generar una imagen con el siguiente Dockerfile

FROM alpine:3 ENTRYPOINT ["ping","-c10"] CMD ["127.0.0.1"]

Generamos la imagen alpine-ping

docker build -t alpine-ping .
[+] Building 3.0s (6/6) FINISHED

Intentemos ejecutar la imagen y obtener un bash como normalmente hacemos

docker run --rm alpine-ping bash
ping: bad address 'bash'

Como puede verse se trata de hacer ping a la dirección bash, es decir docker ejecuta por defecto
el comando especificado en el ENTRYPOINT como base
y añade como parametro lo que se pasa como CMD en la llamada

Ejecutamos la imagen para probar que se hace ping 10 veces (-c10) a 127.0.0.1 (localhost)

docker run --rm alpine-ping
PING 127.0.0.1 (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: seq=0 ttl=64 time=0.047 ms
64 bytes from 127.0.0.1: seq=1 ttl=64 time=0.110 ms
64 bytes from 127.0.0.1: seq=2 ttl=64 time=0.111 ms
64 bytes from 127.0.0.1: seq=3 ttl=64 time=0.056 ms
64 bytes from 127.0.0.1: seq=4 ttl=64 time=0.112 ms
64 bytes from 127.0.0.1: seq=5 ttl=64 time=0.113 ms
64 bytes from 127.0.0.1: seq=6 ttl=64 time=0.116 ms
64 bytes from 127.0.0.1: seq=7 ttl=64 time=0.111 ms
64 bytes from 127.0.0.1: seq=8 ttl=64 time=0.087 ms
64 bytes from 127.0.0.1: seq=9 ttl=64 time=0.111 ms

--- 127.0.0.1 ping statistics ---
10 packets transmitted, 10 packets received, 0% packet loss
round-trip min/avg/max = 0.047/0.097/0.116 ms

Debido a lo anterior es posible pasar el nombre del dominio al que queremos hacer ping como parametro

docker run --rm alpine-ping google.com
PING google.com (216.58.195.238): 56 data bytes
64 bytes from 216.58.195.238: seq=0 ttl=37 time=13.399 ms
64 bytes from 216.58.195.238: seq=1 ttl=37 time=11.562 ms
64 bytes from 216.58.195.238: seq=2 ttl=37 time=18.470 ms
64 bytes from 216.58.195.238: seq=3 ttl=37 time=11.932 ms
64 bytes from 216.58.195.238: seq=4 ttl=37 time=17.515 ms
64 bytes from 216.58.195.238: seq=5 ttl=37 time=12.425 ms
64 bytes from 216.58.195.238: seq=6 ttl=37 time=13.718 ms
64 bytes from 216.58.195.238: seq=7 ttl=37 time=14.281 ms
64 bytes from 216.58.195.238: seq=8 ttl=37 time=9.918 ms
64 bytes from 216.58.195.238: seq=9 ttl=37 time=9.906 ms

--- google.com ping statistics ---
10 packets transmitted, 10 packets received, 0% packet loss
round-trip min/avg/max = 9.906/13.312/18.470 ms

Build context y .dockerignore

Cuando construimos una imagen a partir del Dockerfile, el último argumento es la ruta para el contexto de build.
Si en el Dockerfile hacemos COPY [".","/destination/path"], se terminan copiando todos los archivos que se encuentran en la ruta del contexto de build.

Podemos usar el archivo .dockerignore para especificar que archivos del contexto no se deben incluir al hacer COPY, es un concepto similar al .gitignore

Multi-stage build

--TODO explicar esta parte ./build/ dev.Dockerfile prod.Dockerfile # especificar el docker file a utilizar docker build -t prodapp -f build/prod.Dockerfile prod.Dockerfile ================ FROM node:12 as builder COPY ["package.json", "package-lock.json", "/usr/src/"] WORKDIR /usr/src RUN npm install --only=production COPY [".", "/usr/src/"] RUN npm install --only=development RUN npm run test # Productive image FROM node:12 COPY ["package.json", "package-lock.json", "/usr/src/"] WORKDIR /usr/src RUN npm install --only=production COPY --from=builder ["/usr/src/index.js", "/usr/src/"] EXPOSE 3000 CMD ["node", "index.js"]