Build de CoPyRIT y push al Container Registry
Objetivo
En el post anterior construimos la base de infraestructura: Resource Group, Azure Container Registry y Azure OpenAI con GPT-4o desplegado y verificado. En este post tomamos el código de PyRIT, construimos la imagen Docker de CoPyRIT y la subimos al ACR.
Al final de este post tendremos:
- Docker Engine instalado y funcionando en WSL2
- El repositorio de PyRIT clonado y analizado
- La imagen de CoPyRIT construida localmente (
pyrit:latest) - La imagen publicada en tu Azure Container Registry, lista para el despliegue del Post 4
WSL2 en lugar de Docker Desktop
Antes de entrar en materia, una nota sobre la decisión de entorno que tomamos en este lab.
El camino más obvio para trabajar con Docker en Windows es Docker Desktop una aplicación que instala un daemon de Docker y expone una interfaz gráfica para gestionar contenedores. Sin embargo, Docker Desktop requiere que VirtualMachinePlatform esté habilitado como feature del sistema operativo Windows, y su instalador usa DISM (Deployment Image Servicing and Management) para habilitarlo automáticamente.
En algunos sistemas Windows, especialmente con actualizaciones acumulativas recientes o ciertos estados del store de componentes, ese proceso de DISM falla con el críptico error 0x8000ffff. Es un problema de corrupción del CBS (Component Based Servicing) de Windows, no de Docker en sí. NOTA: Esto lo sé por que después de pelearme unas 2 horas no conseguía instalarlo y para ello necesitaría restablecer prácticamente Windows por que la parte del DISM estaba corrupta y no conseguía recuperarla como tal , así que… quién pueda/quiera con docker desktop puede hacerlo igual realmente saltandose los pasos previos de instalación en wsl2 que hacemos nosotros.
Por lo tanto , como comentaba la alternativa, y la que usamos en este lab, es Docker Engine directamente en WSL2 (Windows Subsystem for Linux 2). WSL2 ya corre un kernel Linux completo en una VM ligera gestionada por Windows y Docker Engine instalado dentro de esa distribución Linux funciona exactamente igual que en una máquina Linux nativa, sin depender del sistema de componentes de Windows.
Prerequisitos
Antes de continuar, verificamos que tenemos :
bash
# WSL2 con Ubuntu 24.04
wsl -d Ubuntu-24.04
# Dentro de Ubuntu — Docker instalado y arrancado
sudo service docker start
docker --version
# Docker version 29.5.3, build d1c06ef
# Azure CLI disponible en WSL2
az --version
az account show --query name --output tsv
# Azure for Students
Si Docker no está instalado en tu WSL2, el proceso de instalación es:
bash
# Dependencias
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
# Clave GPG y repositorio de Docker
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
| sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Instalación
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin
# Arranque del servicio
sudo service docker start
# Añadir tu usuario al grupo docker (evita usar sudo en cada comando)
sudo usermod -aG docker $USER
newgrp docker
Paso 1: Clonar el repositorio de PyRIT
bash
cd ~
git clone https://github.com/microsoft/PyRIT.git
cd PyRIT
Configurar git si no lo tienes (necesario para el script de build):
bash
git config --global user.email "[email protected]"
git config --global user.name "TuNombre"
El script de build hace git rev-parse HEAD para etiquetar la imagen con el hash del commit. Sin configuración de git el comando falla.
Paso 2: Entender la estructura del repositorio
Antes de ejecutar nada, veremos que hay en el repo:
bash
ls ~/PyRIT
CITATION.cff Makefile SUPPORT.md docker pyproject.toml
CODE_OF_CONDUCT LICENSE assets frontend pyrightconfig.json
MANIFEST.in README.md build_scripts gui-deploy.yml pyrit
NOTICE.txt SECURITY.md doc infra tests
Las carpetas relevantes para nosotros:
pyrit/ — el código Python del framework. Aquí están los targets, attacks, converters, scorers y el sistema de memoria. Es el núcleo de PyRIT.
frontend/ — el código de CoPyRIT. Una aplicación React con Fluent UI que constituye la interfaz gráfica. Cuando el contenedor arranca en modo gui, este frontend se sirve desde el backend FastAPI.
docker/ — todo lo relacionado con la construcción de la imagen. Contiene el Dockerfile, el script de build build_pyrit_docker.py, el script de arranque start.sh y documentación.
infra/ — templates Bicep para el despliegue en Azure. Los usaremos en el Post 4.
.devcontainer/ — el Dockerfile de la imagen base. Este es el punto de partida del build en dos fases que veremos a continuación.
gui-deploy.yml — el pipeline de CI/CD que usa el Microsoft AI Red Team internamente para desplegar CoPyRIT en Azure Container Apps vía Azure DevOps. Nos sirve como referencia para entender el proceso de despliegue.
Paso 3: La arquitectura en dos fases
Lo primero que llama la atención al inspeccionar el docker/Dockerfile es esta línea:
dockerfile
ARG BASE_IMAGE
FROM ${BASE_IMAGE} AS production
No hay una imagen base fija — el Dockerfile requiere que se le pase BASE_IMAGE como argumento. Esto es porque el build de CoPyRIT funciona en dos fases:
Fase 1 — Imagen base: se construye a partir de mcr.microsoft.com/devcontainers/python:3.14-bookworm y añade todo el entorno de desarrollo: Python 3.11 en un venv con uv, Node.js 24 para el frontend, el driver ODBC de Microsoft para Azure SQL, dependencias de sistema para el SDK de Azure Speech, y configuración del usuario vscode. Esta imagen pesa ~838 MB comprimida.
Fase 2 — Imagen de producción: toma la imagen base como base y añade el código fuente de PyRIT, instala las dependencias Python con todos los extras (speech, opencv, fairness_bias, fastapi, playwright), construye el frontend React, y configura el entrypoint.
Paso 4: Inspeccionar el script de build
bash
cat docker/build_pyrit_docker.py
El script acepta dos modos:
--source pypi --version X.Y.Z # Instala PyRIT desde PyPI
--source local # Instala desde el código fuente local
Para el lab usamos --source local porque queremos la versión más reciente del repo, incluyendo CoPyRIT aunque no esté aún publicada en PyPI. El script:
- Verifica si la imagen
pyrit-devcontainerya existe (la construye si no) - Obtiene el hash del commit actual con
git rev-parse HEAD - Ejecuta
docker buildpasando el devcontainer comoBASE_IMAGE - Etiqueta la imagen con el hash del commit y con
latest
Paso 5: Construir la imagen
bash
cd ~/PyRIT
sudo python3 docker/build_pyrit_docker.py --source local
Si es la primera vez que ejecutas esto, el build tarda 15-30 minutos — el devcontainer descarga Python, Node.js 24, instala el driver ODBC de Microsoft, compila dependencias nativas y construye el frontend React. Una vez construido el devcontainer, los builds posteriores son instantáneos gracias al sistema de caché de Docker (todas las capas CACHED).
Output esperado al completar:

