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:8081y la canary en127.0.0.1:8082. Si usas Docker, publica cada contenedor en uno de esos puertos. - Un dominio apuntando al servidor. Usaremos
tu_dominiocomo marcador.
Cómo funciona el reparto de tráfico
Nginx ofrece dos piezas que encajan bien para un canary sin módulos adicionales:
| Pieza | Qué hace |
|---|---|
split_clients | Calcula 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. |
map | Permite 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
NotaEstos servidores son solo para practicar. Cuando termines, elimínalos con
sudo systemctl stop app-stable app-canary.
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 unupstreamdefinido.max_failsyfail_timeoutretiran temporalmente un backend que devuelve errores de conexión.- El servidor
backupdel 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:
| Fase | Tráfico canary | Tiempo mínimo | Criterio para avanzar |
|---|---|---|---|
| 1 | 5 % | 15-30 min | 5xx y latencia similares a la estable |
| 2 | 25 % | 30 min | Sin errores nuevos en los logs de la aplicación |
| 3 | 50 % | 1 h | Métricas de negocio sin cambios |
| 4 | 100 % | - | 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:
- Envía el 100 % a la canary con
sudo canary-weight 100. - Despliega la versión nueva en el puerto de la estable (
8081) y comprueba que responde concurl -s http://127.0.0.1:8081/. - Devuelve el tráfico a la estable con
sudo canary-weight 0.
La versión canary queda libre para el siguiente despliegue.
ConsejoSi tu aplicación usa sesiones o cambios de esquema de base de datos, asegúrate de que ambas versiones son compatibles con los mismos datos mientras conviven. Un canary no protege frente a una migración de base de datos destructiva.
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.