Cómo configurar un registro Docker privado en Rocky Linux 8

Si trabajas para una organización y quieres mantener tus imágenes Docker en casa 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 basado en Rocky Linux 8 utilizando Amazon S3 como ubicación de almacenamiento.

Requisitos previos

  • Dos servidores Linux con Rocky Linux 8. 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.

Paso 1 – Configurar el cortafuegos

El primer paso es configurar el cortafuegos. Rocky Linux utiliza el cortafuegos Firewalld. Comprueba el estado del cortafuegos.

$ sudo firewall-cmd --state
running

El cortafuegos funciona con diferentes zonas, y la zona pública es la que utilizaremos por defecto. Enumera todos los servicios y puertos activos en el cortafuegos.

$ sudo firewall-cmd --permanent --list-services

Debería mostrar el siguiente resultado.

cockpit dhcpv6-client ssh

Permite los puertos HTTP y HTTPS.

$ sudo firewall-cmd --permanent --add-service=http
$ sudo firewall-cmd --permanent --add-service=https

Vuelve a comprobar el estado del cortafuegos.

$ sudo firewall-cmd --permanent --list-services

Deberías ver una salida similar.

cockpit dhcpv6-client http https ssh

Recarga el cortafuegos para activar los cambios.

$ sudo firewall-cmd --reload

Paso 2 – Instalar Docker y Docker Compose

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

Instala el repositorio oficial de Docker.

$ sudo dnf install yum-utils
$ sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo

Instala Docker.

$ sudo dnf install docker-ce docker-ce-cli containerd.io

Habilita y ejecuta el demonio Docker.

$ sudo systemctl enable docker --now

Añade tu usuario del sistema al grupo Docker para evitar el uso de sudo para ejecutar los comandos Docker.

$ sudo usermod -aG docker $(whoami)

Vuelve a conectarte a tu servidor después de cerrar la sesión para activar el cambio.

Descarga e instala la última versión estable de Docker Compose.

$ sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

Aplica los permisos de ejecución al archivo binario descargado.

$ sudo chmod +x /usr/local/bin/docker-compose

Instala el script Docker-compose Bash Completion.

$ sudo curl \
    -L https://raw.githubusercontent.com/docker/compose/1.29.2/contrib/completion/bash/docker-compose \
    -o /etc/bash_completion.d/docker-compose

Recarga la configuración de tu perfil para que funcione el bash-completion.

$ source ~/.bashrc

Paso 3 – Configurar el Registro Docker

Crea 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 registros de Nginx.

$ mkdir logs

Crea un cubo 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 establecer el archivo de configuración con algunos ajustes importantes. Estos ajustes también se pueden definir en el archivo docker-compose.yml, pero es mucho mejor tener un archivo separado.

Crea un cubo con los siguientes ajustes.

  • La ACL debe estar desactivada.
  • El acceso público al cubo debe estar desactivado.
  • El versionado del cubo debe estar deshabilitado.
  • Habilita la encriptación del cubo utilizando las claves gestionadas de Amazon S3. (SSE-S3)
  • El bloqueo de objetos debe estar deshabilitado.

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 el S3_BUCKET_NAME por el nombre de tu cubo de S3.

Anota la clave secreta, el valor secreto y la región de tu cubo para utilizarla 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.

version: '3.3'
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=AKIA3FIG4NVFCJ6STMUA
      - 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 utilizamos la última etiqueta porque puede causar problemas en el caso de una actualización de versión mayor. Establecerla como 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 mayor, lo que puede introducir cambios de ruptura.

  2. El contenedor de registro está configurado para que se reinicie siempre en caso de fallo o cierre inesperado.

  3. Hemos configurado varias variables de entorno para el almacenamiento de Amazon S3. Vamos a repasarlas 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 cubo 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 mayor que 5MB (5 * 1024 * 1024).
    • REGISTRY_STORAGE_S3_SECURE – ponlo a true si vas a usar 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 la autenticación de AWS. Si obtienes errores relacionados con el inicio de sesión en S3, establécelo como falso.
    • REGISTRY_STORAGE_S3_ROOTDIRECTORY – establece el directorio raíz de tu cubo 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 almacenamos en la memoria. También puedes configurarlo para utilizar 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 en falso.
  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 asegura que todas las configuraciones 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 de 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 dnf install httpd-tools

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 es para utilizar 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 el comando de nuevo, 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

