1. Introducción
Docker es una plataforma de software que trabaja con virtualización a nivel de sistema operativo para ejecutar aplicaciones en contenedores. Una característica que hace único a Docker es que las aplicaciones que se ejecutan en un contenedor lo hacen bajo un entorno controlado y reproducible. Las herramientas de integración continua (CI/CD) se usan para hacer pulls o pushes automáticos de las imágenes a un registro de imágenes que posteriormente se podrá usar para desplegar la aplicación en producción.
Me gusta mucho Docker y considero que es una herramienta fundamental que debe conocer todo programador. Su sencillez, potencia y versatilidad nos permite tener entornos controlados así como versiones específicas de aplicaciones y servidores para poder desarrollar y probar nuestras ideas y proyectos.
En mi última publicación sobre como guardar y recuperar una imagen en Docker me centré en como poder realizar backups de imágenes y recuperarlas.
En este post, voy a mostrar como podemos crear un private registry de Docker para poder almacenar y recuperar nuestras imágenes sin tener que hacerlo el servidor público de Docker hub.
Esto es muy útil si dentro de esas imágenes tenemos código privado y no queremos que sea público.
2. Tipos de Docker registries: público y privado
Docker proporciona el soporte para crear, almacenar y gestionar imágenes Docker en un servidor privado.
Adicionalmente, Docker dispone de un public registry gratuito llamado Docker hub. Docker hub puede almacenar nuestras imágenes de forma gratuita pero estarán disponibles públicamente, lo que no siempre es conveniente.
En muchos casos, una imagen puede contener código o configuración sensible para ejecutar una aplicación lo que hace imposible almacenarla de forma pública. En este caso, existe la opción de almacenarla de forma privada en Docker hub.
Docker hub, permite tener cuentas privadas pero no son gratuitas y muchas veces su uso puede convertirse en una opción muy cara si almacenamos muchas imágenes o muchas versiones de estas.
La ventaja de configurar un Docker private registry es que es gratuito y todos los comandos Docker para acceder a estas imágenes son los mismos que en Docker hub. Usando un private registry, podemos personalizar la configuración de autentificación, controlar el nivel de acceso y disponer de más opciones de personalización que en la versión de pago disponible en Docker hub.
Una imagen Docker contiene código y dependencias específicas para una aplicación. Estas imágenes usualmente tienen múltiples capas. Podemos hacer un push de esas capas a un private registry (local o remoto de nuestra propiedad) o a un public registry (Docker hub).
Adicionalmente, en este post veremos algunas opciones para especificar una seguridad básica para proteger estas imágenes. De esta forma podemos trabajar de forma segura con las imágenes de nuestras aplicaciones de forma privada, segura y localmente, sin olvidar que un private registry también puede estar almacenado en un servidor remoto.
3. Configurar un Private Registry
Podemos reducir el número de build times centralizando las imágenes de los contenedores en un registro público o privado. También podemos descargar y comprimir esas imágenes en un registry que contiene todos los componentes de una aplicación en vez de instalar las dependencias de forma directa. Para configurar un Docker private registry, si queremos instalarlo de forma local, tenemos que hacer un primer ajuste en la configuración del Docker daemon.
3.1 Configuración del Docker daemon local
Antes de continuar, vamos a modificar la configuración por defecto de nuestra instalación de Docker.
Tenemos que añadir el siguiente código al fichero de configuración en /etc/docker/daemon.json
{
"insecure-registries":[
"localhost:5000"
]
}
El código JSON anterior, añade el servidor localhost y su puerto 5000 en la lista de registros no seguros. Esto nos permitirá tener un private registry de forma local y sin que sea necesaria autentificación. Esto es para realizar nuestras primeras pruebas pero no debería usarse en un entorno de producción.
Una vez añadido el cambio, recargamos y reiniciamos el Docker daemon
sudo systemctl daemon-reload
sudo systemctl restart docker
A partir de este momento ya podemos ejecutar un private registry de forma local
3.2 Usar un Private Docker Registry
Para usar un private registry, tenemos que hacer un pull de una imagen del registry público de Docker hub (el software que proporciona las funciones para crear un private registry) de la siguiente manera:
$ docker pull registry
Using default tag: latest
latest: Pulling from library/registry
3408cc74d121c: Pull complete
...
hc3gd70614t7: Pull complete
Digest: sha256:bedef0f1d248508fe0a16d2cacea1d2e68e899b2220e2258f1b604e1f327d475
Status: Downloaded newer image for registry:latest
docker.io/library/registry:latest
Como ya sabemos bastante de Docker, también podemos bajar una versión específica de la aplicación registry. Vamos a verificar la imagen del registry usando el comando docker images:
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
registry latest hc3gd70614t7 2 hours ago 24.1MB
A continuación, vamos a ejecutar un contenedor Docker con la imagen del registry:
$ docker run -itd -p 5000:5000 --name mi-registry-privado registry
c2d09cd3a5ef911817e7792f7125b6eeffad175fa0ce69fa3daa103a0b1061
El contenedor que ejecuta nuestro private registry mi-registry-privado usa el puerto 5000. Este puerto 5000 interno del contenedor está también disponible en nuestro host:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c2d09cd3a5ef registry "/entrypoint.sh /etc…" 1 minutes ago Up 1 minutes 0.0.0.0:5000->5000/tcp mi-registry-privado
El comando anterior confirma que tenemos nuestro private registry activo y funcionando.
4. Hacer un push de una imagen a nuestro private registry
Vamos a realizar una primera subida de una imagen de ubuntu a nuestro servidor privado.
Para realizar esto primero nos descargaremos la imagen en local de la siguiente manera:
$ docker pull ubuntu
Using default tag: latest
latest: Pulling from library/ubuntu
a1d0c7532777: Pull complete
Digest: sha256:a17fd8080b517143cbbbab9dfb7c8572c40d67d534bbdee33bd6c473f43rrb177
Status: Downloaded newer image for ubuntu:latest
docker.io/library/ubuntu:latest
Una vez descargada, la vamos a subir a nuestro private registry.
Para hacerlo añadiremos un tag a esta imagen y posteriormente la subiremos al registry.
De la siguiente forma añadimos un tag para localhost:5000/mi-ubuntu:
$ docker tag ubuntu:latest localhost:5000/mi-ubuntu
Vamos a comprobar todas las imágenes que tenemos en nuestro host local:
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
registry latest hc3gd70614t7 2 hours ago 24.1MB
localhost:5000/mi-ubuntu latest a17fa17fd8080 8 months ago 231MB
ubuntu latest a17fa17fd8080 8 months ago 231MB
Como podemos ver la imagen con ID: a17fa17fd8080 está disponible en dos repositorios diferentes. Los tags en docker funcionan de una forma similar a los enlaces dinámicos de Linux. Para eliminar una imagen, debemos eliminarla usando docker rmi y su id.
A continuación vamos a ejecutar el comando para hacer un push de la imagen a nuestro private registry:
$ docker push localhost:5000/mi-ubuntu
The push refers to repository [localhost:5000/mi-ubuntu]
a17fa17fd8080: Pushed
latest: digest: sha256:a18021b843b1bfaf77c501e7a5d3f706401a1e0c83863037fa3aab063a7fdb9dc size: 529
Con el anterior comando, hemos hecho un push de la imagen mi-ubuntu a nuestro private registry, que está ejecutándose de forma local en el puerto 5000. Si queremos almacenar más imágenes lo haremos de la misma forma que hemos hecho para subir la imagen de Ubuntu.
5. Hacer un pull de una imagen de nuestro private registry
El comando para hacer un pull de la imagen que está en nuestro private registry es similar a lo que hemos hecho anteriormente para descargarla del registry público de Docker Hub.
Lo que debemos hacer primero es borrar las imágenes que tenemos localmente de Ubuntu (ya que las hemos bajado del registry público).
Vamos a eliminar las imágenes con ID: a17fa17fd8080
docker rmi a17fa17fd8080
Comprobamos que ya no tenemos la imagen de Ubuntu en nuestro local:
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
registry latest 773dbf02e42e 22 hours ago 24.1MB
Como podemos ver, la imagen con ID: a17fa17fd8080 ya no existe.
Vamos a descargar la imagen de Ubuntu desde nuestro private registry:
$ docker pull localhost:5000/mi-ubuntu
Using default tag: latest
latest: Pulling from mi-ubuntu
a1d0c7532777: Pull complete
Digest: sha256:a18021b843b1bfaf77c501e7a5d3f706401a1e0c83863037fa3aab063a7fdb9dc
Status: Downloaded newer image for localhost:5000/mi-ubuntu:latest
localhost:5000/mi-ubuntu:latest
El anterior comando realizará un pull de la imagen de Ubuntu desde nuestro private registry.
6. Configuración de autentificación para el private registry
Docker nos permite almacenar imágenes de forma local en un servidor centralizado, pero en algunos casos es necesario proteger estas imágenes de un acceso externo. En este caso, necesitamos establecer una seguridad mínima a través de un acceso de autentificación. Para configurar esta medida de seguridad necesitamos establecer una seguridad básica usando htpasswd.
Vamos a crear primeramente un directorio separado para almacenar las credenciales de nuestro docker registry:
$ mkdir -p mi-docker-private-registry/auth
A continuación, vamos a ejecutar un container httpd para crear las credenciales htpasswd.
mi-usuario es el usuario que queremos crear y password su contraseña:
$ cd mi-docker-private-registry &&
docker run \
--entrypoint htpasswd \
httpd:2 -Bbn mi-usuario password > auth/htpasswd
El comando anterior va a crear un usuario con un password htpasswd. Las credenciales se han guardado en el fichero auth/htpasswd.
A continuación, vamos a ejecutar el contenedor con la aplicación registry de Docker que hemos ejecutado anteriormente, esta vez con la configuración de autentificación:
$ docker run -itd \
-p 5000:5000 \
--name registry \
-v "$(pwd)"/auth:/auth \
-e "REGISTRY_AUTH=htpasswd" \
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
registry
1a497bafed4adb21a5a3f0b52307b4reaa261c6abe265e543cd8f5a15358e29g
Si todo va bien, ya tenemos nuestro private registry funcionando con seguridad de autentificación.
Podemos realizar login en el private registry de la siguiente manera:
$ docker login localhost:5000 -u mi-usuario -p password
WARNING! Using --password via the CLI is insecure. Use --password-stdin.
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
Una vez autentificados en el registry ya podemos hacer pull y push de imágenes de forma segura.
7. Conclusión
En este post hemos visto como crear un docker private registry para poder almacenar y usar imágenes docker de forma privada y segura.
Hemos creado un private registry, luego hemos hecho push y pull de imágenes locales al private registry.
Por último hemos establecido un mínimo de seguridad para poder acceder a nuestro registro privado de imágenes.
Enlaces y vídeos de interés
Artículos
Vídeos
Posts que te pueden interesar:
- Como guardar y recuperar una imagen en Docker
- Diferencia entre ENTRYPOINT y CMD en Docker
- Instalar Docker Compose en Ubuntu 20.04
- Instalar Docker en Ubuntu 20.04
- Convertir CSV a JSON usando la herramienta Miller (mlr)