Verifica que las imágenes existen:
bash
sudo docker images | grep pyrit

Sobre el tamaño: La imagen de producción pesa ~7.5 GB sin comprimir (1.79 GB comprimida). Es grande porque incluye Python con todos sus extras, Node.js, drivers ODBC, el SDK de Azure Speech, y el frontend compilado. Para un lab esto es aceptable; en producción se podría optimizar con un build multi-stage más agresivo.
Paso 6: Taggear la imagen para el ACR
Azure Container Registry espera imágenes con el formato <loginServer>/<repositorio>:<tag>. Taggeamos la imagen local:
bash
sudo docker tag pyrit:latest acrcyberlabpyrit.azurecr.io/copyrit:latest
Por qué
copyritcomo nombre del repositorio: Seguimos la convención del pipeline oficial de Microsoft (gui-deploy.yml) que referencia la imagen comocopyrit.
Verificamos el tag:
bash
sudo docker images | grep acrcyberlabpyrit

Paso 7: Autenticar contra el ACR y hacer push
El ACR que creamos en el Post 2 tiene --admin-enabled false — si os acordáis , ya dijimos que deshabilitamos el usuario administrador estático. Esto significa que no podemos autenticarnos con usuario/password fijos; necesitamos un token de acceso emitido por Azure AD.NOTA: Más adelante veréis que tendremos un post para el tema de la autenticación , ya que usaremos caddy por que entra era imposible debido a restricciones de la suscripción de azure for student.
El comando az acr login normalmente hace esto de forma transparente, pero en WSL2 hay un problema: busca Docker en el PATH del sistema Windows, no en el PATH de Linux. El resultado es el error DOCKER_COMMAND_ERROR.
La solución es obtener el token manualmente y pasárselo a Docker directamente:
bash
# Obtener el refresh token del ACR
ACR_TOKEN=$(az acr login --name acrcyberlabpyrit --expose-token --output tsv --query accessToken)
# Autenticar Docker con ese token
echo $ACR_TOKEN | sudo docker login acrcyberlabpyrit.azurecr.io \
--username 00000000-0000-0000-0000-000000000000 \
--password-stdin
Sobre el username
00000000-0000-0000-0000-000000000000: No es un placeholder es el username especial que ACR usa cuando la autenticación es vía token de Azure AD en lugar de credenciales de usuario.
Output esperado:
Login Succeeded
Ahora el push:
bash
sudo docker push acrcyberlabpyrit.azurecr.io/copyrit:latest
La imagen pesa 1.79 GB comprimida — el push tardará 5-15 minutos dependiendo de tu conexión. Verás el progreso capa a capa:

Paso 8: Verificar que la imagen está en el ACR
bash
az acr repository show-tags \
--name acrcyberlabpyrit \
--repository copyrit \
--output table
Output esperado:
Result
--------
latest
También se puede verificar desde el portal de Azure: Container Registry → acrcyberlabpyrit → Repositories → copyrit.

El modo de arranque: PYRIT_MODE
Antes de acabar poor hoy, vale la pena entender cómo funciona el entrypoint del contenedor, el start.sh ,porque condiciona cómo lo configuraremos en el Post 4.
El contenedor soporta dos modos de arranque controlados por la variable de entorno PYRIT_MODE:
PYRIT_MODE=jupyter — arranca JupyterLab en el puerto 8888. Útil para explorar los notebooks de documentación de PyRIT de forma interactiva.
PYRIT_MODE=gui — arranca CoPyRIT en el puerto 8000. Este es el modo que nos interesa para el lab.
En modo gui, el script también gestiona la configuración de la base de datos:
- Si
AZURE_SQL_SERVERestá definido → usa Azure SQL - Si no → usa SQLite local
Para el lab usaremos SQLite (sin Azure SQL) para simplificar la configuración y ahorrar crédito de Azure for Students.
El contenedor también lee la variable PYRIT_ENV_CONTENTS — si está definida, escribe su contenido en ~/.pyrit/.env dentro del contenedor. Así es como pasamos las credenciales de Azure OpenAI al contenedor sin hardcodearlas en la imagen.
Arquitectura del Lab hasta ahora

Próximos pasos
En el siguiente post vamos a desplegar CoPyRIT en Azure Container Apps: crearemos el Container App, configuraremos la autenticación con Entra ID, pasaremos las credenciales de Azure OpenAI vía variables de entorno, y verificaremos que CoPyRIT arranca y conecta con GPT-4o.
YA QUEDA MENOS!!!