LabHub
배우기 러닝패스 코스

Envoy 내부 구조 · 요청이 지나가는 줄 · 이론

끊는 실험보다 느리게 하는 실험

LabHub 에서 이어서 보기

한 줄 요약

요청은 라우트에 닿기 전에 필터 사슬을 지난다. 사슬은 순서가 있고 마지막에는 반드시 요청을 실제로 내보내는 종단 필터(router)가 온다. 그 앞에 무엇을 끼우느냐로 장애 주입 · 속도 제한 · 인증 · 헤더 조작이 애플리케이션 코드를 고치지 않고 붙는다.

왜 이게 필요했나

"결제 서비스가 느려지면 주문 서비스는 어떻게 되나요" 라는 질문에 답하는 방법은 두 가지다. 하나는 코드를 읽고 추론하는 것이고, 다른 하나는 실제로 느리게 만들어 보는 것이다. 앞의 방법은 틀린 답을 자신 있게 내놓는다 — 타임아웃이 어디에 걸려 있는지, 연결 풀이 몇 개인지, 재시도가 몇 번인지가 코드 여러 곳에 흩어져 있기 때문이다.

프록시에 필터를 끼우면 뒤의 방법이 가능해진다. 애플리케이션은 자기가 실험 대상인 줄도 모르고, 배포도 필요 없다. 설정만 바꿔 원래대로 되돌린다.

속도 제한도 같은 이유로 여기에 있다. 모든 서비스가 각자 속도 제한을 구현하면 언어마다 다른 라이브러리를 쓰고 동작도 미묘하게 달라진다. 프록시에서 한 번 하면 모두가 같은 규칙을 얻는다.

어떻게 동작하나

순서가 의미를 가진다. 필터는 적은 순서대로 요청을 본다. 그래서 인증 필터는 속도 제한보다 앞에 두는 것이 보통이고(인증도 안 된 요청에 토큰을 쓸 이유가 없다), router반드시 마지막이다. router 는 요청을 업스트림으로 내보내고 응답을 받아 오는 종단 필터라, 그 뒤에 무엇을 두면 영원히 실행되지 않는다. Envoy 는 이 상태를 실행 중에 두지 않고 설정을 읽는 시점에 거절한다.

장애 주입(fault)은 두 가지를 할 수 있다.

둘 다 percentage 로 비율을, headers 로 조건을 걸 수 있다. 조건을 거는 것이 핵심이다. 조건 없이 켜면 모든 사용자가 실험 대상이 된다. 그리고 비율은 요청마다 독립적으로 뽑는 확률이지 '스무 번 중 정확히 열 번' 이 아니다.

여기서 자주 오해하는 것 하나 — 느리게 만드는 실험이 끊는 실험보다 중요하다. 끊기면 즉시 알지만, 느려지면 연결이 쌓이고 스레드가 묶이고 그다음에야 장애가 된다. 타임아웃과 서킷 브레이커가 제대로 걸려 있는지는 delay 로만 드러난다.

지역 속도 제한(local_ratelimit)은 토큰 버킷이다. max_tokens 만큼 담기고 fill_interval 마다 tokens_per_fill 만큼 채워진다. 토큰이 없으면 429 가 나간다. 중요한 성질은 이름에 있는 '지역' 이다 — 그 프록시 안에서만 센다. 프록시가 열 대면 전체 한도는 사실상 열 배가 된다. 대신 외부에 물어보지 않으니 지연이 없고 그 서비스가 죽어도 영향이 없다.

같은 필터를 자리마다 다르게. 필터는 리스너에 붙지만 설정은 라우트나 가상 호스트 단위로 덮어쓸 수 있다(typed_per_filter_config). 로그인 경로만 빡빡하게, 조회 경로는 느슨하게 같은 요구가 이렇게 풀린다. 전역 설정은 꺼 두고 라우트에서만 켜는 모양도 흔하다.

현장에서 만나는 모습

장애 주입을 켜 두고 잊은 경우. 조건 없이 5% 로 켜 둔 abort 가 몇 주 뒤 "가끔 500 이 납니다" 로 돌아온다. 장애 주입은 반드시 조건을 걸고, 켠 사실을 어딘가에 남기고, 끝나면 지운다.

속도 제한 한도가 실제와 다른 경우. 프록시 대수를 계산에 넣지 않으면 한도가 의도의 몇 배가 된다. 반대로 대수로 나눠 두면 트래픽이 한쪽에 몰릴 때 억울하게 막힌다. 통계의 enabledrate_limited 비율을 보고 조정한다.

속도 제한을 켰는데 아무도 막히지 않는 경우. 두 개의 손잡이를 혼동한 것이 대부분이다. filter_enabled 는 '이 요청을 이 필터의 대상으로 삼을 것인가' 이고, filter_enforced 는 '대상이 된 요청을 실제로 막을 것인가' 이다. 뒤쪽을 0으로 두면 토큰은 세지만 막지는 않는다 — 한도를 정하기 전에 실제 트래픽으로 재 보는 관찰 모드가 된다. 통계에는 그 수가 따로 남는다.

필터 순서를 바꿨다가 성능이 나빠진 경우. 무거운 필터(외부에 물어보는 인증 같은)를 사슬 앞쪽에 두면 나중에 어차피 막힐 요청에도 그 비용을 치른다. 싸고 많이 걸러 내는 필터를 앞에 두는 것이 기본이다.

공식 문서: [Fault Injection](https://www.envoyproxy.io/docs/envoy/v1.38.3/configuration/http/http_filters/fault_filter) · [Local rate limit](https://www.envoyproxy.io/docs/envoy/v1.38.3/configuration/http/http_filters/local_rate_limit_filter) · [HTTP filters](https://www.envoyproxy.io/docs/envoy/v1.38.3/configuration/http/http_filters/http_filters)

다음 실습에서 할 것

종단 필터를 마지막이 아닌 자리에 두면 설정이 거절되는 것을 먼저 보고, 조건을 건 중단과 고정 지연을 직접 넣어 본다. 그다음 비율로만 끊어 스무 번 중 몇 번이 걸리는지 세고, 토큰 버킷으로 429 를 만들어 본 뒤, 같은 필터를 라우트마다 다르게 설정해 한쪽만 빡빡하게 만든다. 마지막에는 필터가 남긴 통계를 읽는다.