LabHub
배우기 러닝패스 코스

Istio 심화 — 왜 그렇게 흐르는가 · EnvoyFilter — 생성된 설정 위에 얹는 패치 · 이론

EnvoyFilter 는 번역이 아니라 패치다

LabHub 에서 이어서 보기

한 줄 요약

EnvoyFilter 는 번역이 아니라 패치다. istiod 가 다른 리소스들로 Envoy 설정을 다 만든 뒤, 그 결과물의 한 자리를 applyTo·context·match 로 고르고 operation 으로 조각을 끼우거나 고친다. 끼우는 값은 Envoy 설정 그대로라서, istioctl 이 통과시킨 패치를 Envoy 가 거절하는 일이 생긴다.

개념 지도: 패치 · 일부 · 뜻 모를 덩어리 · 그대로

왜 이게 필요했나

VirtualService·DestinationRule·AuthorizationPolicy 는 Envoy 가 할 줄 아는 것 중 일부만 드러낸다. 로컬 요청 제한, 특정 헤더를 다루는 Lua, 메시가 아직 API 로 감싸지 않은 새 필터처럼 그 바깥이 필요할 때가 있다. Istio 가 모든 Envoy 기능마다 API 를 새로 만들 수는 없으니, 생성된 설정에 직접 손을 대는 비상구를 남겨 둔 것이 EnvoyFilter 다.

비상구에는 대가가 있다. 다른 리소스는 istiod 가 뜻을 알아듣고 책임지고 번역하지만, EnvoyFilter 의 값은 istiod 에게 뜻 모를 덩어리다. istiod 의 번역 결과(필터 이름·순서·모양)는 판마다 바뀔 수 있고, 패치는 그 결과에 기대어 있다. 공식 문서가 첫머리에 "잘못 쓰면 메시 전체를 불안정하게 만들 수 있다" 고 경고하는 이유다. 실제로 istioctl 1.24.2 는 올바른 EnvoyFilter 에도 "EnvoyFilter exposes internal implementation details that may change at any time" 이라는 경고를 붙인다.

어떻게 동작하나

패치 하나는 '어디에' 와 '무엇을' 의 짝이다.

| 칸 | 고르는 것 | 예 |
| --- | --- | --- |
| applyTo | 닿는 Envoy 객체의 종류 | LISTENER · FILTER_CHAIN · NETWORK_FILTER · HTTP_FILTER · CLUSTER · ROUTE_CONFIGURATION |
| match.context | 어느 프록시·어느 방향 | SIDECAR_INBOUND · SIDECAR_OUTBOUND · GATEWAY · ANY |
| match.listener | 그 종류 안의 어느 하나 | 포트, filterChain.filter.name(네트워크 필터), subFilter.name(HTTP 필터) |
| match.proxy.proxyVersion | 어느 판의 프록시 | ^1\.24.* 같은 정규식 |
| patch.operation | 어떻게 | INSERT_BEFORE · INSERT_AFTER · INSERT_FIRST · MERGE · REPLACE · REMOVE · ADD |

HTTP_FILTERsubFilter: envoy.filters.http.routerINSERT_BEFORE 를 주면, 들어오는 쪽 HTTP 연결 관리자의 필터 목록에서 router 바로 앞에 value그대로 항목 하나로 들어간다. 그래서 value 는 Istio 문법이 아니라 Envoy 문법이다. VirtualService 의 percentage: { value: 100 } 을 옮겨 적으면 Envoy 의 fault 필터(분자·분모를 쓰는 FractionalPercent)는 알아듣지 못한다.

순서에는 규칙이 있다. 루트 네임스페이스(istio-system)의 EnvoyFilter 가 먼저, 워크로드 네임스페이스의 것이 나중에 적용되고, 같은 워크로드에 여럿이면 만든 시각 순이다. 서로 충돌하면 결과는 정해져 있지 않다. REPLACE 는 이름이 맞는 대상이 없으면 아무 일도 하지 않는다 — 오류가 나지 않아 조용히 빠진다. 범위는 workloadSelector 가 정한다. 있으면 라벨이 맞는 워크로드, 없으면 그 네임스페이스 전체, 루트 네임스페이스에 두면 메시 전체다.

검사가 어디까지 되는지도 알아야 한다. istioctl 은 EnvoyFilter 의 겉 구조를 검사하고, 값을 Envoy 타입으로 풀어 본다. @type 이름이 아예 없으면 오류(rc=1)로 막지만, 그 타입에 없는 필드는 경고만 내고 rc=0 이다. 그리고 "router 는 목록의 마지막이어야 한다" 같은 Envoy 의 조립 규칙은 전혀 모른다. 마지막 판정은 Envoy 가 한다.

현장에서 만나는 모습

EnvoyFilter 를 넣었는데 아무 변화가 없다. 운영에서 Envoy 가 패치된 리스너를 거절하면 파드는 멀쩡히 돌고 옛 설정이 그대로 남는다(xDS NACK). istioctl proxy-status 에서 그 프록시만 설정이 어긋나 있고, istiod 로그에 거절 이유가 찍힌다. router 뒤에 필터를 넣은 패치가 전형이다. istioctl analyze 가 우선순위 없는 상대 위치 연산에 IST0151 경고를 내는 것도 같은 맥락이다 — 기준 필터가 아직 없으면 패치가 적용되지 않는다.

CI 는 초록인데 배포 뒤 깨진다. 파이프라인이 istioctl validate 의 종료 코드만 보면, 값 안의 잘못된 필드는 경고로 흘러 지나간다. EnvoyFilter 만큼은 경고를 실패로 다루거나, 같은 값을 넣은 Envoy 설정을 envoy --mode validate 로 한 번 더 거르는 편이 안전하다.

업그레이드 날 메시 절반이 이상해진다. 필터 이름이 바뀌거나 istiod 가 만드는 모양이 달라지면 옛 패치가 엉뚱한 자리에 붙거나 거절된다. proxyVersion 으로 판을 묶어 두면 새 판의 프록시에는 패치가 붙지 않아서, 깨지는 대신 기능이 빠진 채 뜬다. 새 판용 패치를 따로 만들어 함께 두고 넘어가는 것이 정석이다.

공식 문서: [EnvoyFilter](https://istio.io/latest/docs/reference/config/networking/envoy-filter/) · [Envoy HTTP filters](https://www.envoyproxy.io/docs/envoy/v1.38.3/configuration/http/http_filters/http_filters)

다음 실습에서 할 것

fault 필터를 router 앞에 끼우는 EnvoyFilter 를 쓰고, 패치가 적용된 결과를 손으로 세워 418 을 받는다. 같은 필터를 router 뒤에 끼운 것과 VirtualService 식 퍼센트를 옮긴 것을 만들어, istioctl 은 통과시키는데 Envoy 가 거절하는 틈을 두 번 본다. 끝으로 판을 묶는 정규식을 실제 프록시 판에 대 보고, 선택자 없는 요청 제한 필터로 범위를 확인한다.