Un despliegue canary publica una versión nueva de tu aplicación solo para una pequeña parte de los usuarios, por ejemplo el 5 %, mientras el resto sigue en la versión estable. Si la nueva versión se comporta bien, aumentas el porcentaje hasta el 100 %; si falla, devuelves todo el tráfico a la versión estable sin que la mayoría de usuarios lo note.

En este tutorial configurarás Nginx en Ubuntu 24.04 para repartir el tráfico entre dos versiones de una aplicación con la directiva split_clients, forzarás la versión canary con una cookie para hacer pruebas, registrarás qué versión atiende cada petición para comparar errores y latencia, y crearás un pequeño comando para cambiar el porcentaje o revertir de forma segura.

Requisitos previos

  • Un servidor con Ubuntu 24.04, por ejemplo un VPS de CubePath.
  • Un usuario no root con privilegios sudo.
  • Nginx instalado (sudo apt install nginx).
  • Dos versiones de tu aplicación escuchando en puertos distintos del mismo servidor o de tu red privada. En esta guía la versión estable escucha en 127.0.0.1:8081 y la canary en 127.0.0.1:8082. Si usas Docker, publica cada contenedor en uno de esos puertos.
  • Un dominio apuntando al servidor. Usaremos tu_dominio como marcador.

Cómo funciona el reparto de tráfico

Nginx ofrece dos piezas que encajan bien para un canary sin módulos adicionales:

PiezaQué hace
split_clientsCalcula un hash de una clave (por ejemplo IP + User-Agent) y asigna un valor según porcentajes. El mismo cliente cae siempre en el mismo grupo, así que no salta de versión entre peticiones.
mapPermite sobrescribir ese resultado, por ejemplo con una cookie canary=always para que tu equipo pruebe la versión nueva antes de abrirla a usuarios.

El valor final es el nombre de un bloque upstream, que Nginx usa en proxy_pass. Cambiar el porcentaje es editar un archivo pequeño y recargar Nginx, lo que no corta conexiones activas.

Paso 1: Preparar dos versiones de prueba (opcional)

Si todavía no tienes tu aplicación desplegada en dos puertos, crea dos servidores de prueba con Python para seguir el tutorial. Cada uno devuelve un texto que identifica la versión:

sudo mkdir -p /srv/app-stable /srv/app-canary
echo "version estable v1" | sudo tee /srv/app-stable/index.html
echo "version canary v2" | sudo tee /srv/app-canary/index.html

Arráncalos como unidades temporales de systemd para que sigan en marcha al cerrar la sesión:

sudo systemd-run --unit=app-stable python3 -m http.server 8081 --bind 127.0.0.1 --directory /srv/app-stable
sudo systemd-run --unit=app-canary python3 -m http.server 8082 --bind 127.0.0.1 --directory /srv/app-canary

Comprueba que responden:

curl -s http://127.0.0.1:8081/
curl -s http://127.0.0.1:8082/
version estable v1
version canary v2

Paso 2: Crear el archivo con el porcentaje canary

Guarda la regla de reparto en un archivo propio. Así el porcentaje se cambia sin tocar el resto de la configuración y es fácil automatizarlo.

sudo nano /etc/nginx/canary-split.conf

Empieza enviando el 5 % del tráfico a la versión canary:

split_clients "${remote_addr}${http_user_agent}" $canary_split {
    5%  app_canary;
    *   app_stable;
}

La clave combina la IP y el User-Agent del cliente. El asterisco recoge el resto del tráfico.

Paso 3: Configurar el sitio en Nginx

Ahora crea el sitio que define los dos upstream, la anulación por cookie, un formato de log que registra la versión y el proxy.

sudo nano /etc/nginx/sites-available/app
upstream app_stable {
    server 127.0.0.1:8081 max_fails=3 fail_timeout=10s;
}

upstream app_canary {
    server 127.0.0.1:8082 max_fails=3 fail_timeout=10s;
    # Si la canary cae, las peticiones pasan a la estable
    server 127.0.0.1:8081 backup;
}

include /etc/nginx/canary-split.conf;

# La cookie canary=always fuerza la versión nueva, canary=never la estable
map $cookie_canary $app_upstream {
    "always"  app_canary;
    "never"   app_stable;
    default   $canary_split;
}

log_format canary '$time_iso8601 $app_upstream $status $request_time '
                  '$upstream_addr "$request"';

server {
    listen 80;
    listen [::]:80;
    server_name tu_dominio;

    access_log /var/log/nginx/canary_access.log canary;

    location / {
        proxy_pass http://$app_upstream;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # Cabecera para comprobar qué versión respondió
        add_header X-App-Upstream $app_upstream always;
    }
}

Puntos importantes de esta configuración:

  • proxy_pass http://$app_upstream; funciona sin resolver DNS porque el valor coincide con el nombre de un upstream definido.
  • max_fails y fail_timeout retiran temporalmente un backend que devuelve errores de conexión.
  • El servidor backup del grupo canary evita que los usuarios del 5 % vean errores si la versión nueva no arranca.

Desactiva el sitio por defecto si ocupa el puerto 80 con el mismo nombre, activa el nuevo y valida la sintaxis:

sudo rm -f /etc/nginx/sites-enabled/default
sudo ln -s /etc/nginx/sites-available/app /etc/nginx/sites-enabled/app
sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Recarga Nginx:

sudo systemctl reload nginx

Paso 4: Verificar el reparto

Simula 1000 clientes distintos variando el User-Agent y cuenta qué versión responde a cada uno:

for i in $(seq 1 1000); do
  curl -s -A "cliente-$i" -H "Host: tu_dominio" http://127.0.0.1/
done | sort | uniq -c

El resultado debe rondar el 95 % / 5 %:

     52 version canary v2
    948 version estable v1

