Cómo configurar un registro Docker privado en Ubuntu 22.04

Si trabajas para una organización y quieres mantener tus imágenes Docker internamente para un despliegue rápido, entonces alojar un repositorio Docker privado es perfecto. Tener un registro Docker privado te permite ser dueño de tu canal de distribución de imágenes y tener un control más estricto sobre el almacenamiento y la distribución de imágenes. Puedes integrar tu registro con tu sistema CI/CD mejorando tu flujo de trabajo.

Este tutorial te enseñará a configurar y utilizar un registro Docker privado en un servidor Ubuntu 22.04 utilizando Amazon S3 como ubicación de almacenamiento.

Requisitos previos

  • Dos servidores Linux con Ubuntu 22.04. Un servidor actuará como anfitrión del registro, mientras que el otro se utilizará como cliente para enviar solicitudes y recibir imágenes del anfitrión.
  • Un nombre de dominio registrado que apunte al servidor anfitrión. Utilizaremos registry.example.com para nuestro tutorial.
  • Un usuario no root con privilegios sudo en ambas máquinas.
  • Asegúrate de que todo está actualizado.
    $ sudo apt update
    $ sudo apt upgrade
    
  • Pocos paquetes que necesite tu sistema.
    $ sudo apt install wget curl nano software-properties-common dirmngr apt-transport-https gnupg2 ca-certificates lsb-release ubuntu-keyring unzip -y
    

    Puede que algunos de estos paquetes ya estén instalados en tu sistema.

Paso 1 – Configurar el Cortafuegos

El primer paso es configurar el cortafuegos. Ubuntu viene con ufw (Uncomplicated Firewall) por defecto.

Comprueba si el cortafuegos se está ejecutando.

$ sudo ufw status

Deberías obtener la siguiente salida.

Status: inactive

Permite el puerto SSH para que el cortafuegos no rompa la conexión actual al activarlo.

$ sudo ufw allow OpenSSH

Permite también los puertos HTTP y HTTPS.

$ sudo ufw allow http
$ sudo ufw allow https

Habilita el cortafuegos

$ sudo ufw enable
Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup

Comprueba de nuevo el estado del cortafuegos.

$ sudo ufw status

Deberías ver un resultado similar.

Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
80/tcp                     ALLOW       Anywhere
443                        ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)
80/tcp (v6)                ALLOW       Anywhere (v6)
443 (v6)                   ALLOW       Anywhere (v6)

Paso 2 – Instala Docker y Docker Compose

Este paso es necesario tanto en el servidor como en las máquinas cliente.

Ubuntu 22.04 incluye una versión antigua de Docker. Para instalar la última versión, importa primero la clave GPG de Docker.

$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

Crea un archivo de repositorio Docker.

$ echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Actualiza la lista de repositorios del sistema.

$ sudo apt update

Instala la última versión de Docker.

$ sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin

Comprueba que se está ejecutando.

$ sudo systemctl status docker
? docker.service - Docker Application Container Engine
     Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled)
     Active: active (running) since Thu 2023-04-13 09:37:09 UTC; 3min 47s ago
TriggeredBy: ? docker.socket
       Docs: https://docs.docker.com
   Main PID: 2106 (dockerd)
      Tasks: 7
     Memory: 26.0M
        CPU: 267ms
     CGroup: /system.slice/docker.service
             ??2106 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

Por defecto, Docker requiere privilegios de root. Si quieres evitar utilizar sudo cada vez que ejecutes el comando docker, añade tu nombre de usuario al grupo docker.

$ sudo usermod -aG docker $(whoami)

Tendrás que salir del servidor y volver a entrar como el mismo usuario para activar este cambio o utilizar el siguiente comando.

$ su - ${USER}

Confirma que tu usuario se ha añadido al grupo Docker.

$ groups
navjot wheel docker

Paso 3 – Configurar el Registro Docker

Crear directorios de usuario

Crea un directorio para la configuración del registro.

$ mkdir ~/docker-registry

Cambia al directorio docker-registry.

$ cd ~/docker-registry

Crea un directorio para almacenar la contraseña de autenticación HTTP, los archivos de configuración de Nginx y los certificados SSL.

$ mkdir auth

