Docker Hub es una plataforma excelente para gestionar imágenes públicas, pero en entornos empresariales es fundamental contar con un registro privado para alojar artefactos propios. Docker Registry v2, desarrlolado en Go, reemplazó a la antigua versión en Python, mejorando drásticamente el rendimiento de las operaciones de push y pull, además de optimizar el almacenamiento y la seguridad.
Las ventajas de utilizar un registro interno incluyen:
- Reducción del consumo de ancho de banda, evitando descargas repetitivas desde internet.
- Centralización y control de acceso a las imágenes corporativas.
Arquitectura del Entorno
Para esta implementación, utilizaremos tres nodos con CentOS 7 y Docker configurado:
- Nodo Registry (10.0.0.50): Alojará el servicio de registro privado.
- Nodo Cliente A (10.0.0.51): Simulará un entorno de desarrollo con acceso básico.
- Nodo Cliente B (10.0.0.52): Simulará un entorno de producción con acceso autenticado.
Despliegue Inicial del Registro Privado
En el Nodo Registry, instalamos y arrancamos el servicio de Docker:
[root@nodo-registry ~]# yum install docker -y
[root@nodo-registry ~]# systemctl enable --now docker
Descargamos la imagen oficial de Registry en su versión 2:
[root@nodo-registry ~]# docker pull registry:2
Iniciamos el contenedor del registro, mapeando el puerto 5000 y montando un volumen para la persistencia de datos en /data/registry-storage:
[root@nodo-registry ~]# docker run -d \
--name private-registry \
-p 5000:5000 \
--restart=always \
-v /data/registry-storage:/var/lib/registry \
registry:2
Para probar el funcionamiento, descargamos una imagen ligera, le asignamos una etiqueta apuntando a nuestro registro local y la subimos:
[root@nodo-registry ~]# docker pull nginx:alpine
[root@nodo-registry ~]# docker tag nginx:alpine 10.0.0.50:5000/nginx:alpine
[root@nodo-registry ~]# docker push 10.0.0.50:5000/nginx:alpine
Configuración de Clientes para HTTP Inseguro
Docker exige HTTPS por defecto para comunicarse con los registros. Como nuestro registro local usa HTTP, los clientes rechazarán la conexión. Para solucionarlo, debemos configurar el demonio de Docker en los nodos clientes para que confíen en nuestro registro.
En el Nodo Cliente A, creamos o editamos el archivo /etc/docker/daemon.json:
{
"insecure-registries": ["10.0.0.50:5000"]
}
Recargamos la configuración y reiniciamos el servicio:
[root@nodo-cliente-a ~]# systemctl daemon-reload
[root@nodo-cliente-a ~]# systemctl restart docker
Ahora podemos extraer la imagen desde el registro privado sin errores:
[root@nodo-cliente-a ~]# docker pull 10.0.0.50:5000/nginx:alpine
Implementación de Autenticación Básica
Un registro sin autenticación es un riesgo de seguridad en producción. Procedemos a configurar la autenticación básica mediante htpasswd en el Nodo Registry.
Creamos el directorio para las credenciales y generamos el archivo de contraseñas para el usuario deployer:
[root@nodo-registry ~]# mkdir -p /data/registry-auth
[root@nodo-registry ~]# docker run --rm --entrypoint htpasswd registry:2 -Bbn deployer 'S3cur3P@ss!' > /data/registry-auth/htpasswd
Adicionalmente, creamos un archivo de configuración para habilitar la eliminación de imágenes y ajustar parámetros de almcaenamiento en /data/registry-config/config.yml:
version: 0.1
log:
fields:
service: registry
storage:
delete:
enabled: true
cache:
blobdescriptor: inmemory
filesystem:
rootdirectory: /var/lib/registry
http:
addr: :5000
headers:
X-Content-Type-Options: [nosniff]
health:
storagedriver:
enabled: true
interval: 10s
threshold: 3
Detenemos y eliminamos el contenedor anterior, y lanzamos uno nuevo inyectando las variables de entorno para la autenticación y montando los nuevos volúmenes:
[root@nodo-registry ~]# docker stop private-registry && docker rm private-registry
[root@nodo-registry ~]# docker run -d \
--name secure-registry \
-p 5000:5000 \
--restart=always \
-v /data/registry-config:/etc/docker/registry \
-v /data/registry-auth:/auth \
-v /data/registry-storage:/var/lib/registry \
-e "REGISTRY_AUTH=htpasswd" \
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
-e "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd" \
registry:2
Operaciones en el Cliente Autenticado
En el Nodo Cliente B, repetimos la configuración de daemon.json para permitir HTTP y reiniciamos Docker.
Intentamos subir una imagen sin autenticarnos para verificar que el registro nos bloquea:
[root@nodo-cliente-b ~]# docker tag nginx:alpine 10.0.0.50:5000/nginx:alpine
[root@nodo-cliente-b ~]# docker push 10.0.0.50:5000/nginx:alpine
# Esto devolverá un error de "no basic auth credentials"
Procedemos a iniciar sesión con las credenciales generadas:
[root@nodo-cliente-b ~]# docker login 10.0.0.50:5000
Username: deployer
Password:
Login Succeeded
Una vez autenticados, la operación de push se completa correctamente:
[root@nodo-cliente-b ~]# docker push 10.0.0.50:5000/nginx:alpine
Finalmente, podemos consultar el catálogo de repositorios disponibles en el registro utilizando curl y pasando las credenciales:
[root@nodo-cliente-b ~]# curl -u deployer:'S3cur3P@ss!' http://10.0.0.50:5000/v2/_catalog
{"repositories":["nginx"]}