[{"data":1,"prerenderedAt":20},["ShallowReactive",2],{"blog:post:vi:su-dung-proxy-de-shadow-traffic-cho-trien-khai-canary":3},{"slug":4,"lang":5,"title":6,"summary":7,"date":8,"tags":9,"tag_slugs":15,"thumbnail_url":16,"translations":17,"body":18,"asset_base":19},"su-dung-proxy-de-shadow-traffic-cho-trien-khai-canary","vi","Sử Dụng Proxy Để Shadow Traffic Cho Triển Khai Canary","Hướng dẫn chi tiết cách cấu hình proxy để mirror lưu lượng thực tế sang phiên bản canary, giúp phát hiện lỗi sớm mà không ảnh hưởng người dùng.","2026-10-06",[10,11,12,13,14],"proxy","canary","traffic-shadowing","deployment","roproxy",[10,11,12,13,14],"https://blog-api.ro-proxy.com/api/blog/posts/su-dung-proxy-de-shadow-traffic-cho-trien-khai-canary/thumbnail.svg?lang=vi",[5],"## Tại sao cần traffic shadowing với proxy?\n\nKhi triển khai một phiên bản mới (canary) của dịch vụ, việc chỉ định một phần nhỏ traffic thực tế đến phiên bản đó giúp quan sát hành vi thực tế mà không rủi ro ảnh hưởng đến toàn bộ người dùng. Proxy đóng vai trò là lớp trung gian có thể nhân bản request gốc và gửi song song đến cả production và canary, sau đó chỉ trả về response của production cho client.\n\nLợi ích chính:\n- **Không làm gián đoạn UX**: Client vẫn nhận response từ phiên bản ổn định.\n- **Dữ liệu thực**: Canary nhận request thật, bao gồm header, cookie, payload, giúp test logic nghiệp vụ chính xác.\n- **Phát hiện sớm**: Lỗi 5xx, latency cao, hoặc hành vi bất thường được phát hiện trước khi mở rộng traffic.\n\n## Kiến trúc tổng quan\n\n```\nClient → [Proxy Edge] → Production (100% response)\n                ↳→ Canary (shadow, response bị bỏ qua)\n```\n\nProxy edge có thể là một instance RoProxy chạy ở chế độ **sticky session** để giữ nguyên kết nối TCP/TLS cho cả hai backend, hoặc một proxy layer tùy chỉnh (NGINX, Envoy, hoặc script Python/Node) thực hiện **request duplication**.\n\n## Chuẩn bị môi trường\n\n2. **Hai endpoint backend**: `https://api.prod.example.com` và `https://api.canary.example.com`.\n3. **Ngôn ngữ**: Python 3.11+ (thư viện `httpx` hỗ trợ async) hoặc Node.js 20+ (`undici`).\n4. **Công cụ quan sát**: Prometheus + Grafana để thu metric latency, error rate cho canary.\n\n## Cấu hình RoProxy cho sticky session\n\nTrong dashboard RoProxy, tạo một **proxy pool** với chế độ `sticky` và TTL 10 phút. Ghi lại `proxy_host`, `proxy_port`, `username`, `password`. Ví dụ:\n\n```text\nproxy_host = \"GATEWAY_HOST\"\nproxy_port = 8080\nproxy_user = \"myuser\"\nproxy_pass = \"mypassword\"\n```\n\nSticky session đảm bảo cùng một IP được dùng cho cả production và canary trong một request duplication, tránh vấn đề session affinity hoặc rate limit theo IP.\n\n## Triển khai request duplication bằng Python (httpx)\n\n```python\nimport os\nimport asyncio\nimport httpx\nfrom typing import Tuple\n\nPROXY = f\"http://{os.getenv('PROXY_USER')}:{os.getenv('PROXY_PASS')}@{os.getenv('PROXY_HOST')}:{os.getenv('PROXY_PORT')}\"\nPROD_URL = \"https://api.prod.example.com\"\nCANARY_URL = \"https://api.canary.example.com\"\n\nasync def forward_request(method: str, path: str, headers: dict, body: bytes) -> httpx.Response:\n    \"\"\"Gửi request đến production và canary song song.\"\"\"\n    url_prod = f\"{PROD_URL}{path}\"\n    url_canary = f\"{CANARY_URL}{path}\"\n    \n    async with httpx.AsyncClient(proxy=PROXY, timeout=30.0, http2=True) as client:\n        # Tạo request objects\n        req_prod = client.build_request(method, url_prod, headers=headers, content=body)\n        req_canary = client.build_request(method, url_canary, headers=headers, content=body)\n        \n        # Gửi song song\n        resp_prod, resp_canary = await asyncio.gather(\n            client.send(req_prod, stream=True),\n            client.send(req_canary, stream=True),\n            return_exceptions=True\n        )\n        \n        # Xử lý exception cho canary (không làm hỏng production)\n        if isinstance(resp_canary, Exception):\n            print(f\"[Canary error] {resp_canary}\")\n        else:\n            # Thu metric canary (latency, status)\n            latency = resp_canary.elapsed.total_seconds()\n            status = resp_canary.status_code\n            # Gửi metric đến Prometheus Pushgateway hoặc OpenTelemetry\n            record_canary_metric(path, status, latency)\n            # Đọc body để giải phóng kết nối\n            await resp_canary.aread()\n        \n        # Trả về response production cho client\n        return resp_prod\n\n# Ví dụ dùng trong FastAPI\nfrom fastapi import FastAPI, Request, Response\napp = FastAPI()\n\n@app.api_route(\"/{path:path}\", methods=[\"GET\",\"POST\",\"PUT\",\"DELETE\",\"PATCH\",\"HEAD\",\"OPTIONS\"])\nasync def proxy_endpoint(request: Request, path: str):\n    body = await request.body()\n    headers = dict(request.headers)\n    # Loại bỏ header hop-by-hop\n    headers.pop(\"host\", None)\n    headers.pop(\"content-length\", None)\n    \n    resp = await forward_request(request.method, f\"/{path}\", headers, body)\n    return Response(content=await resp.aread(), status_code=resp.status_code, headers=dict(resp.headers))\n```\n\n**Giải thích**:\n- `httpx.AsyncClient` với `proxy` tự động áp dụng RoProxy cho cả hai kết nối.\n- `asyncio.gather` gửi song song, `return_exceptions=True` đảm bảo lỗi canary không làm crash production.\n- Metric canary được ghi riêng (hàm `record_canary_metric` bạn tự triển khai với Prometheus client).\n\n## Triển khai tương tự bằng Node.js (undici)\n\n```js\nimport { ProxyAgent } from 'undici-proxy-agent';\nimport { request } from 'undici';\nimport { PrometheusExporter } from '@prometheus/client';\n\nconst proxy = new ProxyAgent(`http://${process.env.PROXY_USER}:${process.env.PROXY_PASS}@${process.env.PROXY_HOST}:${process.env.PROXY_PORT}`);\nconst PROD = 'https://api.prod.example.com';\nconst CANARY = 'https://api.canary.example.com';\nconst exporter = new PrometheusExporter({ port: 9464 });\n\nasync function forward(req, res) {\n  const path = req.url;\n  const method = req.method;\n  const headers = { ...req.headers };\n  delete headers.host;\n  delete headers['content-length'];\n  \n  const body = await new Promise((resolve) => {\n    const chunks = [];\n    req.on('data', c => chunks.push(c));\n    req.on('end', () => resolve(Buffer.concat(chunks)));\n  });\n  \n  const prodReq = request(`${PROD}${path}`, { method, headers, body, dispatcher: proxy });\n  const canaryReq = request(`${CANARY}${path}`, { method, headers, body, dispatcher: proxy });\n  \n  const [prodRes, canaryRes] = await Promise.allSettled([prodReq, canaryReq]);\n  \n  // Xử lý canary metric\n  if (canaryRes.status === 'fulfilled') {\n    const latency = Date.now() - canaryRes.value.startTime; // cần custom wrapper để lấy startTime\n    exporter.registerMetric({\n      name: 'canary_request_latency_seconds',\n      type: 'Histogram',\n      help: 'Latency of shadow requests',\n      labels: { path, status: canaryRes.value.statusCode }\n    }).observe(latency / 1000);\n    canaryRes.value.body.resume(); // consume stream\n  } else {\n    console.error('[Canary error]', canaryRes.reason);\n  }\n  \n  // Trả về production response\n  if (prodRes.status === 'fulfilled') {\n    res.writeHead(prodRes.value.statusCode, prodRes.value.headers);\n    prodRes.value.body.pipe(res);\n  } else {\n    res.writeHead(502);\n    res.end('Bad gateway');\n  }\n}\n\n// Express example\nimport express from 'express';\nconst app = express();\napp.use(express.raw({ type: '*/*' }));\napp.all('*', forward);\napp.listen(8080);\n```\n\n## Theo dõi và cảnh báo\n\n1. **Metric chính**: `canary_request_latency_seconds`, `canary_error_total`, `canary_status_code`.\n2. **Dashboard Grafana**: Biểu đồ latency p95, error rate theo endpoint.\n3. **Alert rule** (Prometheus):\n   ```yaml\n   - alert: CanaryHighErrorRate\n     expr: sum(rate(canary_error_total[5m])) by (path) > 0.05\n     for: 2m\n     labels:\n       severity: warning\n     annotations:\n       summary: \"Canary error rate >5% on {{ $labels.path }}\"\n   ```\n\nKhi alert kích hoạt, team có thể rollback canary hoặc điều tra log mà không ảnh hưởng production.\n\n## Xử lý các trường hợp đặc biệt\n\n| Trường hợp | Giải pháp |\n|------------|-----------|\n| **Request có body lớn (>10MB)** | Bật streaming (`stream=True` trong httpx, `body` là readable stream trong Node) để tránh OOM. |\n| **Authentication token rotation** | Đảm bảo proxy forward header `Authorization` nguyên vẹn; canary cần cùng secret key với production. |\n| **Idempotency** | Chỉ shadow các method an toàn (GET, HEAD, OPTIONS) hoặc những POST có header `Idempotency-Key`. |\n| **Rate limit phía backend** | Sử dụng sticky session để cùng IP cho cả hai backend, tránh bị chặn do IP khác nhau. |\n| **TLS mutual auth** | Cấu hình RoProxy với cert client (`--cert-file`, `--key-file`) và thêm vào `httpx.AsyncClient(cert=(\"cert.pem\",\"key.pem\"))`. |\n\n## Best practices\n\n1. **Bắt đầu nhỏ**: Chỉ shadow 1-2% traffic (có thể dùng weighted routing ở load balancer trước proxy).\n2. **Log correlation**: Thêm header `X-Request-ID` duy nhất cho mỗi request, ghi log cả production và canary để trace.\n3. **Automated rollback**: Kết hợp với Argo Rollouts hoặc Flagger; khi metric canary vượt ngưỡng, tự động pause rollout.\n4. **Separate proxy pools**: Nếu lượng traffic lớn, tách pool residential cho production, datacenter cho canary để tối ưu chi phí.\n5. **Test chaos**: Định kỳ inject latency/failure vào canary (via proxy middleware) để kiểm tra resilience.\n\n## Kết luận\n\nSử dụng proxy để shadow traffic là kỹ thuật mạnh mẽ, cho phép team validate canary trong môi trường thực mà không rủi ro. Với RoProxy cung cấp sticky session, rotating IP và hỗ trợ SOCKS5/HTTP, việc triển khai trở nên đơn giản: chỉ cần một proxy edge, một script duplication request (Python hoặc Node), và hệ thống observability. Áp dụng các best practice trên sẽ giúp quy trình CI/CD an toàn, giảm thời gian phát hiện lỗi từ giờ xuống phút.\n\n---\n\n*Bài viết này tập trung vào góc độ **traffic shadowing cho canary deployment** – một chủ đề chưa được bao phủ trong các bài trước của RoProxy. Hy vọng các ví dụ code và checklist sẽ giúp bạn triển khai ngay hôm nay.*\n","https://blog-api.ro-proxy.com/api/blog/posts/su-dung-proxy-de-shadow-traffic-cho-trien-khai-canary/assets",1792303107151]