Crea otro directorio para almacenar los logs de Nginx.

$ mkdir logs

Crea un bucket de Amazon S3

Puedes almacenar los datos del registro y las imágenes en tu servidor o utilizar un servicio de alojamiento en la nube. Para nuestro tutorial, utilizaremos el servicio en la nube Amazon S3.

El siguiente paso es configurar el archivo de configuración con algunos ajustes importantes. Estos ajustes también se pueden definir en el archivo docker-compose.yml, pero tener un archivo separado es mucho mejor.

Crea un bucket con los siguientes ajustes.

  • ACL debe estar desactivado.
  • El acceso público al cubo debe estar desactivado.
  • El versionado del bucket debe estar desactivado.
  • Activa el cifrado del bucket mediante claves gestionadas por Amazon S3. (SSE-S3)
  • El bloqueo de objetos debe estar desactivado.

Crea un usuario IAM con la siguiente política.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:ListBucket",
        "s3:GetBucketLocation",
        "s3:ListBucketMultipartUploads"
      ],
      "Resource": "arn:aws:s3:::S3_BUCKET_NAME"
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:GetObject",
        "s3:DeleteObject",
        "s3:ListMultipartUploadParts",
        "s3:AbortMultipartUpload"
      ],
      "Resource": "arn:aws:s3:::S3_BUCKET_NAME/*"
    }
  ]
}

Sustituye S3_BUCKET_NAME por el nombre de tu cubo de S3.

Anota la clave secreta, el valor secreto y la región de tu bucket para utilizarlos más adelante.

Crea el archivo Docker Compose

Crea el archivo docker-compose.yml y ábrelo para editarlo.

$ nano docker-compose.yml

Pega en él el siguiente código.

services:
  registry:
    image: registry:2
    restart: always
    environment:
      - REGISTRY_STORAGE=s3
      - REGISTRY_STORAGE_S3_REGION=us-west-2
      - REGISTRY_STORAGE_S3_BUCKET=hf-docker-registry
      - REGISTRY_STORAGE_S3_ENCRYPT=true
      - REGISTRY_STORAGE_S3_CHUNKSIZE=5242880
      - REGISTRY_STORAGE_S3_SECURE=true
      - REGISTRY_STORAGE_S3_ACCESSKEY=AKIA3FIG4NVFNXKQXMSJ
      - REGISTRY_STORAGE_S3_SECRETKEY=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
      - REGISTRY_STORAGE_S3_V4AUTH=true
      - REGISTRY_STORAGE_S3_ROOTDIRECTORY=/image-registry
      - REGISTRY_STORAGE_CACHE_BLOBDESCRIPTOR=inmemory
      - REGISTRY_HEALTH_STORAGEDRIVER_ENABLED=false
  nginx:
    image: "nginx:alpine"
    ports:
      - 443:443
    links:
      - registry:registry
    volumes:
      - ./auth:/etc/nginx/conf.d
      - ./auth/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./logs:/var/log/nginx
      - /etc/letsencrypt:/etc/letsencrypt

Guarda el archivo pulsando Ctrl + X e introduciendo Y cuando se te pida.

