Istio 심화 — 왜 그렇게 흐르는가 · EnvoyFilter — 생성된 설정 위에 얹는 패치 · 实验
패치를 끼워 보고 istioctl 과 Envoy 의 판정을 가른다
목표
EnvoyFilter 로 fault 필터를 router 앞에 끼우고, 그 패치가 Envoy 설정의 어디가 되는지 손으로 재현한다. router 뒤에 끼운 것과 값이 틀린 것을 만들어 istioctl 과 Envoy 의 판정이 갈리는 것을 보고, 판 묶기와 적용 범위를 확인한다.
왜 중요한가
EnvoyFilter 는 메시에서 가장 힘센 리소스이면서 가장 쉽게 조용히 깨지는 리소스다. istioctl 이 통과시켜도 Envoy 가 거절하면 운영에서는 '설정이 안 바뀐다' 로만 보인다. 패치가 닿는 자리와 검사의 한계를 알면 머지하기 전에 걸러 낼 수 있다.
단계
1. /root/ist2-ef 를 만들고 /root/ist2-ef/ef.yaml 에 EnvoyFilter 를 쓰세요 — apiVersion: networking.istio.io/v1alpha3, 이름 reviews-fault, 네임스페이스 default, workloadSelector 는 app: reviews. 패치는 하나로, applyTo: HTTP_FILTER, match.context: SIDECAR_INBOUND, match.listener.filterChain.filter.name 은 envoy.filters.network.http_connection_manager, 그 subFilter.name 은 envoy.filters.http.router, patch.operation: INSERT_BEFORE. 끼울 값은 이름 envoy.filters.http.fault 에 typed_config(@type 은 type.googleapis.com/envoy.extensions.filters.http.fault.v3.HTTPFault)로 abort.http_status: 418, abort.percentage 는 numerator: 100·denominator: HUNDRED 입니다. istioctl validate -f /root/ist2-ef/ef.yaml 의 출력(표준 오류 포함)과 종료 코드를 /root/ist2-ef/01-validate.txt 에 담으세요(마지막 줄 rc=).
2. /root/ist2-ef/ef.yaml 의 첫 패치에서 다섯 값을 yq 로 뽑아 /root/ist2-ef/02-fields.txt 에 적으세요 — applyTo=, context=(match.context), operation=(patch.operation), anchor=(match 가 기준으로 삼는 HTTP 필터, 곧 subFilter.name), filter=(끼워 넣는 값의 name).
3. /root/ist2-ef/envoy-before.yaml 에 Envoy 설정을 쓰세요 — 관리 포트 9989, 리스너 virtualInbound 가 127.0.0.1:10089 에서 듣고, HTTP 연결 관리자의 stat_prefix 는 inbound_0.0.0.0_9080, 모든 경로를 클러스터 inbound|9080||(127.0.0.1:8112)로 보냅니다. http_filters 는 ef.yaml 의 패치 값과 똑같은 fault 필터 항목, 그다음 router 순서입니다. 업스트림을 8112 에 ok 로 띄우고 Envoy 를 띄운 뒤 curl localhost:10089/reviews 를 세 번 보내, /root/ist2-ef/03-before.txt 에 세 줄을 적으세요 — codes=(세 응답 코드를 쉼표로), stat_name=(fault 가 중단시킨 요청 수를 세는 통계의 전체 이름), aborts_injected=(그 통계의 값).
4. /root/ist2-ef/ef.yaml 을 /root/ist2-ef/ef-after.yaml 로 복사해 patch.operation 만 INSERT_AFTER 로 바꾸세요. 그리고 /root/ist2-ef/envoy-before.yaml 을 /root/ist2-ef/envoy-after.yaml 로 복사해 http_filters 의 순서만 router, fault 로 뒤집으세요(그 패치가 적용된 결과). 두 파일을 각각 istioctl validate -f 와 envoy --mode validate -c 로 검사해 /root/ist2-ef/04-after.txt 에 적으세요 — 첫 줄 istioctl_rc=, 둘째 줄 envoy_rc=, 그 아래에 Envoy 가 거절한 이유가 담긴 출력 줄을 그대로.
5. /root/ist2-ef/ef.yaml 을 /root/ist2-ef/ef-badfield.yaml 로, /root/ist2-ef/envoy-before.yaml 을 /root/ist2-ef/envoy-badfield.yaml 로 복사한 뒤 두 파일의 fault abort.percentage 를 VirtualService 에서 쓰던 모양 { value: 100 } 으로 바꾸세요(나머지는 그대로). istioctl validate -f 와 envoy --mode validate -c 로 각각 검사해 /root/ist2-ef/05-gap.txt 에 적으세요 — 첫 줄 istioctl_rc=, 둘째 줄 envoy_rc=, 그 아래에 istioctl 이 이 필드를 문제 삼은 경고 줄과 Envoy 가 거절한 이유 줄을 그대로.
6. /root/ist2-ef/ef.yaml 을 /root/ist2-ef/ef-pinned.yaml 로 복사해 첫 패치의 match.proxy.proxyVersion 에 정규식 ^1\.24.* 을 더하세요(나머지는 그대로). istioctl validate 로 통과하는지 확인하고, 이 실습의 프록시 판을 알아내기 위해 /root/ist2-ef 에서 istioctl kube-inject 로 /opt/lab/fixtures/istio/inject-target.yaml 을 주입해 /root/ist2-ef/inject.yaml 로 저장하세요(주입 설정 세 파일 /opt/istio/inject-config.yaml·mesh-config.yaml·values-config.yaml 을 모두 넘깁니다). /root/ist2-ef/06-version.txt 에 네 줄을 적으세요 — regex=(ef-pinned.yaml 에 적은 정규식 그대로), proxy_version=(istio-proxy 이미지의 태그), matches_proxy=(그 판이 정규식에 맞으면 yes, 아니면 no), matches_1_25_0=(가상의 판 1.25.0 이 맞으면 yes, 아니면 no).
7. /root/ist2-ef/ef-ratelimit.yaml 에 EnvoyFilter 를 쓰세요 — 이름 inbound-ratelimit, 네임스페이스 default, workloadSelector 없이, 패치 모양은 ef.yaml 과 같되(HTTP_FILTER · SIDECAR_INBOUND · router 앞 INSERT_BEFORE) 값은 이름 envoy.filters.http.local_ratelimit, @type type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit, stat_prefix: http_local_rate_limiter, token_bucket 은 max_tokens: 1·tokens_per_fill: 1·fill_interval: 300s, filter_enabled 와 filter_enforced 는 둘 다 default_value 가 numerator: 100·denominator: HUNDRED 입니다. istioctl validate 로 값 경고가 없는지 확인한 뒤, 그 값을 router 앞에 둔 Envoy 설정을 /root/ist2-ef/envoy-rl.yaml 에 쓰세요(관리 포트·리스너·클러스터는 3단계와 같습니다). 띄우고 curl localhost:10089/reviews 를 세 번 보낸 뒤 /root/ist2-ef/07-ratelimit.txt 에 네 줄을 적으세요 — codes=(세 응답 코드를 쉼표로), rate_limited=(통계 http_local_rate_limit.rate_limited 의 값), scope=(이 EnvoyFilter 가 닿는 범위: workload·namespace·mesh 중 하나), mesh_wide_namespace=(같은 파일을 옮겨 두면 메시 전체에 닿는 네임스페이스, /opt/istio/mesh-config.yaml 에서).
8. /root/ist2-ef/08-report.md 에 before_status=, after_envoy_rc=, badfield_istioctl_rc=, ratelimit_codes= 네 줄을 적고(각각 3단계에서 fault 가 돌려준 상태 코드, 4단계에서 Envoy 의 종료 코드, 5단계에서 istioctl 의 종료 코드, 7단계의 세 응답 코드), 그 아래 - 로 시작하는 설명을 네 줄 이상 적으세요.
참고
- 이 파드에는 진짜 istiod 도 진짜 사이드카도 없습니다. 그래서
istioctl proxy-config로 실제 생성물을 볼 수 없고, 번역 규칙을 알고 손으로 등가 Envoy 설정을 만들어 동작을 확인합니다. 같은 규칙이 운영 클러스터의proxy-config출력에 그대로 보입니다. istioctl validate는 올바른 EnvoyFilter 에도 'exposes internal implementation details' 경고를 냅니다. 이 경고는 정상이며, 값 안의 필드를 문제 삼는 경고가 따로 나오는지를 보세요. 경고는 표준 오류로도 나오니 파일에 담을 때2>&1을 붙입니다.- Envoy 의 오류 문구는 단어 사이 공백이 일부러 흔들립니다(
no such field). 눈으로 읽을 때 놀라지 마세요. - 단계마다 설정 파일 이름이 다릅니다. 앞 단계의 파일은 복사해서 쓰고 고치지 마세요 — 앞 단계 채점이 그 파일을 봅니다.
- Envoy 를 띄울 때는
setsid --fork nohup envoy -c <파일> --log-level warn > <로그> 2>&1 </dev/null로 셸에서 완전히 떼어 놓으세요. 다시 띄우기 전에는pkill -x envoy로 정리합니다 (pkill -f 'envoy -c'는 그 문자열이 든 셸 자신까지 죽입니다). - 업스트림 흉내용 서버가 이미지에 있습니다:
python3 /opt/lab/envoy/upstream.py <포트> ok|fail|slow. 응답 본문은<모드>:<포트> <경로>입니다. - 설정을 고친 뒤에는 띄우기 전에
envoy --mode validate -c <파일>로 먼저 거르세요. 클러스터 이름에|가 들어가므로 YAML 에서는 반드시 따옴표로 감쌉니다.
8个步骤
- router 앞에 fault 를 끼우는 EnvoyFilter 를 쓴다
- 패치의 '어디에' 와 '무엇을' 을 네 칸으로 읽는다
- 패치가 적용된 모습을 Envoy 로 세워 418 을 받는다
- router 뒤에 끼우면 istioctl 은 통과, Envoy 는 거절
- VirtualService 식 퍼센트를 옮겨 적으면 경고만 나온다
- proxyVersion 으로 패치를 한 판에 묶는다
- 선택자 없는 EnvoyFilter 로 네임스페이스 전체에 요청 제한을 건다
- EnvoyFilter 를 쓰기 전의 점검표로 정리한다