Run Envoy Yourself and Break It
한국어 원문으로 표시합니다.
목표
Envoy 를 직접 띄워서 라우팅·타임아웃·재시도·아웃라이어 감지를 하나씩 만들고 부숴 봅니다. Istio 의 데이터 플레인이 바로 이 Envoy 이므로, 여기서 배우는 것은 그대로 메시 장애 대응에 쓰입니다.
환경
envoy --version
curl -s localhost:9901/stats # admin (띄운 뒤)
시작 설정이 /opt/lab/envoy/minimal.yaml 에 있습니다. 베껴서 고치세요.
업스트림 만들기
python3 -m http.server 8081 & # 정상
실패하거나 느린 업스트림이 필요하면 /opt/lab/envoy/upstream.py 를 쓰세요.
python3 /opt/lab/envoy/upstream.py 8082 fail & # 항상 503
python3 /opt/lab/envoy/upstream.py 8083 slow & # 3초 걸림
띄우고 다시 띄우기
setsid --fork nohup envoy -c e.yaml --log-level warn > envoy.log 2>&1 </dev/null
# 고친 뒤에는
pkill -f 'envoy -c'
setsid --fork nohup envoy -c e.yaml --log-level warn > envoy.log 2>&1 </dev/null
setsid --fork 를 꼭 붙이세요. 그냥 & 로 띄우면 셸이 바뀔 때 같이 죽습니다.
--fork 없이 setsid nohup … & 로 띄우면 대화형 셸에서는 살아남지만, 채점이나
단계 준비처럼 셸이 짧게 붙었다 떨어지는 경로에서는 그 셸이 끝날 때 함께 죽습니다.
--fork 는 한 번 더 갈라져 나와 PID 1 에 붙으므로 어느 쪽에서든 살아 있습니다.
채점은 살아 있는 Envoy 의 admin 을 보므로, 죽어 있으면 1단계부터 떨어집니다.
설정이 잘못되면 Envoy 는 뜨지 않고 죽습니다. envoy.log 첫 줄에 이유가 있습니다.
Envoy 는 한 번에 하나만 뜬다
포트를 다르게 줘도 두 번째는 이렇게 죽습니다.
unable to bind domain socket with base_id=0, errno=98 (see --base-id option)
포트 문제가 아니라 공유 메모리 도메인 소켓이 겹치는 것입니다. 설정 두 개를
비교할 때는 pkill -f 'envoy -c' 로 끄고 다시 띄우세요(또는 --base-id 1).
접근 로그가 안 보일 때
두 가지가 흔합니다.
- Envoy 는 파일 접근 로그를 모아서 씁니다. 요청 직후에 grep 하면 아직 없습니다 — 10초쯤 기다리세요.
- 다시 띄우면
> envoy.log가 파일을 비웁니다. 모으는 중이었다면 다시 만들어야 합니다.
읽는 순서
Envoy 설정은 이 순서로 읽으면 헷갈리지 않습니다.
listener → filter chain → http_connection_manager → route_config → cluster
단계
- 최소 설정으로 띄우기 →
01-boot.txt - admin 들여다보기 →
02-admin.txt - 라우트 순서 →
03-route.txt - 타임아웃 →
04-timeout.txt - 재시도 →
05-retry.txt - 아웃라이어 감지 →
06-outlier.txt - 응답 플래그 →
07-flags.txt - 정리 →
08-notes.md
최소 설정으로 띄운다
리스너 하나·클러스터 하나짜리 Envoy 를 띄우고 요청이 업스트림까지 가는 것을 확인해 01-boot.txt 에 남기세요.
/opt/lab/envoy/minimal.yaml 을 베껴 고치세요. 띄울 때는 setsid --fork 를 꼭 붙입니다 — 그냥 & 로 띄우면 셸이 바뀔 때 같이 죽어서 채점에서 떨어집니다. --fork 가 있어야 한 번 더 갈라져 나와 PID 1 에 붙고, 셸이 끝나도 살아남습니다.
setsid --fork nohup envoy -c e.yaml --log-level warn > envoy.log 2>&1 </dev/null
확인: curl -s localhost:10000/. Envoy 설정은 listener → filter chain → route → cluster 순서로 읽으면 헷갈리지 않습니다.
admin 으로 들여다본다
admin 인터페이스(9901)에서 클러스터 목록과 config_dump 섹션 이름을 뽑아 02-admin.txt 에 남기세요.
curl -s localhost:9901/clusters, curl -s localhost:9901/config_dump | jq -r '.configs[]."@type"'. 운영에서 'Envoy 가 내 설정을 진짜로 받았나' 를 확인하는 유일한 방법이 config_dump 입니다 — 파일에 쓴 것과 Envoy 가 들고 있는 것은 다를 수 있습니다.
먼저 쓴 라우트가 이긴다
/api 와 / 두 라우트를 서로 다른 클러스터로 보내고, 순서를 바꾸면 같은 요청의 결과가 달라지는 것을 보이세요. 03-route.txt 에 before= / after= 두 줄로 같은 요청의 응답을 남깁니다.
Envoy 는 라우트를 위에서부터 훑고 첫 매치에서 멈춥니다. prefix: "/" 를 위에 두면 그 아래는 전부 죽습니다. 같은 /api/x 요청을 두 순서로 각각 보내고 이렇게 남기세요(쓴 설정도 함께):
before=api:8084 /api/x
after=ok:8081 /api/x
실무에서 '라우트를 추가했는데 안 먹는다' 의 대부분이 이것입니다.
느린 업스트림을 끊는다
3초 걸리는 업스트림에 1초 라우트 타임아웃을 걸어 504 가 나게 하고, 접근 로그의 응답 플래그와 함께 04-timeout.txt 에 남기세요.
라우트에 timeout: 1s. 접근 로그 포맷에 %RESPONSE_FLAGS% 를 넣으면 UT(Upstream Timeout)가 찍힙니다. 걸린 시간이 정확히 1000ms 인지 보세요 — 그게 Envoy 가 끊은 증거입니다.
로그가 바로 안 보이면 10초쯤 기다리세요. Envoy 는 파일 접근 로그를 모아서 씁니다 — 요청 직후에 grep 하면 아직 없습니다.
재시도가 실제로 몇 번 나갔나
항상 503 인 업스트림에 num_retries: 3 재시도를 걸고, 통계로 재시도 횟수를 확인해 05-retry.txt 에 남기세요.
retry_policy: {retry_on: "5xx", num_retries: 3}. 확인은 curl -s localhost:9901/stats | grep upstream_rq_retry. 접근 로그에는 URX(재시도 소진)가 찍힙니다. 재시도는 공짜가 아닙니다 — 업스트림이 이미 죽어 가는 중이면 재시도가 부하를 4배로 만듭니다.
고장난 엔드포인트를 빼낸다
엔드포인트 2개짜리 클러스터에서 하나만 503 이 나게 하고, 아웃라이어 감지로 그 하나가 빠지는 것을 통계로 보여 06-outlier.txt 에 남기세요.
outlier_detection: {consecutive_5xx: 2, interval: 1s, base_ejection_time: 30s}. 20번쯤 요청한 뒤 curl -s localhost:9901/stats | grep -E 'ejections_active|membership_healthy'. 제대로 되면 20번 중 18번쯤 성공합니다 — 처음 2번으로 고장난 쪽을 알아낸 뒤 다시는 안 보내기 때문입니다.
응답 플래그를 읽는다
지금까지 만든 실패들의 접근 로그를 모아 07-flags.txt 에 남기세요. 서로 다른 플래그가 최소 두 종류 있어야 합니다.
UT 업스트림 타임아웃, URX 재시도 소진, UF 연결 실패, NR 매치되는 라우트 없음, UH 성한 업스트림 없음.
모을 때 두 가지를 주의하세요 — 로그는 모아서 쓰이므로 10초쯤 기다려야 보이고, Envoy 를 다시 띄우면 로그 파일이 비워집니다. 실패를 만들 때마다 그때그때 덧붙이는 편이 안전합니다.
이 플래그를 읽을 줄 아는 것이 Envoy·Istio 장애 대응에서 가장 오래 쓰이는 기술입니다 — 5xx 를 보고 앱을 뒤지기 전에 플래그부터 봅니다.
세 가지를 정리한다
08-notes.md 에 세 줄 이상. 라우트가 안 먹을 때 가장 먼저 볼 것, 재시도가 위험해지는 경우, UT 와 URX 의 차이.
본문에 순서, 재시도, 플래그 가 들어가야 합니다.