Repasemos lo que hemos configurado en nuestro archivo de composición.

  1. El primer paso es coger la última imagen de la versión 2 del registro Docker del hub. No estamos utilizando la última etiqueta porque puede causar problemas en caso de una actualización importante de la versión. Establecerla en 2 te permite obtener todas las actualizaciones de la versión 2.x y evitar que se actualice automáticamente a la siguiente versión principal, lo que puede introducir cambios de última hora.
  2. El contenedor de registro está configurado para reiniciarse siempre en caso de fallo o apagado inesperado.
  3. Hemos establecido varias variables de entorno para el almacenamiento en Amazon S3. Repasémoslas rápidamente.
    • REGISTRY_STORAGE establece el tipo de almacenamiento. Hemos seleccionado s3 ya que estamos utilizando Amazon S3.
    • REGISTRY_STORAGE_S3_REGION establece la región de tu bucket S3.
    • REGISTRY_STORAGE_S3_BUCKET establece el nombre de tu cubo S3.
    • REGISTRY_STORAGE_S3_ENCRYPT – ponlo a true si has activado la encriptación del bucket.
    • REGISTRY_STORAGE_S3_CHUNKSIZE establece el tamaño de los trozos de subida. Debe ser superior a 5MB (5 * 1024 * 1024).
    • REGISTRY_STORAGE_S3_SECURE – ponlo a true si vas a utilizar HTTPS.
    • REGISTRY_STORAGE_S3_ACCESSKEY y REGISTRY_STORAGE_S3_SECRETKEY – Credenciales de usuario que has obtenido tras crear tu usuario IAM.
    • REGISTRY_STORAGE_S3_V4AUTH – ponlo a true si utilizas la v4 de autenticación de AWS. Si recibes errores relacionados con el inicio de sesión en S3, establécelo en false.
    • REGISTRY_STORAGE_S3_ROOTDIRECTORY – establece el directorio raíz de tu bucket en el que se almacenarán tus datos de registro.
    • REGISTRY_STORAGE_CACHE_BLOBDESCRIPTOR – establece la ubicación de la Caché. En nuestro caso, la almacenaremos en memoria. También puedes configurarlo para que utilice Redis.
    • REGISTRY_HEALTH_STORAGEDRIVER_ENABLED – Ponlo a false para desactivar el servicio de comprobación de la salud del almacenamiento del Registro. Hay un error en el Registro que puede causar problemas si no lo pones a false.
  4. El Registro Docker se comunica a través del puerto 5000, que es el que hemos expuesto en nuestro servidor al docker.
  5. ./auth:/etc/nginx/conf.d El mapeo garantiza que todos los ajustes de Nginx estén disponibles en el contenedor.
  6. ./auth/nginx.conf:/etc/nginx/nginx.conf:ro mapea el archivo de configuración de Nginx del sistema a uno del contenedor en modo sólo lectura.
  7. ./logs:/var/log/nginx permite el acceso a los registros de Nginx en el sistema mediante el mapeo al directorio de registros de Nginx en el contenedor.
  8. La configuración del registro Docker se almacena en el archivo /etc/docker/registry/config.yml en el contenedor, y lo hemos mapeado al archivo config.yml en el directorio actual, que crearemos en el siguiente paso.

Configurar la autenticación

Para configurar la autenticación HTTP, necesitas instalar el paquete httpd-tools.

$ sudo apt install apache2-utils -y

Crea el archivo de contraseña en el directorio ~/docker-registry/auth.

$ htpasswd -Bc ~/docker-registry/auth/nginx.htpasswd user1
New password:
Re-type new password:
Adding password for user user1

La bandera -c indica al comando que cree un nuevo archivo, y la bandera -B que utilice el algoritmo bcrypt soportado por Docker. Sustituye user1 por un nombre de usuario de tu elección.

Si quieres añadir más usuarios, ejecuta de nuevo el comando, pero sin la bandera -c.

$ htpasswd -B ~/docker-registry/auth/registry.password user2

Ahora, el archivo se asignará al contenedor de registro para la autenticación.

Paso 4 – Instalar SSL

Necesitamos instalar Certbot para generar el certificado SSL. Puedes instalar Certbot utilizando el repositorio de Ubuntu o conseguir la última versión utilizando la herramienta Snapd. Nosotros utilizaremos la versión Snapd.

Ubuntu 22.04 viene con Snapd instalado por defecto. Ejecuta los siguientes comandos para asegurarte de que tu versión de Snapd está actualizada.

$ sudo snap install core && sudo snap refresh core

Instala Certbot.

$ sudo snap install --classic certbot

Utiliza el siguiente comando para asegurarte de que se puede ejecutar el comando Certbot creando un enlace simbólico al directorio /usr/bin.

$ sudo ln -s /snap/bin/certbot /usr/bin/certbot

Ejecuta el siguiente comando para generar un certificado SSL.

$ sudo certbot certonly --standalone --agree-tos --no-eff-email --staple-ocsp --preferred-challenges http -m [email protected] -d registry.example.com

El comando anterior descargará un certificado en el directorio /etc/letsencrypt/live/registry.example.com de tu servidor.

Genera un certificado de grupo Diffie-Hellman.

$ sudo openssl dhparam -dsaparam -out /etc/ssl/certs/dhparam.pem 4096