Para instalar un certificado SSL con Let’s Encrypt, necesitamos descargar la herramienta Certbot, que está disponible en el repositorio de Epel.

Instala el repositorio EPEL y Certbot.

$ sudo dnf install epel-release 
$ sudo dnf install certbot

Genera 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 -out /etc/ssl/certs/dhparam.pem 4096

Prueba la renovación del certificado.

$ sudo certbot renew --dry-run

Si la prueba en seco tiene éxito, significa que tus certificados se renovarán automáticamente.

Copia el archivo Dhparam en el contenedor

Copia el certificado de 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 del front-end para el servidor de registro de Docker. El registro de 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.d/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 te lo pida una vez hayas terminado.

Configura SELinux para permitir las conexiones de red para el Registro Docker Privado.

$ sudo setsebool -P httpd_can_network_connect on

Paso 6 – Iniciar el Registro Docker

Cambia al directorio del Registro Docker.

$ cd ~/docker-registry

Lanza el contenedor Docker.

$ docker-compose up -d

Comprueba el estado de los contenedores.

$ docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS         PORTS                                           NAMES
88d6addc1687   nginx:alpine   "/docker-entrypoint.…"   5 minutes ago   Up 5 minutes   80/tcp, 0.0.0.0:443->443/tcp, :::443->443/tcp   docker-registry_nginx_1
2b112edc1c72   registry:2     "/entrypoint.sh /etc…"   5 minutes ago   Up 5 minutes   5000/tcp                                        docker-registry_registry_1

Accede al registro Docker.

$ docker login -u=testuser -p=testpassword https://registry.example.com

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 testuser -X GET https://registry.nspeaks.xyz/v2/
Enter host password for user 'testuser':
{}

Descarga la última imagen docker de Ubuntu.

$ docker pull ubuntu:latest

Marca esta imagen para el registro privado.

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

Empuja la imagen al registro.

$ docker push registry.example.com/ubuntu2004

Comprueba si el push se ha realizado con éxito.

$ curl -u testuser -X GET https://registry.nspeaks.xyz/v2/_catalog
Enter host password for user 'testuser':
{"repositories":["ubuntu2004"]}

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

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

$ docker images
REPOSITORY                            TAG       IMAGE ID       CREATED       SIZE
registry                              2         d3241e050fc9   5 days ago    24.2MB
nginx                                 alpine    53722defe627   5 days ago    23.4MB
httpd                                 2         118b6abfbf55   5 days ago    144MB
ubuntu                                latest    ff0fea8310f3   2 weeks ago   72.8MB
registry.nspeaks.xyz/ubuntu2004       latest    ff0fea8310f3   2 weeks ago   72.8MB

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

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

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

$ docker login -u=testuser -p=testpassword https://registry.example.com

Saca la imagen de Ubuntu del registro.

$ docker pull registry.example.com/ubuntu2004

Enumera todas las imágenes de tu máquina cliente.

$ docker images
REPOSITORY                        TAG        IMAGE ID       CREATED         SIZE
registry.nspeaks.xyz/ubuntu2004   latest     ff0fea8310f3   2 weeks ago     72.8MB

Crea y lanza un contenedor con la imagen descargada.

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

Entrarás en el Shell dentro del contenedor de Ubuntu.

root@a2da49fdbea9:

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

root@a2da49fdbea9$ cat /etc/os-release
NAME="Ubuntu"
VERSION="20.04.4 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04.4 LTS"
VERSION_ID="20.04"
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"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal

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 basado en Rocky Linux 8 que utiliza Amazon S3 como almacenamiento. Si tienes alguna pregunta, publícala en los comentarios de abajo.

También te podría gustar...