Comprueba ahora que el mismo cliente siempre recibe la misma versión y que la cookie fuerza la canary:

curl -sI -A "cliente-7" -H "Host: tu_dominio" http://127.0.0.1/ | grep -i x-app-upstream
curl -s -H "Host: tu_dominio" --cookie "canary=always" http://127.0.0.1/
X-App-Upstream: app_stable
version canary v2

Para que tu equipo pruebe la versión nueva desde el navegador, basta con crear la cookie canary con valor always para tu_dominio desde las herramientas de desarrollo.

Paso 5: Crear un comando para cambiar el porcentaje

Editar el archivo a mano en cada fase funciona, pero es fácil dejar un error de sintaxis y recargar Nginx con él. Este script reescribe canary-split.conf, valida la configuración y solo recarga si nginx -t pasa; si falla, restaura el archivo anterior.

sudo nano /usr/local/sbin/canary-weight
#!/usr/bin/env bash
set -euo pipefail

conf=/etc/nginx/canary-split.conf
pct="${1:-}"

if [[ ! "$pct" =~ ^[0-9]+$ ]] || (( pct > 100 )); then
    echo "Uso: canary-weight <0-100>" >&2
    exit 1
fi

backup="$(mktemp)"
trap 'rm -f "$backup"' EXIT
cp "$conf" "$backup"

{
    echo 'split_clients "${remote_addr}${http_user_agent}" $canary_split {'
    if (( pct == 0 )); then
        echo '    *   app_stable;'
    elif (( pct == 100 )); then
        echo '    *   app_canary;'
    else
        echo "    ${pct}%  app_canary;"
        echo '    *   app_stable;'
    fi
    echo '}'
} > "$conf"

if nginx -t -q; then
    systemctl reload nginx
    echo "Tráfico canary: ${pct}%"
else
    cp "$backup" "$conf"
    echo "La configuración no es válida, se ha restaurado la anterior" >&2
    exit 1
fi

Hazlo ejecutable y pruébalo subiendo al 25 %:

sudo chmod 755 /usr/local/sbin/canary-weight
sudo canary-weight 25
Tráfico canary: 25%

Repite la prueba del paso 4: ahora alrededor de 250 de las 1000 peticiones deberían llegar a la versión canary.

Paso 6: Comparar errores y latencia por versión

La decisión de avanzar o revertir debe basarse en datos. El log canary_access.log registra en cada línea el upstream, el código HTTP y el tiempo de respuesta, así que un solo awk compara ambas versiones. Este comando analiza las últimas 50 000 peticiones:

sudo tail -n 50000 /var/log/nginx/canary_access.log | awk '
{
    n[$2]++
    t[$2] += $4
    if ($3 >= 500) e[$2]++
}
END {
    for (u in n)
        printf "%-11s peticiones=%-7d 5xx=%-5d (%.2f%%) latencia_media=%.3fs\n",
               u, n[u], e[u], 100 * e[u] / n[u], t[u] / n[u]
}'
app_stable  peticiones=37412   5xx=18    (0.05%) latencia_media=0.041s
app_canary  peticiones=12588   5xx=9     (0.07%) latencia_media=0.044s

Si la canary muestra una tasa de 5xx o una latencia claramente superior a la estable, revierte. Si usas Prometheus o otra herramienta de observabilidad, aplica el mismo criterio con las métricas de tu aplicación, separadas por versión.

Paso 7: Promocionar o revertir

Un plan de avance razonable es:

FaseTráfico canaryTiempo mínimoCriterio para avanzar
15 %15-30 min5xx y latencia similares a la estable
225 %30 minSin errores nuevos en los logs de la aplicación
350 %1 hMétricas de negocio sin cambios
4100 %-Promoción completa

Revertir es inmediato: devuelve todo el tráfico a la versión estable.

sudo canary-weight 0

Promocionar requiere que la versión nueva pase a ser la estable. El proceso seguro es:

  1. Envía el 100 % a la canary con sudo canary-weight 100.
  2. Despliega la versión nueva en el puerto de la estable (8081) y comprueba que responde con curl -s http://127.0.0.1:8081/.
  3. Devuelve el tráfico a la estable con sudo canary-weight 0.

La versión canary queda libre para el siguiente despliegue.

Solución de problemas

nginx -t falla con "split_clients" directive is not allowed here. El include de canary-split.conf debe estar fuera del bloque server, al nivel de los upstream y el map.

Aparecen errores 502 en el log. Uno de los backends no responde. Comprueba cuál con curl -s http://127.0.0.1:8081/ y curl -s http://127.0.0.1:8082/, y revisa /var/log/nginx/error.log. Si es la canary, revierte con sudo canary-weight 0 mientras lo investigas.

Todas las peticiones van a la estable aunque el porcentaje sea alto. Si las pruebas salen siempre desde la misma IP con el mismo User-Agent, el hash da siempre el mismo resultado. Varía el User-Agent como en el paso 4 o usa la cookie canary=always.

Los usuarios detrás de un balanceador llegan con la misma IP. Configura el módulo realip de Nginx (set_real_ip_from y real_ip_header) para que $remote_addr sea la IP real del cliente; si no, el reparto es muy desigual.

Conclusión

Has configurado un despliegue canary con Nginx en Ubuntu 24.04: un porcentaje ajustable de usuarios recibe la versión nueva de forma estable, tu equipo puede forzarla con una cookie, cada petición queda registrada con la versión que la atendió y un único comando cambia el porcentaje o revierte con validación previa.

Como siguientes pasos, puedes añadir HTTPS con Certbot (sudo certbot --nginx -d tu_dominio), llamar a canary-weight desde tu pipeline de CI/CD en cada fase, y exportar las métricas por versión a Prometheus para automatizar la decisión de avanzar o revertir.