본문으로 건너뛰기
EnvoyFilter — 표준 CRD의 탈출구

08 · EnvoyFilter — 표준 CRD로 안 되는 것들의 탈출구

  • EnvoyFilter는 istiod가 만든 Envoy 설정을 직접 패치하는 저수준 탈출구입니다 — applyTo × context × operation으로 읽습니다.
  • 업그레이드에 취약하고 폭발 반경이 크고 리뷰가 어려워 최후의 수단입니다. 표준 CRD → 상위 API(Telemetry/WasmPlugin) → EnvoyFilter 순으로 고릅니다.
  • 레이트 리밋은 local(인스턴스별·무의존)과 global(전역정확·RLS 비용)로 성격이 다릅니다.
  • 쓴다면 workloadSelector로 좁게, 버전 핀·GitOps 리뷰·관측이 필수입니다.

07의 표준 CRD로 대부분은 해결됩니다. 남는 것은 정교한 레이트 리밋, 특정 Envoy HTTP 필터 삽입, 커스텀 요청 조작처럼 VirtualService·AuthorizationPolicy의 어휘로는 표현 안 되는 요구입니다. 그때 열리는 마지막 문이 EnvoyFilter — istiod가 생성한 Envoy 설정을 직접 패치하는 저수준 탈출구입니다. 강력한 만큼 위험하므로 이 문서는 무엇을 할 수 있는지만큼 왜 최후의 수단인지를 함께 다룹니다.

관련 문서: 07 표준 CRD 매핑(먼저 여기로) · 04 GitOps 리뷰 · 02 istiod가 만든 설정

EnvoyFilter가 하는 일 — Envoy 설정 직접 패치

02에서 봤듯 istiod는 listener·filter chain·cluster·route가 다 채워진 Envoy 설정을 각 프록시에 xDS로 내려줍니다. EnvoyFilter는 그 최종 설정에 패치를 덧붙입니다.

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata: { name: my-patch, namespace: prod }
spec:
  workloadSelector: { labels: { app: api } }   # 반드시 좁게 건다
  configPatches:
  - applyTo: HTTP_FILTER                # 무엇을: listener/filter/route/cluster…
    match:
      context: SIDECAR_INBOUND          # 어디서: 인바운드/아웃바운드/게이트웨이
      listener:
        filterChain:
          filter: { name: envoy.filters.network.http_connection_manager }
    patch:
      operation: INSERT_BEFORE          # 어떻게: ADD/MERGE/REMOVE/INSERT_BEFORE…
      value: { ... }                    # 날것의 Envoy 설정

읽을 때는 applyTo가 무엇을 건드리나, match.context가 어느 경로의 프록시인가, patch.operation이 어떻게 바꾸나를 각각 정합니다. value에는 Envoy가 이해하는 날것의 설정이 그대로 들어갑니다 — 위험의 근원이 여기입니다.

왜 최후의 수단인가 (먼저 못 박기)

이 API는 Envoy 내부 구조에 직접 결합합니다. 대가가 뒤따릅니다.

  • 업그레이드에 깨집니다. 필터 이름과 설정 스키마가 Envoy/Istio 버전에 묶여 있습니다. 컨트롤 플레인을 올리면(04의 revision 업그레이드) 패치가 아무 경고 없이 무효화되거나 프록시가 설정을 거부합니다.
  • 폭발 반경이 큽니다. patch 하나를 틀리면 workloadSelector에 걸린 모든 프록시의 데이터 플레인이 통째로 망가질 수 있습니다. 검증 장치도 표준 CRD보다 약합니다.
  • 관측·리뷰가 어렵습니다. 날것의 Envoy 설정이라 리뷰어가 무슨 뜻인지 읽어내기 힘듭니다.

그래서 원칙은 선택 사다리입니다:

1. 표준 CRD로 되나?        → VirtualService/AuthorizationPolicy/Gateway  (07)
2. 상위 확장 API로 되나?    → Telemetry API / WasmPlugin
3. 그래도 안 되면          → EnvoyFilter  (최후, 좁게)

플래그십 사례 — 레이트 리밋

nginx limit_req의 대응이 여기입니다. Istio는 레이트 리밋을 표준 CRD로 노출하지 않으므로 Envoy의 rate limit 필터를 EnvoyFilter(또는 그 위 도구)로 붙입니다. 방식은 둘이고 쓰임이 다릅니다.

Local rate limit — 프록시 로컬 토큰 버킷

각 프록시가 자기 안의 토큰 버킷으로 제한합니다. envoy.filters.http.local_ratelimit 필터를 HTTP 필터 체인에 삽입합니다.

  • 장점: 외부 의존 없음, 지연 없음, 구성 단순.
  • 한계: 인스턴스별입니다. 프록시 10개면 실제 허용량은 설정치 × 10이 됩니다. 파드 수가 변하면 전체 한도도 같이 흔들립니다.
  • 쓸 때: 인스턴스 단위 보호(과부하 방어), 대략적 상한이면 충분할 때.

