Cerraste el módulo 1 con las herramientas en la mano y el problema medido: quince pasos manuales para poner en marcha Aurora Libros. El módulo 2 empieza a demolerlos, y lo hace desde el principio de la cadena: de dónde salen las imágenes. Hasta ahora has escrito docker pull node:22-alpine o docker run nginx:alpine sin preguntarte demasiado quién está al otro lado, quién publicó esa imagen ni por qué deberías fiarte de ella. En esta lección abres esa caja. Vas a entender qué es exactamente un registro y qué es un repositorio dentro de él, te vas a autenticar con docker login y verás dónde quedan tus credenciales (con una sorpresa desagradable incluida), aprenderás a distinguir una imagen oficial de una imagen que alguien subió un martes de 2019 y nunca volvió a tocar, conocerás los límites de descarga que te pueden cortar una build en el peor momento, y montarás tu propio registro en un contenedor para ver que el mecanismo no tiene ninguna magia. Al final elegirás el repositorio donde vivirá la imagen de aurora-api durante el resto del curso.
Contenido
- Registro, repositorio y etiqueta: las tres capas del almacén
- Docker Hub: cuenta e interfaz
docker login,docker logouty dónde quedan tus credenciales- Tipos de imagen en Docker Hub y cómo evaluar su confianza
- Límites de descarga: anónimo frente a autenticado
- Buscar imágenes desde la terminal con
docker search - Repositorios públicos y privados
- Registros alternativos a Docker Hub
- Tu propio registro en un contenedor
- El repositorio de Aurora Libros
- Registro, repositorio y etiqueta: las tres capas del almacén
En la lección 01-05 aprendiste a leer un nombre completo de imagen:
Ahora toca entender qué hay detrás de cada tramo, porque son tres cosas distintas que la gente confunde constantemente al hablar.
- Un registro (registry) es un servicio: un servidor con una API HTTP que almacena y sirve imágenes. Docker Hub es un registro.
ghcr.ioes otro. El que montarás en el apartado 9 en tu portátil también lo es. Un registro contiene muchos repositorios. - Un repositorio (repository) es una colección de imágenes relacionadas entre sí, que comparten nombre y se distinguen por la etiqueta.
library/nginxes un repositorio: dentro vivennginx:1.27,nginx:alpine,nginx:latest… todas son "nginx", en distintas versiones o variantes. - Una etiqueta (tag) es una referencia legible que apunta a un manifiesto concreto, identificado a su vez por un digest
sha256:…inmutable.
flowchart TB
subgraph REG["Registro · docker.io (Docker Hub)"]
subgraph R1["Repositorio: library/node"]
T1["etiqueta 22-alpine"]
T2["etiqueta 22"]
T3["etiqueta latest"]
end
subgraph R2["Repositorio: auroralibros/aurora-api"]
T4["etiqueta 1.0.0"]
T5["etiqueta latest"]
end
end
D1["sha256:9f2c…<br/>manifiesto + capas"]
D2["sha256:41ab…<br/>manifiesto + capas"]
T1 --> D1
T2 --> D2
T3 --> D2
T4 --> D1
T5 --> D1
Fíjate en dos detalles del diagrama que explican comportamientos que ya has visto:
- Varias etiquetas pueden apuntar al mismo digest.
22ylatestson, casi siempre, la misma imagen con dos nombres. Por eso en la lección 01-05docker image lste mostraba dos filas con el mismo IMAGE ID: no eran dos imágenes, era una con dos referencias. Volverás a esto con detalle en la lección 02-06. - El digest es lo único inmutable. La etiqueta
22-alpineapunta hoy a un manifiesto y dentro de tres semanas puede apuntar a otro (parche de seguridad de Node, actualización de Alpine). El digestsha256:9f2c…siempre es exactamente los mismos bytes.
Una analogía útil: el registro es la biblioteca; el repositorio es la estantería dedicada a una obra; las etiquetas son las pegatinas ("primera edición", "última edición", "edición de bolsillo") pegadas a ejemplares concretos. Mover una pegatina no cambia el ejemplar; solo cambia a qué ejemplar apunta ese nombre.
El nombre completo y sus abreviaturas
Cuando escribes docker pull nginx:alpine, Docker expande el nombre así:
| Lo que escribes | Lo que Docker entiende realmente |
|---|---|
nginx |
docker.io/library/nginx:latest |
nginx:alpine |
docker.io/library/nginx:alpine |
auroralibros/aurora-api:1.0.0 |
docker.io/auroralibros/aurora-api:1.0.0 |
ghcr.io/auroralibros/aurora-api:1.0.0 |
tal cual: registro explícito |
localhost:5000/aurora-api:1.0.0 |
tal cual: registro local en el puerto 5000 |
Tres reglas que se deducen de la tabla:
- Si no pones registro, se asume Docker Hub (
docker.io). Es una decisión histórica del cliente de Docker, no un estándar; otras herramientas (Podman, por ejemplo, lección 07-05) pueden preguntarte qué registro quieres. - Si no pones usuario/organización, se asume
library/, el espacio de nombres reservado a las imágenes oficiales. Por esonginxylibrary/nginxson lo mismo, y por eso nadie más puede publicar un repositorio llamado simplementenginx. - Si no pones etiqueta, se asume
latest, con todos los peligros que ya conoces.
Puedes comprobar la primera y la segunda con un comando que ya manejas:
docker pull nginx:alpine
docker image ls nginx --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"Docker te muestra la forma corta porque el registro es el predeterminado. Si haces lo mismo con una imagen de otro registro, el nombre aparecerá completo, con ghcr.io/… delante. Esa asimetría en la salida es una pista rápida para saber de dónde viene cada imagen de tu máquina.
- Docker Hub: cuenta e interfaz
Docker Hub (hub.docker.com) es el registro público por defecto de Docker: aloja millones de repositorios y es donde han ido a buscar todas las imágenes que has descargado hasta ahora. Vas a necesitar una cuenta para dos cosas: subir la imagen de aurora-api (lección 02-06) y —más inmediato— duplicar tu cuota de descargas.
Crear la cuenta
- Entra en
https://hub.docker.comy pulsa Sign Up. - Elige un Docker ID. Piénsalo bien: será el espacio de nombres de todas tus imágenes públicas y no se puede cambiar después. En el curso usaremos
auroralibroscomo Docker ID ficticio del alumno; tú usarás el tuyo real allí donde aparezca. - Verifica el correo electrónico.
- Activa la verificación en dos pasos (Account Settings → Security). No es opcional en la práctica: tu cuenta puede acabar publicando imágenes que otros ejecutan, y una cuenta comprometida es un vector de ataque de manual.
Recorrido por la interfaz
Una vez dentro, estas son las zonas que usarás:
| Zona | Para qué sirve |
|---|---|
| Repositories | Tus repositorios: crear, ver, cambiar visibilidad, borrar |
| Explore | Buscador de imágenes públicas, con filtros por tipo (Official, Verified Publisher…) |
| Organizations | Espacios compartidos por un equipo, con permisos por miembro |
| Account Settings → Security | Contraseña, 2FA y Personal Access Tokens |
| Usage | Consumo de descargas y almacenamiento de tu cuenta |
La página de un repositorio
Abre https://hub.docker.com/_/node (la barra baja indica imagen oficial) y observa la estructura, porque es idéntica en todos los repositorios:
- Descripción: qué es la imagen, cómo se usa, variables de entorno que acepta, ejemplos. En las imágenes bien mantenidas, esta página es la documentación real del producto.
- Comando de descarga arriba a la derecha: el
docker pull nodelisto para copiar. - Pestaña Tags: el catálogo de etiquetas disponibles. Es la pestaña que más vas a usar. Para cada etiqueta te muestra el tamaño comprimido, las arquitecturas disponibles (
linux/amd64,linux/arm64…) y cuándo se actualizó por última vez. Buscar ahí "22-alpine" antes de escribirlo en unFROMte ahorra el clásicomanifest unknownpor una etiqueta que no existe. - Enlace al Dockerfile: en las imágenes serias, cada etiqueta enlaza al repositorio de GitHub donde está el Dockerfile que la construyó. Poder leer ese fichero es una de las mejores señales de confianza que existen.
Un ejercicio de dos minutos que conviene hacer ahora: entra en la pestaña Tags de node, busca 22-alpine y anota su tamaño comprimido y su fecha de actualización. Compara con 22 a secas. La diferencia de tamaño explica por qué en la lección 01-07 elegiste Alpine para aurora-api.
docker login, docker logout y dónde quedan tus credenciales
docker login, docker logout y dónde quedan tus credencialesPara subir imágenes o para descargar de repositorios privados necesitas autenticarte. El comando es directo:
Log in with your Docker ID or email address to push and pull images from Docker Hub.
Username: auroralibros
Password:
Login SucceededSin argumentos, docker login apunta a Docker Hub. Para otro registro, se le pasa como argumento:
Y para cerrar sesión:
Dónde quedan las credenciales (y por qué debe preocuparte)
Tras un login correcto, Docker guarda la credencial en ~/.docker/config.json. Míralo:
{
"auths": {
"https://index.docker.io/v1/": {
"auth": "YXVyb3JhbGlicm9zOmRja3JfcGF0X2VqZW1wbG8="
}
}
}Ese campo auth no está cifrado. Es simplemente usuario:contraseña codificado en base64, que es una codificación reversible, no un sistema de protección. Compruébalo tú mismo:
Cualquiera con acceso de lectura a tu ~/.docker/config.json —otro usuario del sistema con permisos, un proceso malicioso, una copia de seguridad mal protegida o un contenedor al que le montaste el directorio por descuido— tiene tus credenciales en claro. De hecho, Docker te avisa de ello al iniciar sesión:
WARNING! Your password will be stored unencrypted in /home/joan/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/go/credential-store/Tres medidas concretas para mitigarlo, de menor a mayor esfuerzo:
a) Usa un token de acceso personal en lugar de tu contraseña. En Docker Hub, Account Settings → Personal Access Tokens → Generate new token, con permisos mínimos (Public Repo Read-only si solo descargas, Read & Write si vas a publicar). Ventajas: si se filtra, se revoca ese token sin cambiar tu contraseña, no da acceso a la web ni a la configuración de la cuenta, y puedes tener uno por máquina. Se usa igual:
b) Evita docker login -p en scripts. Escribir la contraseña en la línea de comandos la deja en el historial del shell y en la lista de procesos. La forma correcta es por entrada estándar:
--password-stdin lee la credencial de la tubería. Es el patrón obligatorio en CI/CD (lo verás aplicado en la lección 06-02).
c) Configura un credential helper. Es un programa externo que guarda las credenciales en el almacén seguro del sistema operativo en lugar de en un fichero de texto:
| Sistema | Helper | Almacén real |
|---|---|---|
| Linux (escritorio) | docker-credential-secretservice |
Keyring de GNOME / KWallet vía D-Bus |
| Linux (servidor, sin GUI) | docker-credential-pass |
pass, respaldado por GPG |
| macOS | docker-credential-osxkeychain |
Llavero de macOS (viene con Docker Desktop) |
| Windows | docker-credential-wincred |
Administrador de credenciales de Windows |
Instalación y activación en Linux de escritorio:
Y edita ~/.docker/config.json para añadir la línea del helper:
Ahora vuelve a autenticarte:
El objeto de auths está vacío: la credencial ya no está en el fichero, sino en el llavero del sistema, y el aviso ha desaparecido. Docker la recupera del helper cada vez que la necesita.
- Tipos de imagen en Docker Hub y cómo evaluar su confianza
Descargar una imagen y ejecutarla es, en el fondo, ejecutar software de un desconocido en tu máquina. Docker Hub etiqueta los repositorios en categorías que te ayudan a decidir de quién te fías.
| Tipo | Insignia en la web | Quién la mantiene | Espacio de nombres | Cuándo usarla |
|---|---|---|---|---|
| Docker Official Image | Docker Official Image | Equipo de Docker + mantenedores designados, con Dockerfiles públicos y revisados | library/ (sin usuario: nginx, node, postgres) |
Primera opción siempre para software base: lenguajes, bases de datos, servidores web |
| Verified Publisher | Verified Publisher | La empresa propietaria del software, con identidad verificada por Docker | El de la empresa (grafana/grafana, bitnami/…) |
Cuando necesitas el producto comercial de ese fabricante |
| Sponsored OSS | Sponsored OSS | Proyecto de código abierto reconocido, patrocinado por Docker | El del proyecto | Proyectos abiertos consolidados sin imagen oficial |
| Comunidad | ninguna | Cualquier persona con una cuenta | Cualquiera | Solo tras auditarla, o nunca en producción |
Para Aurora Libros esto se traduce en algo muy concreto: node, postgres, redis y nginx son oficiales, y por eso son las cuatro bases del proyecto. No hay ninguna razón para usar algunusuario/node-22-con-todo en su lugar.
Criterios prácticos de evaluación
Las insignias no cubren todos los casos: tarde o temprano necesitarás una imagen de la comunidad. Este es el chequeo que conviene aplicar antes de escribirla en un FROM:
- Fecha de la última actualización. Está en la pestaña Tags. Una imagen que no se toca desde hace un año arrastra un año de vulnerabilidades sin parchear del sistema base. Es el criterio que más descarta.
- ¿El Dockerfile es público? Si no puedes leer cómo se construyó, estás ejecutando una caja negra. Busca el enlace a GitHub en la descripción.
- Tamaño. Una imagen de 2 GB para servir un binario de 10 MB indica que lleva dentro medio sistema operativo con herramientas que no necesitas y que amplían la superficie de ataque.
- Número y contenido de las capas. Con lo aprendido en 01-05 puedes auditar esto sin descargar nada más que la imagen:
docker pull redis:7-alpine
docker image history redis:7-alpine --no-trunc --format "table {{.Size}}\t{{.CreatedBy}}" | head -12Lo que buscas en esa salida: instrucciones comprensibles y justificables. Una capa con un curl http://…/instalador.sh | sh apuntando a un dominio desconocido es una bandera roja inmediata; significa que la imagen descarga y ejecuta código arbitrario de un tercero en tiempo de construcción.
- Descargas y estrellas, con reservas. Un millón de descargas indica que mucha gente la usa, no que sea segura; los números se inflan fácilmente con automatizaciones. Úsalo como señal débil, nunca como argumento principal.
- ¿Publica varias arquitecturas? Si trabajas en un Mac con Apple Silicon o en un servidor ARM, necesitas
linux/arm64. La pestaña Tags lo indica. Si solo hayamd64, funcionará por emulación, más lenta (los manifiestos multi-arquitectura se ven en la lección 05-05).
Todo esto es evaluación manual y previa. El análisis automático del contenido de una imagen en busca de vulnerabilidades conocidas (docker scout, escáneres en el pipeline) y la firma criptográfica de imágenes son otra disciplina, y se tratan en la lección 05-03.
- Límites de descarga: anónimo frente a autenticado
Docker Hub limita cuántas imágenes puedes descargar por unidad de tiempo. Es un límite real que corta builds en producción, y conviene conocerlo antes de que te pase.
| Situación | Límite orientativo | Cómo se identifica |
|---|---|---|
Anónimo (sin docker login) |
El más bajo de todos, contado por dirección IP | Por IP pública de salida |
| Cuenta gratuita autenticada | Sensiblemente superior al anónimo | Por cuenta de usuario |
| Cuenta de pago (Pro / Team / Business) | Muy alto o sin límite práctico | Por cuenta |
Las cifras exactas las revisa Docker periódicamente, así que consulta siempre la página oficial de precios antes de dimensionar nada. Lo que no cambia es la estructura del problema, y hay un matiz crítico: en el caso anónimo el contador es por IP. Si estás en una oficina, en una universidad o detrás del NAT de un proveedor de nube, compartes cuota con todos los demás. Una empresa entera detrás de una sola IP pública puede agotar el límite en minutos sin que nadie haya hecho nada raro.
Cuando lo superas, el error es este:
Error response from daemon: toomanyrequests: You have reached your pull rate limit.
You may increase the limit by authenticating and upgrading:
https://www.docker.com/increase-rate-limitY si ocurre a mitad de una construcción, la verás así:
ERROR: failed to solve: node:22-alpine: failed to resolve source metadata for
docker.io/library/node:22-alpine: toomanyrequests: You have reached your pull rate limit.Cómo evitarlo, por orden de eficacia:
- Autentícate siempre, incluso para descargar imágenes públicas. Un simple
docker loginen tu máquina y en los agentes de CI duplica la cuota y la aísla por cuenta en lugar de por IP. - Aprovecha la caché local. Las capas ya descargadas no vuelven a bajarse. Un agente de CI efímero que empieza siempre en blanco descarga todo cada vez: es el escenario que más consume.
- Usa un registro espejo o un caché de tirones (pull-through cache) en tu organización. El registro del apartado 9 puede configurarse justamente para eso.
- Consume las imágenes base desde otro registro. Muchas imágenes están espejadas en
ghcr.io, en ECR Public o enmcr.microsoft.com, con políticas de límite distintas.
Puedes comprobar tu situación consultando la cabecera que devuelve el registro al pedir un manifiesto, sin descargar la imagen:
TOKEN=$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:ratelimitpreview/test:pull" | grep -o '"token":"[^"]*' | cut -d'"' -f4)
curl -s --head -H "Authorization: Bearer $TOKEN" https://registry-1.docker.io/v2/ratelimitpreview/test/manifests/latest | grep -i ratelimitLa lectura es: cuota de 100 descargas en una ventana de 21 600 segundos (6 horas), de las que te quedan 76. Es una comprobación útil cuando un pipeline empieza a fallar de forma intermitente sin cambios en el código.
- Buscar imágenes desde la terminal con
docker search
docker searchNo hace falta abrir el navegador para buscar en Docker Hub:
NAME DESCRIPTION STARS OFFICIAL
postgres The PostgreSQL object-relational database ... 14231 [OK]
bitnami/postgresql Bitnami container image for PostgreSQL 202
timescale/timescaledb TimescaleDB - time-series database ... 130
postgrest/postgrest REST API for any PostgreSQL database 102
paradedb/paradedb Postgres for Search and Analytics 8Columnas: nombre del repositorio, descripción, estrellas y si es oficial. La primera fila no lleva usuario delante, la marca inequívoca de que vive en library/.
Filtros y formato disponibles:
# Solo imágenes oficiales
docker search --filter is-official=true node
# Solo repositorios con al menos 100 estrellas
docker search --filter stars=100 redis
# Con formato personalizado, reutilizando lo aprendido en 01-04
docker search nginx --limit 5 --format "table {{.Name}}\t{{.StarCount}}\t{{.IsOfficial}}"NAME STARS OFFICIAL
nginx 21045 [OK]
nginxinc/nginx-unprivileged 145
nginxproxy/nginx-proxy 130
bitnami/nginx 191
nginx/nginx-prometheus-exporter 58Dos limitaciones importantes de docker search que hay que tener claras:
- Solo busca en Docker Hub. No consulta
ghcr.ioni ningún otro registro, aunque tengas sesión abierta en ellos. Es una limitación del propio comando, no de tu configuración. - No lista etiquetas. Te dice que existe el repositorio
node, pero no qué versiones hay dentro. Para eso, la pestaña Tags de la web o una consulta a la API del registro:
curl -s "https://hub.docker.com/v2/repositories/library/node/tags?page_size=100&name=22-alpine" \
| grep -o '"name":"[^"]*' | cut -d'"' -f4Este comando consulta la API pública de Docker Hub y extrae los nombres de las etiquetas que contienen 22-alpine. Es la forma rápida de verificar desde la terminal que una etiqueta existe antes de ponerla en un FROM y descubrir el error a mitad de la build.
- Repositorios públicos y privados
Al crear un repositorio en Docker Hub eliges su visibilidad, y la decisión tiene consecuencias prácticas:
| Aspecto | Público | Privado |
|---|---|---|
| Quién puede descargar | Cualquiera, sin autenticarse | Solo cuentas con permiso, siempre autenticadas |
| Quién puede subir | Solo tú o tu organización | Solo tú o tu organización |
| Aparece en búsquedas | Sí | No |
| Coste | Gratuito e ilimitado | Cupo limitado en el plan gratuito; de pago a partir de ahí |
| Consume cuota de descargas del que la baja | Sí | Sí |
| Uso típico | Software libre, imágenes de ejemplo, formación | Aplicaciones de empresa, cualquier cosa con lógica de negocio |
La regla de oro: una imagen pública es código publicado. Todo lo que hay dentro —tu server.js, tus rutas internas, los comentarios del código, cualquier fichero que se coló en el COPY— puede ser inspeccionado y extraído por cualquiera. Esto conecta directamente con la advertencia del ejercicio 3 de la lección 01-07: las credenciales nunca entran en la imagen, y con un repositorio público esa regla pasa de ser una buena práctica a ser crítica.
Para el curso, auroralibros/aurora-api será público: así podrás compartir el enlace y ejecutarlo desde cualquier máquina sin autenticarte, que es exactamente lo que quieres demostrar. En la lección 02-06 verás cómo cambiar la visibilidad a privada y cómo dar acceso a un equipo, que es lo que haría Aurora Libros de verdad.
- Registros alternativos a Docker Hub
Docker Hub no es obligatorio. Estos son los registros que te vas a encontrar en el mundo real:
| Registro | Prefijo | Punto fuerte | Autenticación |
|---|---|---|---|
| Docker Hub | docker.io/ (implícito) |
El catálogo público más grande; imágenes oficiales | Docker ID + token |
| GitHub Container Registry | ghcr.io/ |
Integrado con el repositorio de código y con GitHub Actions; privados gratuitos ilimitados | Usuario de GitHub + personal access token con permiso write:packages |
| Amazon ECR | <id>.dkr.ecr.<región>.amazonaws.com/ |
Permisos con IAM, cercanía a las cargas de AWS, escaneo integrado | Token temporal vía aws ecr get-login-password |
| GitLab Container Registry | registry.gitlab.com/ |
Integrado con GitLab CI, un registro por proyecto | Usuario + deploy token o token de CI |
| Harbor | tu dominio | Registro autoalojado de la CNCF: replicación, políticas de retención, control de acceso fino | LDAP/OIDC o usuarios locales |
Registry (registry:2) |
tu dominio o localhost:5000 |
La implementación de referencia, mínima y sin interfaz web | Opcional (básica o token) |
La buena noticia es que todos hablan el mismo protocolo, la OCI Distribution Specification. Por eso los comandos son idénticos en todos ellos: cambia el prefijo del nombre y nada más. Un docker push a Docker Hub y uno a Harbor son la misma operación contra servidores distintos. Lo comprobarás ahora mismo.
- Tu propio registro en un contenedor
Nada aclara mejor qué es un registro que levantar uno. La imagen oficial registry:2 implementa el protocolo de distribución y cabe en unos pocos megabytes.
Unable to find image 'registry:2' locally
2: Pulling from library/registry
...
Status: Downloaded newer image for registry:2
7c9a1e4f0b2d8a3c6f1b5e2d4a8c7b9e3f1a6d2c8b4e7a9f1c3d5b8e2a4f6c9dRepasa las opciones, todas conocidas de la lección 01-06: -d lo deja en segundo plano, --name le da un nombre manejable y -p 5000:5000 publica el puerto del registro en tu máquina. Comprueba que responde:
/v2/ es la raíz de la API de distribución OCI, y _catalog lista los repositorios. Está vacío: acabas de estrenarlo. Vamos a meter algo dentro.
# 1. Traer una imagen pequeña desde Docker Hub
docker pull alpine:3.21
# 2. Crear una referencia nueva que apunte a tu registro
docker tag alpine:3.21 localhost:5000/alpine:3.21
# 3. Subirla
docker push localhost:5000/alpine:3.21The push refers to repository [localhost:5000/alpine]
6e771e15690e: Pushed
3.21: digest: sha256:21dc6063fd678b478f57c0e13f47560d0ea4eeba26dfc947b2a4f81f686b9f45 size: 528Qué acaba de pasar, paso a paso:
docker tagno copia ni duplica nada: crea un nombre nuevo para la misma imagen local.alpine:3.21ylocalhost:5000/alpine:3.21comparten IMAGE ID. Es el mecanismo que estudiarás a fondo en 02-06.- El prefijo
localhost:5000/es lo que determina el destino del push. Docker no tiene un comando "sube a este servidor": el servidor forma parte del nombre de la imagen. Esta es la idea clave de toda la lección. - La salida muestra la capa subida y el digest resultante, el mismo identificador inmutable del apartado 1.
Verifica el catálogo y las etiquetas del repositorio:
Y ahora la prueba de fuego: borra la imagen local y recupérala desde tu registro.
docker image rm localhost:5000/alpine:3.21 alpine:3.21
docker pull localhost:5000/alpine:3.21
docker run --rm localhost:5000/alpine:3.21 cat /etc/alpine-releaseHas completado un ciclo entero —pull de Docker Hub, tag, push a tu registro, borrado local, pull de tu registro, run— con los mismos comandos que usarías contra cualquier registro del mundo. La única diferencia entre tu registro de juguete y el de una multinacional es el prefijo del nombre y quién administra el servidor.
Un detalle importante que descubrirás en cuanto pases del localhost: Docker exige HTTPS para cualquier registro remoto. localhost está exento por ser local. Si intentas usar 192.168.1.50:5000 sin TLS, obtendrás:
Error response from daemon: Get "https://192.168.1.50:5000/v2/": http: server gave HTTP response to HTTPS clientLa solución correcta es poner un certificado válido delante del registro. La solución rápida para una red de laboratorio es declararlo como registro inseguro en /etc/docker/daemon.json y reiniciar el demonio:
Úsalo solo en entornos aislados y de prueba: sin TLS, cualquiera en la red puede leer y alterar las imágenes en tránsito.
Cuando termines de experimentar, limpia:
Ten en cuenta que las imágenes que subiste vivían dentro del contenedor y desaparecen con él, porque no montaste ningún volumen. Un registro real necesita almacenamiento persistente, y ese es justamente el tema de la lección 03-06.
- El repositorio de Aurora Libros
Con todo lo anterior, ya puedes tomar la decisión que gobierna el resto del módulo. La imagen de la API de Aurora Libros vivirá en:
Que escribirás en su forma corta:
Justificación de cada tramo:
| Tramo | Valor | Por qué |
|---|---|---|
| Registro | docker.io (implícito) |
Es el predeterminado, no requiere configuración y es el que más gente puede consumir sin fricción |
| Usuario | auroralibros |
El Docker ID de la organización. Sustitúyelo por el tuyo real |
| Repositorio | aurora-api |
Un repositorio por servicio, no uno por proyecto: aurora-api y, si algún día hiciera falta, aurora-web |
| Visibilidad | Pública | Para poder ejecutarla desde cualquier máquina sin autenticarse, que es la demostración del módulo |
La regla de "un repositorio por servicio" merece un comentario, porque es un error frecuente hacerlo al revés: meter varias imágenes distintas en un mismo repositorio distinguiéndolas por la etiqueta (aurora:api-1.0.0, aurora:web-1.0.0). Funciona técnicamente, pero rompe todo lo demás: no puedes dar permisos distintos por servicio, las etiquetas dejan de significar "versión" y la pestaña Tags se vuelve ilegible. Un repositorio por artefacto desplegable.
Como preparación para la lección 02-06, crea ya el repositorio vacío desde la web (Repositories → Create repository), con nombre aurora-api, visibilidad pública y una descripción corta como "API REST del catálogo de Aurora Libros S.L.". No es imprescindible —Docker Hub crea el repositorio automáticamente en el primer push—, pero así fijas la descripción y la visibilidad desde el principio y evitas publicar por accidente algo privado en público.
En 02-02 construirás la imagen que llenará ese repositorio.
Errores Comunes y Consejos
- Confundir registro con repositorio. "Sube la imagen al repositorio de Docker" no significa nada preciso. El registro es el servidor (
docker.io); el repositorio es el nombre dentro de él (auroralibros/aurora-api). Cuando depures unpushfallido, la primera pregunta siempre es a qué registro apunta el nombre completo de la imagen. - Creer que
docker login"cambia de registro". No hay un registro activo. Puedes tener sesión abierta simultáneamente en Docker Hub,ghcr.ioy tu registro local; el destino de cada operación lo decide el prefijo del nombre de la imagen, no un estado global. - Dejar la contraseña real en
~/.docker/config.json. Base64 no es cifrado. Usa un token de acceso personal con permisos mínimos y, a poder ser, un credential helper. En un servidor compartido esto no es una recomendación, es un requisito. docker login -p miContraseñaen un script. Queda en el historial del shell y es visible enpspara cualquier usuario de la máquina. Usa--password-stdinsiempre.- Builds anónimas en CI. Es la causa número uno del
toomanyrequestsque aparece "de repente" un martes por la tarde: la IP compartida del proveedor agotó la cuota. Autentica los agentes de CI aunque solo descarguen imágenes públicas. - Fiarse de las estrellas. Una imagen de la comunidad con muchas descargas y sin actualizar desde hace catorce meses es peor opción que una oficial menos popular. Prioriza en este orden: oficial → verificada → comunidad con Dockerfile público y actualizada.
- Consejo: mira siempre la pestaña Tags antes de escribir un
FROM. Confirma que la etiqueta existe, con qué arquitecturas y cuándo se actualizó. Te ahorra elmanifest unknowna mitad de una build. - Consejo: un registro local es la mejor herramienta de aprendizaje. Cuando algo del flujo
tag/push/pullno te cuadre, reprodúcelo contralocalhost:5000: es instantáneo, no consume cuota y no publica nada en Internet.
Ejercicios
Ejercicio 1: audita tres imágenes antes de confiar en ellas
Elige tres repositorios de Docker Hub: uno oficial (postgres), uno de Verified Publisher o Sponsored OSS a tu elección, y uno de comunidad que encuentres buscando "nodejs" en Explore. Para cada uno, rellena esta tabla y decide si lo usarías como base de un servicio de Aurora Libros:
| Criterio | Imagen A | Imagen B | Imagen C |
|---|---|---|---|
| Nombre completo | |||
| Tipo (oficial/verificada/comunidad) | |||
| Última actualización de la etiqueta que usarías | |||
| Tamaño comprimido | |||
| ¿Dockerfile público? URL | |||
| Arquitecturas disponibles | |||
| ¿La usarías en producción? Motivo |
Apóyate en la web para los tres primeros criterios y en docker image history para inspeccionar cómo se construyeron.
Ejercicio 2: monta un registro y publica una imagen en él
- Levanta un registro local en el puerto 5001 (no 5000, para practicar el mapeo) llamado
aurora-registry-lab. - Descarga
redis:7-alpinede Docker Hub. - Publícala en tu registro local con el nombre
localhost:5001/aurora-cache:7. - Consulta el catálogo del registro y la lista de etiquetas del repositorio con
curl. - Borra ambas referencias locales y vuelve a descargar la imagen desde tu registro.
- Arranca un contenedor con ella y comprueba que Redis responde.
- Limpia todo.
Ejercicio 3: diagnostica cuatro problemas de registro
Explica la causa de cada mensaje y el comando exacto con el que lo resolverías:
a) Error response from daemon: pull access denied for aurora-api, repository does not
exist or may require 'docker login': denied: requested access to the resource is denied
b) denied: requested access to the resource is denied
(al ejecutar: docker push aurora-api:1.0.0)
c) Error response from daemon: toomanyrequests: You have reached your pull rate limit.
d) Error response from daemon: Get "https://registro.interno.local:5000/v2/":
x509: certificate signed by unknown authoritySoluciones
Solución al ejercicio 1
Un resultado típico, con los valores del momento de escribir esta lección (los tuyos variarán, lo importante es el razonamiento):
| Criterio | postgres:16-alpine |
grafana/grafana:11.4.0 |
algunusuario/nodejs:latest |
|---|---|---|---|
| Tipo | Docker Official Image | Verified Publisher | Comunidad |
| Última actualización | Hace días | Hace días | Hace 2 años |
| Tamaño comprimido | ~110 MB | ~180 MB | ~380 MB |
| Dockerfile público | Sí, github.com/docker-library/postgres |
Sí, github.com/grafana/grafana |
No |
| Arquitecturas | amd64, arm64, y varias más |
amd64, arm64 |
solo amd64 |
| ¿Producción? | Sí, es la referencia | Sí, es el fabricante | No |
El razonamiento para descartar la tercera es acumulativo, y cualquiera de los puntos bastaría por sí solo:
- Dos años sin actualizar significa dos años de CVE sin parchear en el sistema base. Aunque el código de la aplicación fuera perfecto, la
libc,openssly el resto del sistema no lo son. - Sin Dockerfile público no puedes saber qué hay dentro ni auditarlo.
latestcomo única etiqueta es un síntoma de proyecto no mantenido: no hay versionado, así que no puedes fijar nada ni volver atrás.- Solo
amd64la hace inservible en un Mac con Apple Silicon o en instancias ARM salvo emulación. - El tamaño delata un sistema base completo con herramientas innecesarias.
La conclusión práctica para Aurora Libros: las cuatro bases del proyecto (node, postgres, redis, nginx) son oficiales, y esa es precisamente la razón por la que se eligieron en la lección 01-07.
Solución al ejercicio 2
# 1. Registro en el puerto 5001 del host
docker run -d --name aurora-registry-lab -p 5001:5000 registry:2Fíjate en -p 5001:5000: el registro siempre escucha en el 5000 dentro del contenedor; lo que cambias es el puerto del host. Es exactamente el mapeo puerto-host → puerto-contenedor de la lección 01-06.
# 2. Descargar de Docker Hub
docker pull redis:7-alpine
# 3. Reetiquetar y publicar
docker tag redis:7-alpine localhost:5001/aurora-cache:7
docker push localhost:5001/aurora-cache:7The push refers to repository [localhost:5001/aurora-cache]
7: digest: sha256:0d1a3e0c... size: 1571# 4. Consultar el registro
curl -s http://localhost:5001/v2/_catalog
curl -s http://localhost:5001/v2/aurora-cache/tags/list# 5. Borrar ambas referencias locales y recuperar del registro propio
docker image rm redis:7-alpine localhost:5001/aurora-cache:7
docker image ls | grep -E "redis|aurora-cache" # no debe devolver nada
docker pull localhost:5001/aurora-cache:7El paso 5 es el que demuestra el objetivo del ejercicio: al borrar las dos referencias, la imagen desaparece de verdad de tu máquina (recuerda de 01-05 que dos etiquetas sobre el mismo IMAGE ID cuentan como una sola imagen: mientras quede una referencia, los datos siguen ahí). Si el pull posterior funciona, los bytes vienen necesariamente de tu registro.
# 6. Arrancar y comprobar
docker run -d --name cache-lab -p 6379:6379 localhost:5001/aurora-cache:7
docker exec cache-lab redis-cli ping# 7. Limpieza
docker stop cache-lab aurora-registry-lab
docker rm cache-lab aurora-registry-lab
docker image rm localhost:5001/aurora-cache:7Solución al ejercicio 3
(a) pull access denied ... may require 'docker login'. El mensaje mezcla dos causas porque el registro no distingue entre "no existe" y "no tienes permiso": hacerlo permitiría averiguar qué repositorios privados tiene una organización. Las causas reales, por frecuencia:
- El nombre no lleva usuario:
aurora-apise expande adocker.io/library/aurora-api, ylibrary/está reservado a imágenes oficiales, así que no existe. Correcto:docker pull auroralibros/aurora-api:1.0.0. - El repositorio es privado y no tienes sesión:
docker login. - Simplemente hay una errata en el nombre.
(b) denied al hacer push. Estás intentando subir a docker.io/library/aurora-api, un espacio donde nadie tiene permiso de escritura. La imagen debe llevar tu espacio de nombres:
docker tag aurora-api:1.0.0 auroralibros/aurora-api:1.0.0
docker login
docker push auroralibros/aurora-api:1.0.0Regla general: si el push da denied, mira el nombre antes que las credenciales. Es más frecuente el nombre mal formado que la sesión caducada.
(c) toomanyrequests. Has agotado la cuota de descargas. Solución inmediata:
Si ya estabas autenticado, la cuota agotada es la de tu cuenta y toca esperar a que se renueve la ventana, usar un espejo o subir de plan. En CI, la causa casi siempre es que los agentes descargan de forma anónima: añade un paso de login con --password-stdin al principio del pipeline (lección 06-02). Para confirmar el diagnóstico, la consulta de cabeceras ratelimit-* del apartado 5.
(d) x509: certificate signed by unknown authority. El registro sí habla HTTPS, pero su certificado está firmado por una autoridad que tu máquina no reconoce (típicamente una CA interna de la empresa). No es un problema de credenciales. La solución correcta es instalar el certificado de esa CA donde Docker lo busca:
sudo mkdir -p /etc/docker/certs.d/registro.interno.local:5000
sudo cp ca-empresa.crt /etc/docker/certs.d/registro.interno.local:5000/ca.crt
sudo systemctl restart dockerEl nombre del directorio debe coincidir exactamente con el host y puerto que usas en el nombre de la imagen. La alternativa de añadirlo a insecure-registries no arregla esto: desactivaría la verificación en lugar de resolverla, y solo es aceptable en un laboratorio aislado. Distínguelo del error del apartado 9 (server gave HTTP response to HTTPS client), que es el caso contrario: allí el servidor no tiene TLS en absoluto.
Conclusión
Ya sabes de dónde vienen las imágenes que ejecutas. Un registro es un servidor que habla el protocolo de distribución OCI; dentro tiene repositorios, colecciones de imágenes que comparten nombre; y dentro de cada repositorio, las etiquetas son punteros móviles a digests inmutables. Ese modelo de tres capas explica por qué nginx significa realmente docker.io/library/nginx:latest, por qué dos etiquetas pueden compartir IMAGE ID y por qué el destino de un push no es un parámetro del comando sino parte del nombre de la imagen.
Además, ya tienes cuenta en Docker Hub, sabes que docker login deja tus credenciales en ~/.docker/config.json en base64 —no cifrado— y cómo evitarlo con tokens de acceso personal, --password-stdin y un credential helper. Puedes distinguir una Docker Official Image de una imagen de comunidad abandonada y aplicar un chequeo real de confianza: fecha de actualización, tamaño, Dockerfile público, capas comprensibles y arquitecturas. Conoces los límites de descarga, por qué golpean sobre todo a las builds anónimas detrás de una IP compartida y qué error exacto producen. Y has levantado tu propio registro con registry:2 para comprobar, con un ciclo completo de tag, push, borrado y pull, que el mecanismo no tiene nada de mágico. La imagen de la API de Aurora Libros ya tiene dirección: auroralibros/aurora-api.
Lo único que falta es la imagen. En la siguiente lección, Construyendo Imágenes Docker, pasarás de consumir imágenes ajenas a fabricar la tuya: verás qué hace exactamente docker build, qué significa ese punto final del comando —el famoso contexto de construcción—, cómo el fichero .dockerignore que quedó prometido en 01-07 recorta ese contexto, y cómo la caché de capas de BuildKit convierte una build de un minuto en una de dos segundos si ordenas bien las instrucciones. Al terminarla tendrás auroralibros/aurora-api:0.1.0 construida en tu máquina, corriendo en un contenedor y respondiendo a curl http://localhost:3000/salud. El primer paso de los quince empieza a caer.
Docker: De Principiante a Avanzado
Módulo 1: Introducción a Docker
- ¿Qué es Docker?
- Instalando Docker
- Arquitectura de Docker
- Comandos Básicos de Docker
- Entendiendo las Imágenes de Docker
- Creando tu Primer Contenedor Docker
- El Proyecto del Curso: la Plataforma Aurora Libros
Módulo 2: Trabajando con Imágenes Docker
- Docker Hub y Repositorios
- Construyendo Imágenes Docker
- Conceptos Básicos de Dockerfile
- Instrucciones Avanzadas del Dockerfile
- Gestionando Imágenes Docker
- Etiquetado y Publicación de Imágenes
Módulo 3: Contenedores Docker
- Ejecutando Contenedores
- Ciclo de Vida del Contenedor
- Gestionando Contenedores
- Inspección y Depuración de Contenedores
- Redes en Docker
- Persistencia de Datos con Volúmenes
- Límites de Recursos y Políticas de Reinicio
Módulo 4: Docker Compose
- Introducción a Docker Compose
- Definiendo Servicios en Docker Compose
- Comandos de Docker Compose
- Aplicaciones Multi-Contenedor
- Variables de Entorno en Docker Compose
- Perfiles, Overrides y Múltiples Entornos
- Desarrollo Local con Docker Compose
Módulo 5: Conceptos Avanzados de Docker
- Profundización en Redes Docker
- Opciones de Almacenamiento Docker
- Mejores Prácticas de Seguridad en Docker
- Optimizando Imágenes Docker
- Builds Avanzadas con BuildKit y Buildx
- Registro y Monitoreo en Docker
- El Runtime por Dentro: Namespaces, Cgroups y Capas
Módulo 6: Docker en Producción
- Preparar una Imagen para Producción
- CI/CD con Docker
- Orquestando Contenedores con Docker Swarm
- Introducción a Kubernetes
- Desplegando Contenedores Docker en Kubernetes
- Escalado y Balanceo de Carga
- Estrategias de Despliegue y Rollback
