# Migración masiva de imágenes


Esta guía te muestra cómo migrar imágenes de Docker de forma masiva entre organizaciones o espacios de nombres de Docker Hub. Tanto si estás consolidando repositorios, cambiando la estructura de la organización o moviendo imágenes a una nueva cuenta, estas técnicas te ayudan a migrar de manera eficiente mientras preservas la integridad de las imágenes.

Este tema está estructurado para aumentar de escala:

1. [Migrar la etiqueta de una sola imagen](#migrar-la-etiqueta-de-una-sola-imagen)
2. [Migrar todas las etiquetas de un repositorio](#migrar-todas-las-etiquetas-de-un-repositorio)
3. [Migrar múltiples repositorios](#migrar-multiples-repositorios)

La herramienta recomendada para este flujo de trabajo es `crane`. También se muestra una alternativa equivalente que utiliza `regctl`. Ambas herramientas realizan copias directamente de registro a registro sin descargar las imágenes localmente y preservan las imágenes multiarquitectura.

Se recomienda `crane` por su simplicidad y su flujo de trabajo centrado en la copia de imágenes. `regctl` también es una buena opción, especialmente si ya la utilizas para tareas de gestión de registros más amplias más allá de la copia de imágenes.

> [!NOTE]
>
> Los flujos de trabajo principales de este tema operan únicamente con imágenes etiquetadas. Los manifiestos sin etiqueta o el contenido al que ya no se pueda acceder mediante etiquetas no se migran. En la práctica, suelen ser artefactos no utilizados, pero ten en cuenta esta limitación antes de la migración. Aunque puedes migrar manifiestos específicos sin etiqueta utilizando [referencias de resumen (digest)](#migrar-por-resumen-digest), no existe ninguna API para enumerar los manifiestos sin etiqueta en un repositorio.

## Requisitos previos

Antes de comenzar, asegúrate de tener:

- Una de las siguientes herramientas instalada y disponible en tu `$PATH`:
  - [`crane`](https://github.com/google/go-containerregistry)
  - [`regctl`](https://regclient.org/usage/regctl/)
- Acceso de escritura (push) tanto en la organización de origen como en la de destino
- Autenticación del registro configurada para la herramienta elegida

## Autenticarse en los registros

Ambas herramientas se autentican directamente contra los registros:

- `crane` utiliza los asistentes de credenciales de Docker y `~/.docker/config.json`. Consulta la [documentación de crane](https://github.com/google/go-containerregistry/tree/main/cmd/crane/doc).
- `regctl` utiliza su propio archivo de configuración y puede importar credenciales de Docker. Consulta la [documentación de regctl](https://github.com/regclient/regclient/tree/main/docs).

Sigue las instrucciones de autenticación para el registro y la herramienta que prefieras.

## Migrar la etiqueta de una sola imagen

Este es el escenario de migración más simple y común.

El siguiente script de ejemplo copia el manifiesto de la imagen directamente entre registros y preserva las imágenes multiarquitectura cuando están presentes. Repite este proceso para cada etiqueta que quieras migrar. Reemplaza los valores de las variables de entorno con los nombres de tus organizaciones de origen y destino, el nombre del repositorio y la etiqueta.

```bash
#!/usr/bin/env bash
set -euo pipefail

SRC_ORG="oldorg"
DEST_ORG="neworg"
REPO="myapp"
TAG="1.2.3"

SRC_IMAGE="${SRC_ORG}/${REPO}:${TAG}"
DEST_IMAGE="${DEST_ORG}/${REPO}:${TAG}"

# Usando crane (recomendado)
crane cp "${SRC_IMAGE}" "${DEST_IMAGE}"

# Usando regctl (alternativa)
# regctl image copy "${SRC_IMAGE}" "${DEST_IMAGE}"
```

### Migrar por resumen (digest)

Para migrar una imagen específica por su resumen en lugar de por su etiqueta, usa el resumen en la referencia de origen. Esto es útil cuando necesitas migrar una versión exacta de la imagen, incluso si la etiqueta se ha actualizado. Reemplaza los valores de las variables de entorno con los nombres de tus organizaciones de origen y destino, el nombre del repositorio, el resumen y la etiqueta. Puedes elegir entre `crane` y `regctl` para la operación de copia.

```bash
#!/usr/bin/env bash
set -euo pipefail

SRC_ORG="oldorg"
DEST_ORG="neworg"
REPO="myapp"
DIGEST="sha256:abcd1234..."
TAG="stable"

SRC_IMAGE="${SRC_ORG}/${REPO}@${DIGEST}"
DEST_IMAGE="${DEST_ORG}/${REPO}:${TAG}"

# Usando crane
crane cp "${SRC_IMAGE}" "${DEST_IMAGE}"

# Usando regctl
# regctl image copy "${SRC_IMAGE}" "${DEST_IMAGE}"
```

## Migrar todas las etiquetas de un repositorio

Para migrar cada imagen etiquetada en un repositorio, usa la API de Docker Hub para enumerar las etiquetas y copiar cada una. El siguiente script de ejemplo recupera todas las etiquetas de un repositorio determinado y las migra en un bucle. Este enfoque se escala a repositorios con muchas etiquetas sin sobrecargar los recursos locales. Ten en cuenta que existe un límite de frecuencia en las solicitudes a Docker Hub, por lo que es posible que debas añadir retrasos o gestionar la paginación en repositorios grandes.

Reemplaza los valores de las variables de entorno con los nombres de tus organizaciones de origen y destino y el nombre del repositorio. Si tu repositorio de origen es privado, configura también `HUB_USER` y `HUB_TOKEN` con credenciales que tengan acceso de lectura (pull). También puedes elegir entre `crane` y `regctl` para la operación de copia.

```bash
#!/usr/bin/env bash
set -euo pipefail

# Usa variables de entorno si están configuradas; de lo contrario, usa valores por defecto
SRC_ORG="${SRC_ORG:-oldorg}"
DEST_ORG="${DEST_ORG:-neworg}"
REPO="${REPO:-myapp}"

# Opcional: para repositorios privados
# HUB_USER="your-username"
# HUB_TOKEN="your-access-token"
# AUTH="-u ${HUB_USER}:${HUB_TOKEN}"
AUTH=""

TOOL="crane"   # o: TOOL="regctl"

TAGS_URL="https://hub.docker.com/v2/repositories/${SRC_ORG}/${REPO}/tags?page_size=100"

while [[ -n "${TAGS_URL}" && "${TAGS_URL}" != "null" ]]; do
  RESP=$(curl -fsSL ${AUTH} "${TAGS_URL}")

  echo "${RESP}" | jq -r '.results[].name' | while read -r TAG; do
    [[ -z "${TAG}" ]] && continue

    SRC_IMAGE="${SRC_ORG}/${REPO}:${TAG}"
    DEST_IMAGE="${DEST_ORG}/${REPO}:${TAG}"

    echo "Migrando ${SRC_IMAGE} → ${DEST_IMAGE}"

    case "${TOOL}" in
      crane)
        crane cp "${SRC_IMAGE}" "${DEST_IMAGE}"
        ;;
      regctl)
        regctl image copy "${SRC_IMAGE}" "${DEST_IMAGE}"
        ;;
    esac
  done

  TAGS_URL=$(echo "${RESP}" | jq -r '.next')
done
```

> [!NOTE]
>
> Docker Hub crea automáticamente el repositorio de destino en la primera subida si tu cuenta tiene los permisos necesarios.

## Migrar múltiples repositorios

Para migrar varios repositorios, crea una lista y ejecuta el script de repositorio único para cada uno.

Por ejemplo, crea un archivo `repos.txt` con los nombres de los repositorios:

```text
api
web
worker
```

Guarda el script de la sección anterior como `migrate-single-repo.sh`. Luego, ejecuta el siguiente script de ejemplo que procesa cada repositorio del archivo. Reemplaza los valores de las variables de entorno con los nombres de tus organizaciones de origen y destino.

```bash
#!/usr/bin/env bash
set -euo pipefail

SRC_ORG="oldorg"
DEST_ORG="neworg"

while read -r REPO; do
  [[ -z "${REPO}" ]] && continue
  echo "==== Migrando repo: ${REPO}"
  SRC_ORG="${SRC_ORG}" DEST_ORG="${DEST_ORG}" REPO="${REPO}" ./migrate-single-repo.sh
done < repos.txt
```

## Verificar la integridad de la migración

Después de copiar, verifica que el origen y el destino coincidan comparando sus resúmenes (digests).

### Verificación básica del resumen

El siguiente script de ejemplo recupera el resumen de la imagen para una etiqueta específica tanto del origen como del destino y los compara. Si los resúmenes coinciden, la migración se ha realizado correctamente. Reemplaza los valores de las variables de entorno con los nombres de tus organizaciones de origen y destino, el nombre del repositorio y la etiqueta. Puedes elegir entre `crane` y `regctl` para obtener los resúmenes.

```bash
#!/usr/bin/env bash
set -euo pipefail

SRC_ORG="oldorg"
DEST_ORG="neworg"
REPO="myapp"
TAG="1.2.3"

SRC_IMAGE="${SRC_ORG}/${REPO}:${TAG}"
DEST_IMAGE="${DEST_ORG}/${REPO}:${TAG}"

# Usando crane
SRC_DIGEST=$(crane digest "${SRC_IMAGE}")
DEST_DIGEST=$(crane digest "${DEST_IMAGE}")

# Usando regctl (alternativa)
# SRC_DIGEST=$(regctl image digest "${SRC_IMAGE}")
# DEST_DIGEST=$(regctl image digest "${DEST_IMAGE}")

echo "Origen:      ${SRC_DIGEST}"
echo "Destino:     ${DEST_DIGEST}"

if [[ "${SRC_DIGEST}" == "${DEST_DIGEST}" ]]; then
  echo "✓ Migración verificada: los resúmenes coinciden"
else
  echo "✗ Error en la migración: los resúmenes no coinciden"
  exit 1
fi
```

### Verificación de multiarquitectura

Para imágenes multiarquitectura, verifica también la lista de manifiestos para asegurarte de que todas las plataformas se copiaron correctamente. Reemplaza los valores de las variables de entorno con los nombres de tus organizaciones de origen y destino, el nombre del repositorio y la etiqueta. Puedes elegir entre `crane` y `regctl` para obtener los manifiestos.

```bash
#!/usr/bin/env bash
set -euo pipefail

SRC_ORG="oldorg"
DEST_ORG="neworg"
REPO="myapp"
TAG="1.2.3"

SRC_IMAGE="${SRC_ORG}/${REPO}:${TAG}"
DEST_IMAGE="${DEST_ORG}/${REPO}:${TAG}"

# Usando crane
SRC_MANIFEST=$(crane manifest "${SRC_IMAGE}")
DEST_MANIFEST=$(crane manifest "${DEST_IMAGE}")

# Usando regctl (alternativa)
# SRC_MANIFEST=$(regctl image manifest --format raw-body "${SRC_IMAGE}")
# DEST_MANIFEST=$(regctl image manifest --format raw-body "${DEST_IMAGE}")

# Comprueba si es una lista de manifiestos (multiarquitectura)
if echo "${SRC_MANIFEST}" | jq -e '.manifests' > /dev/null 2>&1; then
  echo "Imagen multiarquitectura detectada"
  
  # Compara la lista de plataformas
  SRC_PLATFORMS=$(echo "${SRC_MANIFEST}" | jq -r '.manifests[] | "\(.platform.os)/\(.platform.architecture)"' | sort)
  DEST_PLATFORMS=$(echo "${DEST_MANIFEST}" | jq -r '.manifests[] | "\(.platform.os)/\(.platform.architecture)"' | sort)
  
  if [[ "${SRC_PLATFORMS}" == "${DEST_PLATFORMS}" ]]; then
    echo "✓ La lista de plataformas coincide:"
    echo "${SRC_PLATFORMS}"
  else
    echo "✗ Las listas de plataformas no coinciden"
    echo "Plataformas de origen:"
    echo "${SRC_PLATFORMS}"
    echo "Plataformas de destino:"
    echo "${DEST_PLATFORMS}"
    exit 1
  fi
else
  echo "Imagen de arquitectura única"
fi
```

## Completar la migración

Después de migrar tus imágenes, completa estos pasos adicionales:

1. Copia los metadatos del repositorio en la interfaz de usuario de Docker Hub o a través de la API:
   - Contenido del README
   - Descripción del repositorio
   - Temas (topics) y etiquetas

2. Configura los ajustes del repositorio para que coincidan con el origen:
   - Visibilidad (pública o privada)
   - Permisos de equipo y controles de acceso

3. Reconfigura las integraciones en la organización de destino:
   - Webhooks
   - Compilaciones automatizadas
   - Escáneres de seguridad

4. Actualiza las referencias de imágenes en tus proyectos:
   - Cambia `FROM oldorg/repo:tag` por `FROM neworg/repo:tag` en tus Dockerfiles
   - Actualiza las configuraciones de despliegue
   - Actualiza la documentación

5. Declara obsoleta la ubicación antigua:
   - Actualiza la descripción del repositorio de origen para que apunte a la nueva ubicación
   - Considera añadir un período de gracia antes de hacer que el repositorio antiguo sea privado o de solo lectura
