Saltar al contenido
sudoquest@root
Docker

Imágenes

El nombre y la etiqueta de una imagen, sus capas, las etiquetas propias, borrar sin romper nada y llevarlas a un servidor sin internet.

25 min de lectura

En el tema anterior arrancabas contenedores de nginx o de debian sin preguntarte mucho de dónde salían. Salen de una imagen: la plantilla con los archivos y el programa. En este tema la imagen es la protagonista: cómo se llama, qué versión es, qué lleva dentro, cómo se le pone un nombre propio, cómo se borra sin dejar el servidor a medias y cómo se lleva a una máquina que no sale a internet.

Los ejercicios pasan en el servidor de pruebas de El Diario, un periódico digital. Su web, su gestor de contenidos y su análisis de visitas son imágenes propias (periodico/...), hechas sobre las oficiales.

El nombre de una imagen

Una imagen se nombra con su repositorio y su etiqueta, separados por dos puntos: nginx:1.27-alpine es el repositorio nginx con la etiqueta 1.27-alpine (nginx 1.27, sobre Alpine Linux). Lo completo sería docker.io/library/nginx:1.27-alpine: el registro (Docker Hub, docker.io), el espacio de las imágenes oficiales (library) y lo demás. Si no pones etiqueta, Docker pone latest.

docker pull baja una imagen sin arrancar nada:

tecnico@srv-pruebas:~$ docker pull nginx:1.27-alpine
1.27-alpine: Pulling from library/nginx
f18232174bc9: Pull complete
61ca4f733c80: Pull complete
b464cfdf2a63: Pull complete
d7e507024086: Pull complete
81bd8ed7ec67: Pull complete
197eb75867ef: Pull complete
34a64644b756: Pull complete
39c2ddfd6010: Pull complete
Digest: sha256:65645c7bb6a0661892a8b03b89d0743208a18dd2f3f17a54ef4b76fb8e2f2a10
Status: Downloaded newer image for nginx:1.27-alpine
docker.io/library/nginx:1.27-alpine

Cada línea con un código es una capa (enseguida vemos qué son). El digest es la huella de la imagen: si alguien cambia un solo byte, el digest cambia. Si la vuelves a pedir y ya la tienes al día, no baja nada:

tecnico@srv-pruebas:~$ docker pull nginx:1.27-alpine
1.27-alpine: Pulling from library/nginx
Digest: sha256:65645c7bb6a0661892a8b03b89d0743208a18dd2f3f17a54ef4b76fb8e2f2a10
Status: Image is up to date for nginx:1.27-alpine
docker.io/library/nginx:1.27-alpine

Si la etiqueta no existe, el registro lo dice; y si el repositorio no existe (o es privado), no te deja bajarlo:

tecnico@srv-pruebas:~$ docker pull nginx:9.99
Error response from daemon: manifest for nginx:9.99 not found: manifest unknown: manifest unknown

Ejercicio

Una versión fija

La hemeroteca del periódico tiene que ir siempre con la misma versión de nginx: la 1.27, la de Alpine (nginx:1.27-alpine). Bájala y arranca con ella el contenedor hemeroteca.

Preparando la máquina…

Las imágenes que tienes: docker images

tecnico@srv-pruebas:~$ docker images
REPOSITORY   TAG           IMAGE ID       CREATED         SIZE
python       3.11-slim     d3846bce6e97   5 days ago      133MB
python       3.12-slim     414a398990af   5 days ago      128MB
nginx        1.27-alpine   6769dc3a703c   17 months ago   48.2MB
  • IMAGE ID: su identificador. Dos etiquetas con el mismo id son la misma imagen.
  • CREATED: cuándo la hizo quien la publica (no cuándo la bajaste tú).
  • SIZE: lo que ocupa descomprimida.

Con un nombre detrás, solo las de ese repositorio (docker images python); con --digests, el digest de cada una; con -q, solo los ids.

Por dentro: las capas

Una imagen no es un bloque: es una pila de capas, cada una con los cambios de un paso de su construcción (instalar algo, copiar un archivo). Las capas se comparten: si dos imágenes empiezan igual, esa parte se baja y se guarda una sola vez. Mira lo que pasa al bajar la segunda versión de Python:

tecnico@srv-pruebas:~$ docker pull python:3.11-slim
3.11-slim: Pulling from library/python
6b37362b3da7: Already exists
f64163c1b799: Pull complete
295f1967d045: Pull complete
48355dfcbf9e: Pull complete
Digest: sha256:bab1b7ef4b450c81002278d035eff85ebe394ae94df904f7a3ba14f7e16e487b
Status: Downloaded newer image for python:3.11-slim
docker.io/library/python:3.11-slim

La primera capa (el Debian de la base) ya estaba: Already exists. docker history enseña los pasos de una imagen, del último al primero, con lo que ocupa cada uno:

tecnico@srv-pruebas:~$ docker history python:3.12-slim
IMAGE          CREATED       CREATED BY                                      SIZE      COMMENT
414a398990af   5 days ago    CMD ["python3"]                                 0B        buildkit.dockerfile.v0
<missing>      5 days ago    RUN /bin/sh -c set -eux;  for src in idle3 p…   0B        buildkit.dockerfile.v0
<missing>      5 days ago    RUN /bin/sh -c set -eux;   savedAptMark="$(a…   36.8MB    buildkit.dockerfile.v0
<missing>      5 days ago    ENV PYTHON_SHA256=c2c4321961fab0fb999d66e0ce…   0B        buildkit.dockerfile.v0
<missing>      5 days ago    ENV PYTHON_VERSION=3.12.15                      0B        buildkit.dockerfile.v0
<missing>      5 days ago    ENV GPG_KEY=7169605F62C751356D054A26A821E680…   0B        buildkit.dockerfile.v0
<missing>      5 days ago    RUN /bin/sh -c set -eux;  apt-get update;  a…   12MB      buildkit.dockerfile.v0
<missing>      5 days ago    ENV LANG=C.UTF-8                                0B        buildkit.dockerfile.v0
<missing>      5 days ago    ENV PATH=/usr/local/bin:/usr/local/sbin:/usr…   0B        buildkit.dockerfile.v0
<missing>      2 weeks ago   # debian.sh --arch 'amd64' out/ 'trixie' '@1…   78.8MB    debuerreotype 0.17

Los pasos de 0B no añaden archivos: cambian la configuración (ENV, CMD). <missing> no es un error: esas capas no tienen un id propio en tu máquina (se bajaron como parte de la imagen). Lo que trae la imagen también está en docker image inspect, que da su configuración entera en JSON; con -f eliges un dato:

tecnico@srv-pruebas:~$ docker image inspect -f '{{.Config.Env}}' python:3.12-slim
[PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin LANG=C.UTF-8 GPG_KEY=7169605F62C751356D054A26A821E680E5FA6305 PYTHON_VERSION=3.12.15 PYTHON_SHA256=c2c4321961fab0fb999d66e0cecf521c2ab3994c7992873ea99e306c1094fd5a]

Y siempre queda preguntárselo al programa, en un contenedor de usar y tirar:

tecnico@srv-pruebas:~$ docker run --rm python:3.12-slim python --version
Python 3.12.15

Ejercicio

¿Qué trae esta imagen?

El equipo de datos pregunta con qué versión de Python viene su imagen, periodico/analitica:1.0. Averígualo y apúntala (solo el número, como 3.10.4) en python.txt, en tu carpeta.

Preparando la máquina…

Etiquetas propias: docker tag

docker tag le pone otro nombre a una imagen que ya tienes. No copia nada: es la misma imagen con dos etiquetas (fíjate en el IMAGE ID):

tecnico@srv-pruebas:~$ docker tag nginx:1.27-alpine periodico/web:2.3
tecnico@srv-pruebas:~$ docker images
REPOSITORY      TAG           IMAGE ID       CREATED         SIZE
python          3.11-slim     d3846bce6e97   5 days ago      133MB
python          3.12-slim     414a398990af   5 days ago      128MB
nginx           1.27-alpine   6769dc3a703c   17 months ago   48.2MB
periodico/web   2.3           6769dc3a703c   17 months ago   48.2MB

Es lo que hacen los equipos para ordenarse: las versiones de lo suyo (periodico/web:2.3, 2.4…) y etiquetas con significado, como estable o produccion, que se mueven a la versión que toque. Una etiqueta solo puede estar en una imagen: si la pones en otra, se va de la primera.

Ejercicio

Etiquetas propias

La versión 2.3 de la web del periódico es la buena. Ponle también la etiqueta estable (periodico/web:estable), para que todos sepan cuál usar.

Preparando la máquina…

Borrar imágenes: docker rmi

docker rmi (o docker image rm) borra una etiqueta. Si a la imagen le quedan otras, solo se quita esa (Untagged); si era la última, se borra la imagen y las capas que nadie más usa (Deleted):

tecnico@srv-pruebas:~$ docker rmi periodico/web:2.3
Untagged: periodico/web:2.3

Una imagen que usa algún contenedor no se borra, aunque el contenedor esté parado:

tecnico@srv-pruebas:~$ docker rmi nginx:1.26-alpine
Error response from daemon: conflict: unable to remove repository reference "nginx:1.26-alpine" (must force) - container 9328e219070e is using its referenced image 42ce3d3585d4

Primero se borra el contenedor (docker rm) y luego la imagen:

tecnico@srv-pruebas:~$ docker rmi nginx:1.26-alpine
Untagged: nginx:1.26-alpine
Untagged: nginx@sha256:1eadbb07820339e8bbfed18c771691970baee292ec4ab2558f1453d26153e22d
Deleted: sha256:42ce3d3585d47518cb0c1ef4bd3d8a65c1edfcbbfd423eda86990a83f378b111
Deleted: sha256:80b3029b6869e6a8366b719476de8cf8d375befde2d48ab75b0797d929a46da3
…

