Despliegues canary: lanzamientos graduales bien hechos
Los despliegues canary envían una parte del tráfico a la nueva versión y revierten solos si suben los errores: cómo implementarlos de forma segura.

Los despliegues blue-green cambian el 100% del tráfico de una sola vez. Eso está bien cuando tus pruebas de humo lo detectan todo, pero algunos errores solo aparecen bajo patrones de tráfico reales. Los despliegues canary adoptan un enfoque más cauteloso: dirigen un pequeño porcentaje del tráfico a la nueva versión, monitorean las métricas clave y aumentan gradualmente el porcentaje, o revierten automáticamente si algo sale mal.
El proceso canary
Un despliegue canary sigue un lanzamiento progresivo: empieza con el 1-5% del tráfico, observa durante una ventana de tiempo, aumenta el porcentaje si las métricas están sanas y repite hasta llegar al 100%.
interface CanaryStage {
percentage: number;
durationMinutes: number;
metrics: MetricCheck[];
}
interface MetricCheck {
name: string;
query: string;
threshold: number;
comparison: "less_than" | "greater_than";
}
const canaryPlan: CanaryStage[] = [
{
percentage: 5,
durationMinutes: 10,
metrics: [
{ name: "error_rate", query: "rate(http_5xx[5m])/rate(http_total[5m])", threshold: 0.01, comparison: "less_than" },
{ name: "p99_latency", query: "histogram_quantile(0.99, rate(http_duration_bucket[5m]))", threshold: 2.0, comparison: "less_than" },
],
},
{
percentage: 25,
durationMinutes: 15,
metrics: [
{ name: "error_rate", query: "rate(http_5xx[5m])/rate(http_total[5m])", threshold: 0.01, comparison: "less_than" },
{ name: "p99_latency", query: "histogram_quantile(0.99, rate(http_duration_bucket[5m]))", threshold: 2.0, comparison: "less_than" },
],
},
{
percentage: 50,
durationMinutes: 15,
metrics: [
{ name: "error_rate", query: "rate(http_5xx[5m])/rate(http_total[5m])", threshold: 0.01, comparison: "less_than" },
{ name: "p99_latency", query: "histogram_quantile(0.99, rate(http_duration_bucket[5m]))", threshold: 2.0, comparison: "less_than" },
],
},
{ percentage: 100, durationMinutes: 0, metrics: [] },
];División de tráfico con Nginx
La directiva split_clients de Nginx dirige un porcentaje determinista del tráfico al canary basándose en un identificador del cliente.
# ❌ Random routing — same user might flip between versions
upstream canary { server canary-1:3000; server canary-2:3000; }
upstream stable { server stable-1:3000; server stable-2:3000; }
# ✅ Consistent routing — same user always hits the same version
split_clients "$remote_addr$uri" $upstream_variant {
5% canary;
* stable;
}
upstream canary { server canary-1:3000; server canary-2:3000; }
upstream stable { server stable-1:3000; server stable-2:3000; }
server {
listen 80;
location / {
proxy_pass http://$upstream_variant;
# Add header so downstream services know which version handled the request
proxy_set_header X-Deployment-Version $upstream_variant;
}
}Comparación automatizada de métricas
El controlador de lanzamiento canary compara las métricas entre las versiones canary y estable. Si la tasa de errores o la latencia del canary es significativamente peor, dispara una reversión automática.
interface MetricComparison {
metric: string;
canaryValue: number;
stableValue: number;
degradation: number;
acceptable: boolean;
}
async function compareCanaryMetrics(
canarySelector: string,
stableSelector: string,
window: string
): Promise<MetricComparison[]> {
const comparisons: MetricComparison[] = [];
// Compare error rates
const canaryErrors = await queryPrometheus(
`rate(http_errors_total{deployment="${canarySelector}"}[${window}])`
);
const stableErrors = await queryPrometheus(
`rate(http_errors_total{deployment="${stableSelector}"}[${window}])`
);
const errorDegradation = stableErrors > 0
? (canaryErrors - stableErrors) / stableErrors
: canaryErrors > 0 ? 1 : 0;
comparisons.push({
metric: "error_rate",
canaryValue: canaryErrors,
stableValue: stableErrors,
degradation: errorDegradation,
acceptable: errorDegradation < 0.10, // Less than 10% worse
});
// Compare p99 latency
const canaryLatency = await queryPrometheus(
`histogram_quantile(0.99, rate(http_duration_bucket{deployment="${canarySelector}"}[${window}]))`
);
const stableLatency = await queryPrometheus(
`histogram_quantile(0.99, rate(http_duration_bucket{deployment="${stableSelector}"}[${window}]))`
);
const latencyDegradation = stableLatency > 0
? (canaryLatency - stableLatency) / stableLatency
: 0;
comparisons.push({
metric: "p99_latency",
canaryValue: canaryLatency,
stableValue: stableLatency,
degradation: latencyDegradation,
acceptable: latencyDegradation < 0.15, // Less than 15% worse
});
return comparisons;
}Canary en Kubernetes con Istio
El VirtualService de Istio proporciona una división de tráfico granular para los despliegues en Kubernetes.
# Istio VirtualService — 5% canary split
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: api-server
spec:
hosts:
- api-server
http:
- route:
- destination:
host: api-server
subset: stable
weight: 95
- destination:
host: api-server
subset: canary
weight: 5
---
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: api-server
spec:
host: api-server
subsets:
- name: stable
labels:
version: v1.2.0
- name: canary
labels:
version: v1.3.0Cuándo usar canary vs. blue-green
| Criteria | Canary | Blue-Green |
|-----------------------|-------------------------------|-------------------------|
| Risk tolerance | Lower (gradual exposure) | Higher (all-at-once) |
| Rollback speed | Fast (reduce to 0%) | Instant (switch back) |
| Infrastructure cost | Minimal (small canary fleet) | Double (two full envs) |
| Metric comparison | Side-by-side during rollout | Before/after only |
| Complexity | Higher (traffic splitting) | Lower (DNS/LB switch) |
| Best for | High-traffic, risk-averse | Low-traffic, simple |Conclusiones clave
- Los despliegues canary reducen el radio de impacto: exponen la nueva versión a una pequeña fracción de usuarios antes del lanzamiento completo
- Usa enrutamiento consistente: el mismo usuario debe llegar siempre a la misma versión durante el período canary
- Automatiza la comparación de métricas: compara las tasas de error y latencias del canary frente a las de la versión estable, no solo umbrales absolutos
- Define los disparadores de reversión antes de empezar: la reversión automática ante la degradación de métricas evita la vacilación humana
- Aumenta el tráfico gradualmente: 5% → 25% → 50% → 100% proporciona múltiples ventanas de observación
- Canary detecta lo que las pruebas pasan por alto: algunos errores solo se manifiestan bajo patrones de tráfico y datos reales