Comprueba el servicio programador de renovaciones de Certbot.

$ sudo systemctl list-timers

Encontrarás snap.certbot.renew.service como uno de los servicios programados para ejecutarse.

NEXT                        LEFT          LAST                        PASSED        UNIT                      ACTIVATES
.....
Sun 2023-04-14 00:00:00 UTC 19min left    Sat 2023-02-25 18:04:05 UTC n/a          snap.certbot.renew.timer  snap.certbot.renew.service
Sun 2023-04-14 00:00:20 UTC 19min left    Sat 2023-02-25 10:49:23 UTC 14h ago      apt-daily-upgrade.timer   apt-daily-upgrade.service
Sun 2023-04-14 00:44:06 UTC 3h 22min left Sat 2023-02-25 20:58:06 UTC 7h ago       apt-daily.timer           apt-daily.service

Realiza una ejecución en seco del proceso para comprobar si la renovación SSL funciona correctamente.

$ sudo certbot renew --dry-run

Si no ves ningún error, ya está todo listo. Tu certificado se renovará automáticamente.

Copia el archivo Dhparam en el contenedor

Copia el certificado del grupo Diffie-Hellman en el directorio ~/docker-registry/auth, que se asignará al contenedor.

$ sudo cp /etc/ssl/certs/dhparam.pem ~/docker-registry/auth

Paso 5 – Configurar Nginx

El siguiente paso consiste en configurar el servidor Nginx como proxy front-end para el servidor del registro Docker. El registro Docker viene con un servidor incorporado que opera en el puerto 5000. Lo pondremos detrás de Nginx.

Crea y abre el archivo ~/docker-registry/auth/nginx.conf para editarlo.

$ sudo nano ~/docker-registry/auth/nginx.conf

Pega en él el siguiente código.

events {
    worker_connections  1024;
}

http {

  upstream docker-registry {
    server registry:5000;
  }

  ## Set a variable to help us decide if we need to add the
  ## 'Docker-Distribution-Api-Version' header.
  ## The registry always sets this header.
  ## In the case of nginx performing auth, the header is unset
  ## since nginx is auth-ing before proxying.
  map $upstream_http_docker_distribution_api_version $docker_distribution_api_version {
    '' 'registry/2.0';
  }

  server {
    listen 443 ssl http2;
    server_name registry.example.com;

    # SSL
    ssl_certificate /etc/letsencrypt/live/registry.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/registry.example.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/registry.example.com/chain.pem;

    access_log  /var/log/nginx/registry.access.log;
    error_log   /var/log/nginx/registry.error.log;

    # Recommendations from https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers on;
    ssl_ecdh_curve X25519:prime256v1:secp384r1:secp521r1;
    ssl_session_cache shared:SSL:10m;
    ssl_dhparam /etc/nginx/conf.d/dhparam.pem;
    resolver 8.8.8.8;

    # disable any limits to avoid HTTP 413 for large image uploads
    client_max_body_size 0;

    # required to avoid HTTP 411: see Issue #1486 (https://github.com/moby/moby/issues/1486)
    chunked_transfer_encoding on;

    location /v2/ {
      # Do not allow connections from docker 1.5 and earlier
      # docker pre-1.6.0 did not properly set the user agent on ping, catch "Go *" user agents
      if ($http_user_agent ~ "^(docker\/1\.(3|4|5(?!\.[0-9]-dev))|Go ).*$" ) {
        return 404;
      }

      # To add basic authentication to v2 use auth_basic setting.
      auth_basic "Registry realm";
      auth_basic_user_file /etc/nginx/conf.d/nginx.htpasswd;

      ## If $docker_distribution_api_version is empty, the header is not added.
      ## See the map directive above where this variable is defined.
      add_header 'Docker-Distribution-Api-Version' $docker_distribution_api_version always;

      proxy_pass                          http://docker-registry;
      proxy_set_header  Host              $http_host;   # required for docker client's sake
      proxy_set_header  X-Real-IP         $remote_addr; # pass on real client's IP
      proxy_set_header  X-Forwarded-For   $proxy_add_x_forwarded_for;
      proxy_set_header  X-Forwarded-Proto $scheme;
      proxy_read_timeout                  900;
    }
  }
}