Ejercicio

No me deja borrarla

Sobra la versión vieja del gestor de contenidos, periodico/cms:3.0. Pero docker rmi da error: hay un contenedor parado que la usa (cms-viejo). Bórralos los dos.

Preparando la máquina…

Las colgadas y la limpieza

Cuando una etiqueta pasa a otra imagen (al bajar una versión nueva de latest, al cargar una con el mismo nombre), la vieja se queda sin etiqueta: es una imagen colgada (dangling), y sale como <none>:

tecnico@srv-pruebas:~$ docker images -f dangling=true
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
<none>       <none>    83b2b6703a62   12 months ago   7.39MB
<none>       <none>    42ce3d3585d4   20 months ago   47.2MB

Nadie las puede pedir por su nombre: ocupan disco sin servir para nada. docker image prune las borra (pregunta antes; con -f, sin preguntar):

tecnico@srv-pruebas:~$ docker image prune -f
Deleted Images:
deleted: sha256:83b2b6703a620bf2e001ab57f7adc414d891787b3c59859b1b62909e48dd2242
deleted: sha256:0b44b2151d78267ab6f2c76208c3be18688f49b2b0afd6852a9533f2cce121c5
deleted: sha256:42ce3d3585d47518cb0c1ef4bd3d8a65c1edfcbbfd423eda86990a83f378b111
…

Total reclaimed space: 54.58MB

docker system df dice cuánto ocupa todo lo de Docker, y cuánto se podría liberar. Cuenta cada capa una vez, aunque la compartan varias imágenes:

tecnico@srv-pruebas:~$ docker system df
TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          4         0         56.02MB   56.02MB (100%)
Containers      0         0         0B        0B
Local Volumes   0         0         0B        0B
Build Cache     0         0         0B        0B

Ejercicio

Imágenes colgadas

El disco del servidor de pruebas se está llenando. Hay imágenes colgadas (<none>, sin etiqueta): bórralas, y solo esas. Las que tienen etiqueta se usan, aunque ahora no haya ningún contenedor con ellas.

Preparando la máquina…

Sin internet: docker save y docker load

Muchos servidores no salen a internet, por seguridad. Ahí docker pull no llega al registro:

tecnico@srv-diario:~$ docker pull nginx:1.27-alpine
Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)

Las imágenes viajan entonces en un archivo. En una máquina que sí la tiene, docker save guarda la imagen (con todas sus capas y su etiqueta) en un .tar:

tecnico@srv-pruebas:~$ docker save -o web.tar nginx:1.27-alpine

Se copia el archivo al otro servidor (con scp, un disco…) y allí docker load la carga, capa a capa, con su etiqueta:

tecnico@srv-diario:~$ docker load -i web.tar
08000c18d16d: Loading layer  8.121MB/8.121MB
d71eae0084c1: Loading layer  4.511MB/4.511MB
c56f134d3805: Loading layer  3.584kB/3.584kB
e244aa659f61: Loading layer  4.608kB/4.608kB
b8d7d1d22634: Loading layer   2.56kB/2.56kB
811a4dbbf4a5: Loading layer   5.12kB/5.12kB
947e805a4ac7: Loading layer  7.168kB/7.168kB
0d853d50b128: Loading layer  36.98MB/36.98MB
Loaded image: nginx:1.27-alpine

Es la misma imagen, con el mismo id. Lo único que no lleva es el digest del registro (no vino de él).

Ejercicio

Llega en un archivo

Este servidor no sale a internet: docker pull no sirve. El proveedor ha dejado la versión nueva del gestor de contenidos en /srv/entregas/cms-3.1.tar. Cárgala en Docker.

Preparando la máquina…

Los registros

Docker Hub es el registro de serie, pero no el único: las empresas tienen los suyos (el de GitHub, el de su nube, uno propio). En Docker Hub hay dos clases de repositorios:

  • Los oficiales (nginx, python, postgres): sin nombre delante, mantenidos por Docker y por quien hace el programa.
  • Los de usuarios y empresas (ana/app, periodico/web): con su nombre delante. Pueden ser públicos o privados.

Para subir una imagen (docker push) hay que entrar antes en el registro (docker login) y tener permiso en ese repositorio; si no, el registro lo rechaza (denied: requested access to the resource is denied). Las imágenes propias de El Diario no están en ningún registro público: solo en sus servidores.

Resumen

Orden Para qué
docker pull repo:etiqueta bajar una imagen (sin etiqueta, latest)
docker images las que tienes (-f dangling=true: las colgadas)
docker history imagen sus capas, con lo que ocupa cada paso
docker image inspect imagen su configuración (-f para un dato)
docker tag origen destino ponerle otra etiqueta (es la misma imagen)
docker rmi imagen quitar una etiqueta, y la imagen si era la última
docker image prune borrar las colgadas (-a: todas las que no se usan)
docker system df lo que ocupa Docker
docker save -o x.tar imagen / docker load -i x.tar llevar imágenes en un archivo