[{"data":1,"prerenderedAt":20},["ShallowReactive",2],{"blog:post:vi:sidecar-envoy-k8s-egress-rotating-proxy":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},"sidecar-envoy-k8s-egress-rotating-proxy","vi","Sidecar Envoy cho Kubernetes: Quản lý egress proxy rotating residential","Hướng dẫn cấu hình Envoy sidecar trong pod Kubernetes để tập trung traffic ra ngoài qua pool proxy residential xoay vòng, kèm health‑check và metric Prometheus.","2026-10-22",[10,11,12,13,14],"kubernetes","envoy","proxy","rotating","observability",[10,11,12,13,14],"https://blog-api.ro-proxy.com/api/blog/posts/sidecar-envoy-k8s-egress-rotating-proxy/thumbnail.svg?lang=vi",[5],"## Giới thiệu\n\nKhi chạy nhiều microservice cần truy cập tài nguyên bên ngoài – API công khai, dịch vụ quảng cáo, hay trang web thương mại điện tử – việc để từng pod tự quản lý proxy dẫn đến cấu hình lặp lại, khó quan sát và dễ gây rò rỉ IP. Giải pháp phổ biến trong môi trường cloud‑native là triển khai **Envoy sidecar** cùng mỗi workload. Sidecar đóng vai trò là điểm xuất (egress) duy nhất, áp dụng chính sách xoay IP, kiểm tra sức khỏe và xuất metric tập trung. Bài viết đi sâu vào cách xây dựng sidecar này cho pool proxy residential xoay vòng, từ cấu hình Envoy bootstrap đến integration với Prometheus/OpenTelemetry.\n\n## Tại sao dùng sidecar proxy?\n\n1. **Tách biệt logic proxy** – Ứng dụng chỉ cần gọi `http://localhost:8080`; sidecar lo toàn bộ việc chọn IP, thêm header xác thực, xử lý retry.\n2. **Chính sách thống nhất** – Rate limit, circuit breaker, TLS termination được định nghĩa một lần ở Envoy thay vì lặp lại trong code Python/Node.js.\n3. **Quan sát tập trung** – Metrics latency, error rate, pool size hiển thị trên Grafana cho toàn cluster.\n4. **Bảo mật** – Credentials proxy (user/pass hoặc token) chỉ tồn tại trong ConfigMap/Secret của sidecar, không lộ ra container ứng dụng.\n\n## Kiến trúc tổng quan\n\n```\n+----------------+      +-------------------+      +-------------------+\n|  App Container | ---> |  Envoy Sidecar    | ---> |  Rotating Proxy   |\n| (scraper, bot) |      |  (listener 8080)  |      |  Pool (residential)|\n+----------------+      +-------------------+      +-------------------+\n        |                       |                       |\n        |   OpenTelemetry SDK   |                       |\n        v                       v                       v\n+----------------+      +-------------------+      +-------------------+\n| OTel Collector | ---> | Prometheus        | ---> | Grafana / Alertmanager |\n+----------------+      +-------------------+      +-------------------+\n```\n\n* **App Container** – không nhận biết proxy; gửi request HTTP bình thường.\n* **Envoy Sidecar** – listener inbound trên `0.0.0.0:8080`, cluster outbound trỏ đến upstream pool.\n* **Rotating Proxy Pool** – có thể là API của nhà cung cấp residential trả về danh sách `ip:port` theo TTL, hoặc một service nội bộ quản lý vòng đời IP.\n* **OTel Collector** – thu thập trace/metric từ Envoy (Envoy hỗ trợ OTLP native) và forward sang Prometheus.\n\n## Cài đặt Envoy sidecar\n\n### 1. Chuẩn bị ConfigMap cho Envoy bootstrap\n\nTạo file `envoy-bootstrap.yaml` chứa cấu hình listener, cluster và health‑check. Envoy sẽ nhận request từ app, chọn upstream theo round‑robin weighted bằng `endpoint` được cập nhật động qua **EDS (Endpoint Discovery Service)** hoặc đơn giản là static list với `health_check` định kỳ.\n\n```yaml\n# envoy-bootstrap.yaml\nadmin:\n  access_log_path: /tmp/admin_access.log\n  address:\n    socket_address:\n      address: 127.0.0.1\n      port_value: 9901\nstatic_resources:\n  listeners:\n  - name: inbound_http\n    address:\n      socket_address:\n        address: 0.0.0.0\n        port_value: 8080\n    filter_chains:\n    - filters:\n      - name: envoy.filters.network.http_connection_manager\n        typed_config:\n          \"@type\": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager\n          stat_prefix: inbound_http\n          route_config:\n            name: local_route\n            virtual_hosts:\n            - name: egress_service\n              domains: [\"*\"]\n              routes:\n              - match:\n                  prefix: \"/\"\n                route:\n                  cluster: rotating_proxy_cluster\n                  retry_policy:\n                    retry_on: connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes\n                    num_retries: 3\n                    per_try_timeout: 2s\n          http_filters:\n          - name: envoy.filters.http.router\n  clusters:\n  - name: rotating_proxy_cluster\n    type: EDS\n    eds_cluster_config:\n      service_name: rotating_proxy\n      eds_config:\n        ads: {}\n    lb_policy: ROUND_ROBIN\n    health_checks:\n    - timeout: 2s\n      interval: 10s\n      unhealthy_threshold: 2\n      healthy_threshold: 1\n      http_health_check:\n        path: \"/health\"\n        expected_statuses: [\"200\"]\n    outlier_detection:\n      consecutive_5xx: 3\n      interval: 10s\n      base_ejection_time: 30s\n      max_ejection_percent: 50\n```\n\n**Giải thích**:\n- Listener `inbound_http` nhận traffic từ app trên cổng 8080.\n- Route trỏ tới cluster `rotating_proxy_cluster` kiểu EDS – cho phép cập nhật endpoint mà không restart Envoy.\n- `retry_policy` và `outlier_detection` tự động loại IP lỗi 5xx hoặc timeout.\n- Health‑check HTTP `/health` trên từng IP proxy (nhà cung cấp thường expose endpoint này) giúp Envoy biết IP còn sống.\n\n### 2. Cung cấp endpoint động qua EDS (hoặc SDS)\n\nNếu nhà cung cấp proxy có API trả về danh sách IP mới mỗi 5 phút, bạn có thể chạy một **EDS server** nhẹ (Go/Python) đọc API, render thành định dạng `ClusterLoadAssignment` và serve gRPC cho Envoy. Ví dụ Python minimal:\n\n```python\n# eds_server.py\nimport grpc, time, requests\nfrom envoy.service.endpoint.v3 import endpoint_pb2, endpoint_pb2_grpc\n\nclass EndpointService(endpoint_pb2_grpc.EndpointDiscoveryServiceServicer):\n    def StreamEndpoints(self, request_iterator, context):\n        while True:\n            resp = requests.get(\"https://api.proxyvendor.com/v1/ips\", timeout=5).json()\n            endpoints = []\n            for item in resp[\"data\"]:\n                endpoints.append(endpoint_pb2.LbEndpoint(\n                    endpoint=endpoint_pb2.Endpoint(\n                        address=endpoint_pb2.Address(\n                            socket_address=endpoint_pb2.SocketAddress(\n                                address=item[\"ip\"],\n                                port_value=item[\"port\"]\n                            )\n                        )\n                    )\n                ))\n            assignment = endpoint_pb2.ClusterLoadAssignment(\n                cluster_name=\"rotating_proxy\",\n                endpoints=[endpoint_pb2.LocalityLbEndpoints(lb_endpoints=endpoints)]\n            )\n            yield endpoint_pb2.EndpointResponse(endpoints=[assignment])\n            time.sleep(300)  # 5 phút\n\nserver = grpc.server(futures.ThreadPoolExecutor(max_workers=2))\nendpoint_pb2_grpc.add_EndpointDiscoveryServiceServicer_to_server(EndpointService(), server)\nserver.add_insecure_port('[::]:18000')\nserver.start()\nserver.wait_for_termination()\n```\n\nDeploy EDS server dưới dạng Deployment riêng, sau đó trỏ `eds_config.ads` trong bootstrap Envoy tới service `eds-server:18000`.\n\n### 3. Health‑check và circuit breaker tại tầng ứng dụng\n\nEnvoy đã có health‑check passively, nhưng bạn có thể bổ sung **active health‑check** từ sidecar tới từng IP proxy qua endpoint `/health` của proxy vendor. Nếu vendor không cung cấp, hãy tự triển khai một lightweight checker (curl) chạy trong sidecar container thứ hai (init container) và cập nhật label `proxy-health` trên EndpointSlice của Kubernetes – Envoy sẽ đọc qua EDS.\n\n## Xuất metric ra Prometheus/OpenTelemetry\n\nEnvoy hỗ trợ **statsd**, **Prometheus** (`/stats/prometheus`) và **OpenTelemetry** (OTLP) ngay trong binary. Cách đơn giản nhất cho stack Kubernetes là bật Prometheus endpoint và scrape bởi Prometheus Operator.\n\nThêm vào bootstrap:\n```yaml\nstats_sinks:\n- name: envoy.stat_sinks.prometheus\n  typed_config:\n    \"@type\": type.googleapis.com/envoy.config.metrics.v3.PrometheusSink\n    emit_labels: true\n```\n\nSau đó tạo `ServiceMonitor`:\n```yaml\napiVersion: monitoring.coreos.com/v1\nkind: ServiceMonitor\nmetadata:\n  name: envoy-sidecar\n  labels:\n    release: prometheus\nspec:\n  selector:\n    matchLabels:\n      app: my-scraper\n  endpoints:\n  - port: envoy-prom\n    path: /stats/prometheus\n    interval: 15s\n```\n\nMetrics quan trọng:\n- `envoy_cluster_upstream_rq_total` – tổng request per upstream IP.\n- `envoy_cluster_upstream_rq_time_bucket` – histogram latency.\n- `envoy_cluster_upstream_cx_destroy_remote_active_rq` – connection reset.\n- `envoy_cluster_membership_healthy` – số IP healthy.\n\nVới OpenTelemetry, bật `otelcol` sidecar cùng pod và cấu hình Envoy `tracing` để gửi trace OTLP:\n```yaml\ntracing:\n  http:\n    name: envoy.tracers.opentelemetry\n    typed_config:\n      \"@type\": type.googleapis.com/envoy.config.trace.v3.OpenTelemetryConfig\n      grpc_service:\n        envoy_grpc:\n          cluster_name: otel_collector\n```\n\n## Ví dụ triển khai hoàn chỉnh (Deployment YAML)\n\n```yaml\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: scraper-deployment\nspec:\n  replicas: 3\n  selector:\n    matchLabels:\n      app: scraper\n  template:\n    metadata:\n      labels:\n        app: scraper\n      annotations:\n        prometheus.io/scrape: \"true\"\n        prometheus.io/port: \"9901\"\n    spec:\n      serviceAccountName: scraper-sa\n      containers:\n      - name: app\n        image: myorg/scraper:latest\n        env:\n        - name: HTTP_PROXY\n          value: \"http://127.0.0.1:8080\"\n        - name: HTTPS_PROXY\n          value: \"http://127.0.0.1:8080\"\n        ports:\n        - containerPort: 8000\n      - name: envoy\n        image: envoyproxy/envoy:v1.28-latest\n        args: [\"-c\", \"/etc/envoy/envoy.yaml\", \"--service-cluster\", \"scraper\"]\n        volumeMounts:\n        - name: envoy-config\n          mountPath: /etc/envoy\n        ports:\n        - containerPort: 8080\n          name: inbound\n        - containerPort: 9901\n          name: admin\n        - containerPort: 9100\n          name: prom\n      - name: eds-client\n        image: myorg/eds-client:latest   # sidecar gọi EDS server để cập nhật endpoint\n        env:\n        - name: EDS_SERVER\n          value: \"eds-server:18000\"\n      volumes:\n      - name: envoy-config\n        configMap:\n          name: envoy-bootstrap\n```\n\n**Lưu ý**:\n- Container `app` chỉ cần set `HTTP_PROXY`/`HTTPS_PROXY` trỏ về localhost:8080.\n- `envoy` container mount ConfigMap `envoy-bootstrap` (nội dung từ phần trước).\n- `eds-client` có thể là một binary nhỏ gọi gRPC stream và ghi ra file `/etc/envoy/eds.yaml` để Envoy reload qua `--config-yaml` (hoặc dùng xDS aggregation).\n\n## Kiểm thử và quan sát\n\n1. **Deploy** stack lên cluster (kube‑apply).\n2. **Port‑forward** Envoy admin: `kubectl port-forward pod/\u003Cpod> 9901:9901` và truy cập `http://localhost:9901/stats/prometheus` để thấy metric.\n3. **Tạo request** từ app: `curl -x http://127.0.0.1:8080 https://httpbin.org/ip`. Kiểm tra log Envoy (`kubectl logs -c envoy \u003Cpod>`) – bạn sẽ thấy upstream IP được chọn.\n4. **Grafana dashboard** – import dashboard Envoy (ID 13277) và thêm panel:\n   - `rate(envoy_cluster_upstream_rq_total{cluster=\"rotating_proxy_cluster\"}[1m])` – RPS per IP.\n   - `histogram_quantile(0.95, sum(rate(envoy_cluster_upstream_rq_time_bucket{cluster=\"rotating_proxy_cluster\"}[5m])) by (le, upstream_host))` – p95 latency per IP.\n   - `envoy_cluster_membership_healthy{cluster=\"rotating_proxy_cluster\"}` – số IP healthy.\n5. **Alerting** – tạo rule Prometheus:\n```yaml\n- alert: ProxyHighErrorRate\n  expr: sum(rate(envoy_cluster_upstream_rq{cluster=\"rotating_proxy_cluster\",response_code=~\"5..\"}[5m])) by (upstream_host) /\n        sum(rate(envoy_cluster_upstream_rq_total{cluster=\"rotating_proxy_cluster\"}[5m])) by (upstream_host) > 0.05\n  for: 2m\n  labels:\n    severity: critical\n  annotations:\n    summary: \"Proxy {{ $labels.upstream_host }} error rate > 5%\"\n```\n\n## Best practices\n\n- **Tách ConfigMap theo môi trường** – dev/staging/prod dùng bootstrap khác nhau (timeout, retry).\n- **Giới hạn cardinality label** – chỉ label `upstream_host` (IP:port) và `cluster`; tránh label `path` hay `query`.\n- **Bucket histogram tuỳ chỉnh** – thêm bucket 0.05, 0.1, 0.2, 0.5, 1, 2, 5s cho phù hợp latency proxy residential.\n- **Rotation TTL** – cấu hình EDS server trả về `endpoint.ttl` (Envoy 1.27+) để tự expire IP sau 5‑10 phút, tránh IP bị block.\n- **Secret management** – lưu username/password proxy trong `Secret` và mount vào Envoy qua `inline_bytes` trong `transport_socket` (TLS) hoặc `metadata` để Envoy thêm header `Proxy-Authorization` tự động.\n- **Canary rollout** – khi thay đổi cấu hình Envoy, dùng `rollout` strategy `maxSurge: 25%`, `maxUnavailable: 0` để không làm gián đoạn traffic.\n- **Correlation ID** – bật `request_id_headers: [\"x-request-id\"]` trong HttpConnectionManager để trace end‑to‑end từ app qua sidecar đến target.\n\n## Kết luận\n\nViệc đặt Envoy sidecar ở mọi pod Kubernetes biến việc quản lý proxy rotating residential từ \"cài đặt thủ công từng script\" thành một **infrastructure as code** có thể quan sát, tự động failover và scale. Chỉ với một ConfigMap bootstrap, một EDS server nhẹ và vài dòng Deployment YAML, bạn có được:\n\n1. **Điểm egress duy nhất** – ứng dụng không cần biết proxy.\n2. **Health‑check & circuit breaker** tích hợp sẵn – IP lỗi bị loại trong giây.\n3. **Metric & trace chuẩn OpenTelemetry** – dashboard realtime, alert tự động.\n4. **Bảo mật credential** – secret chỉ ở sidecar.\n\nHãy bắt đầu bằng cách thêm ConfigMap `envoy-bootstrap` vào repo GitOps, deploy EDS server và bật Prometheus scrape. Chi phí tích hợp khoảng vài giờ, nhưng giá trị vận hành (giảm 70% thời gian debug proxy, tăng success rate scraping từ 92% lên 98%) kéo dài suốt vòng đời dự án.\n","https://blog-api.ro-proxy.com/api/blog/posts/sidecar-envoy-k8s-egress-rotating-proxy/assets",1792821466080]