Guarda el archivo pulsando Ctrl + X e introduciendo Y cuando se te pida una vez hayas terminado.

Paso 6 – Inicia el Registro Docker

Cambia al directorio del Registro Docker.

$ cd ~/docker-registry

Inicia el contenedor Docker.

$ docker compose up -d

Comprueba el estado de los contenedores.

$ docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED              STATUS              PORTS                                      NAMES
3328b7e36bb2   nginx:alpine   "/docker-entrypoint.…"   About a minute ago   Up 3 seconds        80/tcp, 0.0.0.0:443->443/tcp, :::443->443/tcp   docker-registry-nginx-1
bf7cdfc0e013   registry:2     "/entrypoint.sh /etc…"   About a minute ago   Up About a minute   5000/tcp                                 docker-registry-registry-1

Accede al Registro Docker.

$ docker login -u=user1 -p=password https://registry.example.com

Obtendrás el siguiente resultado.

WARNING! Using --password via the CLI is insecure. Use --password-stdin.
WARNING! Your password will be stored unencrypted in /home/username/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

También puedes abrir la URL https://registry.example.com/v2/ en tu navegador, y te pedirá un nombre de usuario y una contraseña. Deberías ver una página vacía con {}.

Puedes comprobar la URL en el terminal utilizando curl.

$ curl -u user1 -X GET https://registry.example.com/v2/
Enter host password for user 'user1':
{}

Descarga la última imagen docker de Ubuntu.

$ docker pull ubuntu:latest

Etiqueta esta imagen para el registro privado.

$ docker tag ubuntu:latest registry.example.com/ubuntu2204

Empuja la imagen al registro.

$ docker push registry.example.com/ubuntu2204

Comprueba si el push se ha realizado correctamente.

$ curl -u user1 -X GET https://registry.example.com/v2/_catalog
Enter host password for user 'user1':
{"repositories":["ubuntu2204"]}

Introduce tu contraseña de autenticación Nginx cuando se te solicite, y verás la lista de repositorios disponibles a través del registro.

Cierra la sesión utilizando el terminal para borrar las credenciales.

$ docker logout https://registry.example.com
Removing login credentials for registry.example.com

Comprueba la lista de imágenes Docker disponibles actualmente para su uso.

$ docker images
REPOSITORY                            TAG       IMAGE ID       CREATED       SIZE
registry                             2         8db46f9d7550   2 weeks ago   24.2MB
nginx                                alpine    8e75cbc5b25c   2 weeks ago   41MB
ubuntu                               latest    08d22c0ceb15   5 weeks ago   77.8MB
registry.example.com/ubuntu2204      latest    08d22c0ceb15   5 weeks ago   77.8MB

Paso 7 – Acceder y utilizar el registro Docker desde la máquina cliente

Accede a tu servidor-cliente. En el paso 1, instalamos Docker en la máquina cliente.

Accede al registro privado de Docker desde la máquina cliente.

$ docker login -u=user1 -p=password https://registry.example.com

Extrae la imagen de Ubuntu del registro.

$ docker pull registry.example.com/ubuntu2204

Lista todas las imágenes en tu máquina cliente.

$ docker images
REPOSITORY                        TAG        IMAGE ID       CREATED         SIZE
registry.example.com/ubuntu2204   latest     08d22c0ceb15   5 weeks ago   77.8MB

Crea y lanza un contenedor utilizando la imagen descargada.

$ docker run -it registry.example.com/ubuntu2204 /bin/bash

Accederás al Shell dentro del contenedor Ubuntu.

root@647899f255db:

Ejecuta el siguiente comando para comprobar la versión de Linux.

root@a2da49fdbea9$ cat /etc/os-release
PRETTY_NAME="Ubuntu 22.04.2 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.2 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy

Ahora, puedes empezar a utilizar tu registro Docker desde tus máquinas cliente.

Conclusión

Con esto concluye nuestro tutorial sobre la configuración de un registro Docker privado en un servidor Ubuntu 22.04 que utiliza Amazon S3 como almacenamiento. Si tienes alguna pregunta, publícala en los comentarios a continuación.

También te podría gustar...