Global rate limit — 외부 RLS로 클러스터 일관성

클러스터 전역에서 하나의 일관된 한도가 필요하면 Envoy의 envoy.filters.http.ratelimit 필터를 씁니다. 이 필터는 매 요청을 외부 Rate Limit Service(RLS)(보통 Envoy ratelimit + Redis)에 물어봅니다.

global rate limit — 매 요청이 외부 RLS를 한 번 왕복해 allow/deny를 받고, 그 판정이 통과와 429를 가릅니다.
도식 텍스트
  • 클라이언트
  • Envoy
  • ratelimit 필터
  • RLS + Redis
  1. 1. 인바운드 요청
  2. 2. HTTP 필터 체인 → ratelimit 필터
  3. 3. descriptor로 쿼터 질의
  4. Redis 카운터 하나가 클러스터 전역 한도를 센다 · 요청마다 이 왕복이 지연으로 더해진다
  5. 4. allow / deny
  6. 분기 — allow
    1. 5. 통과 — 업스트림으로
    2. 6. 정상 응답
  7. deny
    1. 5'. 차단 판정
    2. 6'. 429 Too Many Requests
  • 장점: 프록시 수와 무관하게 전역 정확도. 사용자·API키·경로별 descriptor로 세밀한 정책.
  • 비용: RLS·Redis라는 운영 대상과 요청당 왕복 지연이 붙습니다.
  • 쓸 때: “사용자당 초당 N회” 같은 전역 계약을 지켜야 할 때.
LocalGlobal
상태프록시 로컬외부 RLS+Redis
정확도인스턴스별(근사)클러스터 전역(정확)
지연/의존없음RLS 왕복
용도과부하 방어정확한 쿼터 계약

실무에선 둘을 겹쳐 쓰기도 합니다. global로 공정 쿼터를, local로 인스턴스 과부하 방어를 맡깁니다.

커스텀 로직 — Lua와 WASM

요청/응답을 프로그래밍해서 조작해야 할 때 남는 영역입니다.

Lua — 간단한 인라인 스크립트

envoy.filters.http.lua 필터로 짧은 Lua를 인라인으로 박습니다. 헤더 몇 개 가공, 조건부 분기 같은 가벼운 로직에 적합합니다.

    patch:
      operation: INSERT_BEFORE
      value:
        name: envoy.filters.http.lua
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
          inline_code: |
            function envoy_on_request(handle)
              handle:headers():add("x-mesh-touched", "1")
            end

간단하지만 요청 경로에서 도는 코드이므로 무거운 연산·외부 호출은 피합니다.

WASM — 진짜 커스텀 필터, 단 상위 API로

복잡하거나 성능이 중요한 커스텀 필터는 언어로 짜서 WASM으로 로드합니다. 이때 EnvoyFilter로 날것을 붙이기보다 WasmPlugin API를 쓰는 쪽을 권합니다 — 배포·버전·대상 선택을 다루는 더 안전한 상위 추상화라서, EnvoyFilter가 업그레이드에 깨지는 문제를 크게 덜어줍니다.

여기서도 마찬가지입니다. Telemetry API·WasmPlugin 같은 상위 API로 되는 일을 굳이 EnvoyFilter 날것으로 하지 않습니다.

운영 수칙

EnvoyFilter를 쓸 수밖에 없다면 최소한 다음은 지킵니다.

  • 좁게 겁니다. workloadSelector를 반드시 지정해 폭발 반경을 워크로드 하나로 가둡니다. 전역 EnvoyFilter는 금물입니다.
  • 버전에 핀을 박고 테스트합니다. Istio/Envoy 업그레이드 전 스테이징에서 patch가 여전히 붙는지 검증합니다. revision 카나리(04)로 먼저 소수에만 태웁니다.
  • GitOps 리뷰는 필수입니다. 날것의 Envoy 설정일수록 리뷰·감사가 중요합니다. 손 apply는 절대 금지입니다(04).
  • 관측을 붙입니다. 레이트 리밋 필터도 자체 메트릭(제한된 요청 수 등)을 내므로 06의 대시보드에 걸어 실제로 얼마나 막히는지 봅니다.

이 문서에서 가져갈 것

  • EnvoyFilter는 istiod가 만든 Envoy 설정을 직접 패치하는 저수준 탈출구입니다. applyTo × context × operation으로 읽습니다.
  • 업그레이드 취약·폭발 반경·리뷰 난이도 때문에 최후의 수단입니다. 선택 사다리 = 표준 CRD → 상위 API(Telemetry/WasmPlugin) → EnvoyFilter.
  • 대표 용도는 레이트 리밋(local=인스턴스별·무의존, global=전역정확·RLS비용)과 커스텀 로직(Lua=가벼움, WASM=WasmPlugin으로)입니다. 쓴다면 좁게·버전핀·GitOps·관측이 필수입니다.
마지막 수정 일자