Envoy設定を読む順序
한국어 원문으로 표시합니다.
한 줄 요약
리스너로 받아서, 필터체인을 통과시키고, 라우트가 고른 클러스터의 엔드포인트로 보낸다. Envoy 설정은 전부 이 다섯 단어의 조합이다.
왜 이게 필요했나
Envoy 설정 JSON 은 처음 보면 압도적이다. 그런데 구조는 단순하다. 들어오는 쪽과 나가는 쪽이 짝을 이룬다.
| 개념 | 대응하는 것 | 하는 일 |
|---|---|---|
| Listener | nginx 의 server { listen } |
어느 주소·포트에서 받나 |
| Filter chain | nginx 의 location + 모듈 |
받은 것을 어떻게 처리하나 |
| Route | nginx 의 location 매칭 |
어느 백엔드로 보낼지 고른다 |
| Cluster | nginx 의 upstream |
백엔드 묶음(정책 포함) |
| Endpoint | upstream 의 server 한 줄 |
실제 주소:포트 |
어떻게 동작하나
요청 하나가 지나가는 길은 이렇다.
클라이언트
→ Listener (0.0.0.0:15001)
→ Filter chain (TLS 종료 → HTTP 커넥션 매니저)
→ Route (Host: shop.example.com, path /api/* )
→ Cluster (outbound|8080||shop.default.svc.cluster.local)
→ Endpoint (10.244.1.7:8080) ← 로드밸런싱으로 하나 고름
핵심은 클러스터와 엔드포인트가 나뉘어 있다는 것이다. 클러스터는 정책(로드밸런싱 알고리즘, 커넥션 풀 크기, 이상치 감지, 서킷 브레이커)을 담고, 엔드포인트는 그 순간의 실제 주소 목록이다. 파드가 뜨고 죽어도 클러스터 정의는 그대로고 엔드포인트 목록만 바뀐다.
xDS — 설정이 흘러드는 통로
Envoy 는 설정 파일을 다시 읽고 재시작하지 않는다. 컨트롤 플레인이 gRPC 스트림으로 밀어 넣는다. 종류마다 이름이 있다.
| 약자 | 무엇을 보내나 |
|---|---|
| LDS | Listener |
| RDS | Route |
| CDS | Cluster |
| EDS | Endpoint |
| SDS | 인증서(Secret) |
Istio 의 istiod 가 하는 일이 정확히 이것이다. 여러분이 쓴 VirtualService 는 RDS 로, DestinationRule 은 CDS 로, 서비스의 파드 목록은 EDS 로 번역돼 각 사이드카에 밀린다.
그래서 "Istio 설정을 고쳤는데 반영이 안 된다" 를 조사하는 방법도 정해진다 — 사이드카가 실제로 받은 설정을 본다.
istioctl proxy-config route <pod> # RDS 로 받은 것
istioctl proxy-config cluster <pod> # CDS
istioctl proxy-config endpoint <pod> # EDS
VirtualService 를 다시 읽는 것은 소용이 없다. 우리가 쓴 것과 프록시가 받은 것이 다를 수 있고, 그 차이가 바로 원인이다.
설정을 못 읽겠을 때 실제로 여는 창
엔보이의 설정 덤프는 수만 줄이 나온다. 통째로 읽으려 하면 아무것도 못 얻으므로, 질문을 정하고 그 조각만 꺼낸다.
curl -s localhost:15000/config_dump | jq '.configs[].dynamic_listeners[]?.name'
curl -s localhost:15000/clusters | grep -E "myapi|health"
curl -s localhost:15000/stats | grep -E "upstream_rq_(5xx|pending|timeout)"
curl -s localhost:15000/server_info | jq '.state'
요청이 어디로 갔는지는 통계가 먼저 답한다. upstream_rq_5xx 가 늘었는지,
upstream_cx_connect_fail 이 있는지, pending 이 쌓였는지에 따라 볼 곳이 갈린다.
설정을 읽는 것은 그다음이다.
503 은 원인이 여러 가지이고, 응답 플래그가 그것을 구분해 준다. 접근 로그에
%RESPONSE_FLAGS% 를 넣어 두면 한 글자 코드로 나온다.
| 플래그 | 뜻 |
|---|---|
UH |
그 클러스터에 건강한 대상이 하나도 없다 |
UF |
상류에 연결하지 못했다 |
UO |
회로 차단기가 열려 막았다 |
NR |
맞는 경로(route)가 없다 |
URX |
재시도 상한에 걸렸다 |
이 한 글자가 "설정이 틀렸다" 와 "대상이 죽었다" 를 가른다. 접근 로그에 응답 플래그가 없으면 그 클러스터에서는 503 하나를 놓고 계속 추측하게 된다.
회로 차단기의 기본값은 대체로 너무 낮다. max_pending_requests 가 1024 인데
동시 요청이 그보다 많으면, 상류는 멀쩡한데 엔보이가 막는다. UO 플래그가 보이면
이 자리다.
설정이 바뀌지 않는 것처럼 보이면 xDS 를 본다. /stats 의
*.update_rejected 가 늘고 있다면 컨트롤 플레인이 보낸 설정을 엔보이가 거절한
것이다. 이때 엔보이는 마지막으로 성공한 설정을 그대로 유지하므로, 겉보기에는
아무 일도 없다. 이 지표에 경보를 걸어 두는 것이 실효가 크다.
흔한 착각
"사이드카가 요청을 느리게 만든다" — Envoy 자체의 지연은 보통 밀리초 이하다. 체감 지연의 대부분은 커넥션 풀 설정, mTLS 핸드셰이크, 그리고 재시도 설정에서 온다. 특히 재시도는 장애 시 부하를 몇 배로 키운다.
클러스터 이름이 랜덤해 보이는 것. outbound|8080||shop.default.svc.cluster.local 은 규칙이다 — 방향|포트|서브셋|호스트. 서브셋이 비어 있으면 DestinationRule 의 subset 을 안 쓴다는 뜻이다. 이 규칙을 알면 설정 덤프에서 원하는 줄을 바로 찾는다.
실무에서 진짜 중요한 것
Envoy 통계 이름도 규칙이 있고, 그게 장애 조사의 지도다.
cluster.<클러스터이름>.upstream_rq_5xx 백엔드가 낸 5xx
cluster.<클러스터이름>.upstream_rq_pending_overflow 커넥션 풀이 넘쳤다
cluster.<클러스터이름>.outlier_detection.ejections_active 쫓겨난 엔드포인트 수
listener.<주소>.downstream_cx_total 들어온 연결 수
upstream_rq_pending_overflow 가 늘어나면 애플리케이션이 느린 게 아니라 커넥션 풀이 작은 것이다. 이 둘은 대처가 정반대다.