LabHub
배우기 러닝패스 코스

Envoyの内部構造

Envoyを自分で立ち上げて壊す

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

목표

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 설정은 이 순서로 읽으면 헷갈리지 않습니다.

listener → filter chain → http_connection_manager → route_config → cluster

단계

  1. 최소 설정으로 띄우기 → 01-boot.txt
  2. admin 들여다보기 → 02-admin.txt
  3. 라우트 순서 → 03-route.txt
  4. 타임아웃 → 04-timeout.txt
  5. 재시도 → 05-retry.txt
  6. 아웃라이어 감지 → 06-outlier.txt
  7. 응답 플래그 → 07-flags.txt
  8. 정리 → 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.txtbefore= / 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 에 세 줄 이상. 라우트가 안 먹을 때 가장 먼저 볼 것, 재시도가 위험해지는 경우, UTURX 의 차이.

본문에 순서, 재시도, 플래그 가 들어가야 합니다.