본문으로 건너뛰기

03-4 · 507 status code와 istiod disconnected 탐지 — 부록으로 남긴 두 사례 (2026-07)

참조한 내용정리 · 이 문서는 아래 원문을 읽고 우리 지식베이스 형식으로 재구성한 요약입니다. 원문 자체가 아니며 정확한 워딩·전체 맥락·그림은 원문에서 확인합니다.

  • 507 Insufficient Storage가 떴지만 스토리지 부족이 아니었습니다. response_code_detailsrequest_payload_exceeded_retry_buffer_limit이었고 Envoy가 retry에 쓸 request body를 replay하지 못하는 상태를 가리키는 local reply입니다.
  • 경계를 그은 건 retry 조건입니다. 요청 크기 제한이 아닙니다. Envoy는 body를 chunk로 스트리밍하므로 수십 MB payload도 평소엔 통과하고 retry가 필요해지는 순간에만 per_connection_buffer_limit_bytes(기본 1MB)가 경계로 올라옵니다.
  • 대응 후보는 buffer 증설 · retry 비활성화 · client retry였습니다. 채널팀이 고른 쪽은 large payload는 애플리케이션 레벨에서 멱등성 키를 갖추고 재시도하는 방향이고 retry를 끄는 선택지는 3-1편의 503 문제 때문에 막혔습니다.
  • 두 번째 사례는 gateway/waypoint가 istiod와 xDS 연결을 잃은 것으로 보이는 상황입니다. 로그에는 lookup istiod.istio-system.svc: i/o timeoutclosed since 1614s ago까지 붙었으나 채널팀은 root cause를 특정하지 못했다고 명시하고 탐지·완화로 방향을 틀었습니다.
  • 기본 readinessProbe는 “최초 xDS config를 받았는가"에 가까워 사후 단절을 못 잡습니다. 보완책으로 잡은 것이 control_plane.connected_state 수집과 failureThreshold: 3 + successThreshold: 3 조합입니다.

채널코퍼레이션의 Istio Ambient mode 도입기 시리즈 3-4편입니다. 3-1편·3-2편·3-3편은 각각 하나의 원인을 끝까지 파고드는 글이었습니다. 3-4편은 운영 중 만난 Istio/Envoy 이슈 중 기억에 남는 두 사례를 부록처럼 묶었습니다.

채널팀은 두 사례 모두 Ambient mode에 한정된 문제가 아니라고 분명히 합니다. 관찰 지점이 waypoint였을 뿐 소재는 Envoy가 요청을 buffering하고 retry하는 방식, 그리고 Envoy·istiod 사이 xDS 연결을 readiness로 어떻게 볼 것인가입니다. sidecar mode나 ingress gateway를 쓰는 환경에도 그대로 적용됩니다. 전부 사이드카 모드 기준인 이 레포의 상위 Istio 챕터와 이 섹션이 여기서 이어집니다.

1. 처음 본 507 status code

1.1 문제 상황

운영 중 애플리케이션에서 낯선 에러가 올라왔습니다.

MailException: MessengerServer Error: failed to send email
MessengerServer Error: failed to send email
status: 507
msg: exceeded request buffer limit while retrying upstream

503이나 504라면 Istio/Envoy를 운영하며 흔히 만납니다. 507은 그렇지 않았습니다. 일반적으로 507 Insufficient Storage는 서버가 요청 처리에 필요한 저장 공간을 확보하지 못했다는 뜻이라 채널팀도 처음에는 애플리케이션 서버의 스토리지 리소스 부족을 의심했습니다.

waypoint access log를 확인하니 이 응답을 만든 쪽은 애플리케이션이 아니었습니다. Envoy 자신이 직접 생성한 local reply였고 근거는 response_code_details였습니다.

request_payload_exceeded_retry_buffer_limit

Envoy 문서는 이 값을 이렇게 설명합니다.

Envoy is doing streaming proxying but too much data arrived while waiting to attempt a retry.

Envoy는 요청 body를 streaming으로 upstream에 전달하면서 retry에 대비해 일부 데이터를 buffer에 붙들고 있었습니다. 그 상태에서 retry용으로 보관해야 하는 request payload가 buffer limit을 넘었다는 뜻입니다.

1.2 507은 request size 제한과는 다르다

헷갈리기 쉬운 대목은 507과 413 Content Too Large의 차이입니다.

구분413 Content Too Large507 + request_payload_exceeded_retry_buffer_limit
계열4xx (클라이언트 오류)5xx local reply (Envoy 생성)
판단 기준서버가 정의한 request body size 제한 초과retry에 필요한 request body를 buffer에 보관하지 못함
의미“이 payload 자체가 너무 커서 거부한다”“retry하려면 body를 replay해야 하는데 replay할 수 없다”
발생 시점요청 수신 시점에 결정적으로large payload + retryable failure가 겹칠 때만

정상적인 streaming proxying만 놓고 보면 큰 payload 자체가 항상 문제는 아닙니다. Envoy는 request body 전체를 메모리에 올려두고 upstream으로 보내지 않고 chunk 단위로 받아 upstream으로 흘려보냅니다. payload 전체가 수십 MB라도 Envoy가 한 번에 그 전체를 buffer로 들고 있을 필요는 없습니다.

문제는 retry에서 생깁니다. Envoy가 upstream 실패 시 요청을 재시도하려면 같은 request를 다시 upstream으로 보낼 수 있어야 합니다. 그런데 POST body처럼 이미 streaming으로 흘려보낸 데이터를 다시 보내려면 적어도 retry에 필요한 만큼의 request body를 Envoy가 buffer에 들고 있어야 합니다. buffer가 limit을 넘는 순간 Envoy는 더 이상 retry용 request body를 보관하지 못합니다. 그 뒤 upstream reset이나 5xx 같은 retry 조건이 발생하면 요청을 replay할 수 없어 507 local reply를 반환합니다.

이번 문제는 “payload가 커서 실패했다"보다 “large payload request에서 retry가 필요해지는 순간 실패했다"에 가깝습니다.

1.3 왜 하필 507인가

Envoy 구현을 보면 request_payload_exceeded_retry_buffer_limit 상황에서 Http::Code::InsufficientStorage, 즉 507을 local reply로 보냅니다. 개념적 흐름은 이 순서입니다.

같은 요청이 두 갈래로 갈립니다 — retry 시점에 buffer가 body를 들고 있으면 replay해서 재시도하고, 1MB를 넘겨 buffering을 포기한 뒤였다면 507 local reply가 나갑니다.
도식 텍스트
  • 클라이언트
  • waypoint Envoy
  • upstream 앱
  1. 1. large POST body를 chunk로 전송
  2. 2. 받은 chunk를 그대로 흘려보냄
  3. retry·shadow에 대비해 같은 body를 decoding buffer에도 적재 · 경계 = per_connection_buffer_limit_bytes (기본 1MB)
  4. 분기 — 누적 ≤ 1MB — replay 가능
    1. 3. upstream reset / 5xx
    2. 4. buffer의 body를 replay해 retry
    3. 5. 정상 응답
  5. 누적 > 1MB — retry state reset
    1. buffering 포기 — 이 시점부터 body를 replay할 수 없다
    2. 3'. upstream reset / 5xx
    3. 4'. 507 local reply

이 buffer는 retry뿐 아니라 shadowing에도 쓰이고 Envoy는 새 data를 더한 크기가 effective buffer limit을 넘는지 매번 확인합니다. 실제 Envoy 코드에서도 buffer limit을 넘는 순간 retry state를 reset하고 이후 local reply를 만들 때 response code detail을 RequestPayloadExceededRetryBufferLimit으로 설정합니다. 그래서 관측되는 조합은 이렇습니다.

response_code: 507
response_code_details: request_payload_exceeded_retry_buffer_limit
body: exceeded request buffer limit while retrying upstream

1.4 per_connection_buffer_limit_bytes와 1MB

조사하다 마주친 설정이 Envoy의 per_connection_buffer_limit_bytes입니다. Envoy의 기본 buffer limit은 1MB이고 Istio에서 별도 설정을 하지 않으면 이 기본값의 영향을 받을 수 있습니다.

이 값은 “request payload 최대 크기"와 같은 의미가 아닙니다. 큰 request라도 streaming만 된다면 통과합니다. retry용으로 Envoy가 request body를 보관해야 하는 상황에서만 이 buffer limit이 사실상의 경계로 동작합니다.

운영자 입장에서 난감한 건 여기입니다. request body size 제한을 걸어둔 적이 없는데도 특정 조건에서만 일정 크기 이상의 요청이 507로 실패합니다. 그 조건이 “large payload + retryable failure"라 평소에는 드러나지 않다가 장애나 reset이 겹칠 때만 나타나고 재현도 추적도 어렵습니다.

1.5 대응 방안 검토

채널팀이 검토한 방안은 이렇습니다.

방안내용채널팀의 판단
① buffer limit 증설buffer 키워 retry buffer 유지상한 설정 어려움 · 메모리 압박 우려로 미채택
② retry 비활성화retry를 끄면 replay 자체가 불필요3-1편의 reset 대응이 무너져 503 노출
③ client에서 retrylarge payload 요청은 client/애플리케이션 레벨에서 재시도가장 현실적인 방향으로 선택

① buffer limit을 늘립니다. 가장 직접적입니다. 실제로 이 값을 늘리면 더 큰 payload에서도 retry buffer를 유지할 수 있습니다. 하지만 몇 MB까지 허용할지 정해야 하고 media나 file upload처럼 payload가 매우 큰 요청까지 고려하면 값을 무작정 키우기 어렵습니다. buffer limit은 실제로 데이터가 쌓일 때 쓰이더라도 많은 connection에서 큰 request가 동시에 들어오면 메모리 압박으로 이어질 수 있습니다. 전체 gateway/waypoint에 일괄 적용하면 blast radius가 커지고 특정 route나 service에만 적용하면 관리 복잡도가 올라갑니다.

② retry를 끕니다. retry를 하지 않으면 request body를 replay할 필요가 없으므로 문제 자체가 사라집니다. 채널팀 환경에서는 선택하기 어려웠습니다. 3-1편에서 다뤘듯 Ambient mode 운영 중 waypoint/ztunnel 구간의 reset에는 retry가 필요하다는 사실을 이미 확인했기 때문입니다. retry를 끄면 507은 줄지만 다른 많은 요청에서 503이 사용자에게 노출될 수 있습니다.

③ large payload는 client에서 retry합니다. 가장 현실적인 방향이었습니다. Envoy가 모든 큰 POST body를 안전하게 buffer해두고 재시도하기에는 비용이 큽니다. large payload 요청은 보통 업로드, 메일 발송, 문서 처리처럼 요청 자체가 무겁고 오래 걸리는 작업입니다. 이런 API는 애플리케이션 레벨에서 멱등성 키(idempotency key)나 중복 처리 방어를 갖추고 5xx가 오면 재시도하는 편이 더 명시적이고 안전합니다.

원문이 뽑은 교훈은 이렇습니다. retry를 켜면 large request body에 보이지 않는 buffer limit이 생기고 큰 payload가 항상 실패하지는 않지만 retry가 필요한 순간에는 507 request_payload_exceeded_retry_buffer_limit으로 실패합니다.

2. istiod와 disconnected 된 것으로 보이는 gateway/waypoint

원문은 이 사례의 원인을 명확히 찾지 못했다고 명시합니다. 아래 내용도 “원인은 이것입니다"가 아니라 관찰한 현상과 탐지·대응용으로 검토한 메트릭 위주의 정리입니다.

2.1 문제 상황

운영 중 특정 gateway/waypoint Envoy가 istiod와 맺은 xDS 연결을 제대로 유지하지 못하는 것처럼 보이는 상황이 있었습니다. 당시 로그에는 이런 메시지가 반복해서 찍혔습니다.

"DeltaAggregatedResources" gRPC stream to xds-grpc closed:
14 (Unavailable): lookup istiod.istio-system.svc: i/o timeout (closed since 1614s ago)

각 필드의 의미는 이렇습니다.

필드의미
DeltaAggregatedResourcesEnvoy가 Delta xDS stream을 사용 중
xds-grpcEnvoy가 바라보는 xDS cluster. 실제로는 pod 내부의 pilot-agent UDS proxy
14gRPC Unavailable
lookup istiod.istio-system.svc: i/o timeoutpilot-agent가 istiod로 연결하는 과정에서 DNS timeout 발생
closed since 1614s ago최초 실패 이후 오랜 시간 stream close/retry가 반복됨

로그만 보면 istiod DNS lookup이 실패한 것처럼 보였고 lookup ... i/o timeout이라는 메시지 자체도 DNS resolution failure에 가까웠습니다. 그래서 처음에는 CoreDNS나 cluster 전반의 DNS 문제를 의심했습니다.

하지만 같은 시간대에 cluster 전체에서 광범위한 DNS lookup error가 보이지 않았고 CoreDNS 자체에도 눈에 띄는 이상이 없었습니다. 메시지는 DNS failure처럼 보였지만 root cause를 CoreDNS로 지목할 근거는 부족했습니다.

이 대목에서 채널팀은 태스크의 방향을 바꿨습니다. DNS lookup을 계속 파고들어 단일 root cause를 특정하는 쪽은 접었습니다. 대신 비슷한 상황이 다시 생겼을 때 gateway/waypoint가 오래된 xDS config만 들고 계속 traffic을 받는 상태를 어떻게 피할지, 못 피한다면 어떻게 빨리 탐지하고 완화할지에 초점을 맞췄습니다.

2.2 Envoy · pilot-agent · istiod의 연결 구조

Istio gateway/waypoint Pod 안에는 Envoy와 pilot-agent가 함께 있습니다. Envoy 입장에서 xDS upstream은 xds-grpc cluster지만 이 cluster의 실제 목적지는 원격 istiod가 아니라 같은 Pod 안의 pilot-agent UDS입니다.

장애 지점은 두 구간으로 갈립니다 — ① Envoy↔pilot-agent는 Pod 내부 UDS라 거의 실패하지 않고, 이번에 깨진 곳은 Kubernetes DNS와 network를 거치는 ② 구간입니다.
도식 텍스트
  • Envoy — xds-grpc cluster
  • pilot-agent — 같은 Pod · xDS proxy
  • istiod — xDS 원본 · TCP/TLS
  • cluster DNS — istiod.istio-system.svc
  • ① UDS
  • ② TCP/TLS
  • 이름 해석

이번에 관찰한 로그는 ② 구간에서 실패했고 그 에러가 ① 구간의 gRPC stream close로 전파된 형태였습니다.

2.3 pilot-agent 에러는 Envoy에 전파된다

Istio agent의 xDS proxy 구현을 보면 pilot-agent는 istiod upstream 연결 실패를 Envoy downstream stream에 숨기지 않고 전파합니다. 개념적으로는 다음과 같습니다.

// upstream: istiod로의 xDS gRPC stream
upstreamMsg, err := upstream.Recv()
if err != nil {
    errChan <- err
    return
}

// downstream: Envoy로의 xDS gRPC stream
if err := downstream.Send(upstreamMsg); err != nil {
    errChan <- err
    return
}

istiod 쪽 stream에서 Recv() 에러가 발생하면 upstream error channel로 전달되고 handler가 error를 반환합니다. 그러면 Envoy가 보고 있던 gRPC config stream도 닫힙니다.

onRemoteClose()
  -> control_plane.connected_state = 0
  -> setRetryTimer()
  -> backoff 후 다시 xDS stream 생성
istiod 연결 실패는 pilot-agent를 거쳐 Envoy의 stream close로 전파되고, backoff 재연결 때 UDS가 잠깐 열리면서 connected_state가 0과 1 사이를 오갑니다.
도식 텍스트
  • Envoy
  • pilot-agent
  • istiod
  1. 1. xDS stream 생성 (Pod 내부 UDS)
  2. 2. istiod.istio-system.svc 연결 시도
  3. DNS lookup i/o timeout · istiod에 도달하지 못함
  4. 3. Recv() 에러를 downstream에 전파
  5. connected_state = 0 · retry timer 시작
  6. 4. backoff 후 재연결 — UDS는 잠깐 열린다
  7. 이 순간 connected_state = 1 로 보인다
  8. 분기 — istiod 도달 가능
    1. stream 재수립 → 정상
  9. 여전히 실패
    1. 다시 연결 시도
    2. 다시 i/o timeout
    3. 2~4가 반복 — closed since 1614s ago · connected_state가 0↔1로 진동한다

istiod와 통신하지 못하는 상황에서도 관찰 시점에 따라 connected_state=1이 보일 수 있습니다. 이 진동이 2.6에서 flapping 문제로 되돌아옵니다.

2.4 기존 readinessProbe의 한계

Istio의 기본 readinessProbe는 “최초 xDS config를 받았는가"에 더 가깝습니다. pilot-agent status probe 구현에는 receivedFirstUpdate, atleastOnceReady 같은 상태가 있고 한 번 ready가 된 뒤에는 이후 xDS 연결이 끊겨도 readiness가 곧바로 실패하지 않을 수 있습니다. 형태는 이렇습니다.

func (s *server) isReady(context.Context) bool {
    return s.receivedFirstUpdate
}

startup readiness로는 이 방식이 적합합니다. Envoy가 최초 config를 받기 전에 traffic을 받지 않도록 막아 주기 때문입니다. 하지만 한 번 ready가 된 gateway/waypoint가 이후 istiod와 장시간 끊긴 상태는 탐지하지 못합니다. 최초 config를 받은 사실은 그대로 true로 남습니다.

이 차이가 문제로 번지는 건 gateway/waypoint가 이미 받은 config로 한동안 트래픽을 계속 처리하기 때문입니다. 겉보기에는 멀쩡하지만 istiod와 오래 끊기면 새 config, endpoint, certificate rotation 같은 control plane 업데이트를 받지 못합니다. 그래서 process가 live인지뿐 아니라 xDS control plane과 지금 연결되어 있는지도 봐야 합니다.

xDS stream이 왜 이렇게 오래 유지되는 장수 커넥션인지, 그리고 그 커넥션이 재분배되지 않는 이유는 사이드카 모드 기준으로 09 istiod 스케일링과 xDS 커넥션 재분배에서 다룹니다. 여기서 본 단절도 한 번 맺힌 stream이 알아서 낫지 않는다는 같은 성질에서 옵니다.

2.5 어떤 메트릭을 볼 수 있을까

채널팀이 검토한 메트릭은 이렇습니다.

  • envoy_control_plane_connected_state — xDS gRPC stream 개폐 상태. 정상값 1. 한계: Envoy↔pilot-agent stream 상태이지 istiod 직결 상태가 아님
  • envoy_cluster_upstream_cx_active{cluster_name="xds-grpc"} — pilot-agent UDS에 맺은 upstream connection 수. 정상값 보통 1. 한계: 같은 이유로 istiod 연결 상태를 직접 보지는 못함

envoy_control_plane_connected_state

Envoy 내부 stat 이름은 다음과 같습니다.

control_plane.connected_state

Prometheus로 export되면 보통 다음 이름으로 보입니다.

envoy_control_plane_connected_state

이 값은 xDS gRPC stream이 열려 있으면 1, 닫히면 0이 되는 gauge입니다. 2.3에서 설명한 대로 Envoy ↔ pilot-agent stream 상태를 반영하지만 pilot-agent가 istiod 연결 실패를 error로 전파하므로 간접적으로 istiod 연결 문제도 반영할 수 있습니다.

수집에는 전제가 하나 붙습니다. proxyStatsMatcher 설정이 올바르게 되어 있어야 하고 proxyStatsMatcher.inclusionRegexpsPrometheus metric 이름이 아니라 Envoy 내부 stat 이름을 기준으로 매칭됩니다. 아래는 잘못된 설정입니다.

# 틀린 설정: Prometheus metric 이름 사용
meshConfig:
  proxyStatsMatcher:
    inclusionRegexps:
    - "envoy_control_plane_connected_state"

올바른 설정은 Envoy 내부 stat 이름인 control_plane.connected_state를 기준으로 해야 합니다.

# 올바른 설정: Envoy 내부 stat 이름 사용
meshConfig:
  proxyStatsMatcher:
    inclusionRegexps:
    - "control_plane\\.connected_state"

이 설정이 잘못되어 있으면 /stats를 조회해도 stat 자체가 생성되지 않습니다. 실제 장애 당시에도 이 설정이 잘못되어 있어 해당 메트릭을 사후 분석에 쓰지 못했습니다.

envoy_cluster_upstream_cx_active{cluster_name="xds-grpc"}

또 하나 볼 수 있는 값은 xds-grpc cluster의 active connection 수입니다.

envoy_cluster_upstream_cx_active{cluster_name="xds-grpc"}

이 값은 Envoy가 pilot-agent UDS에 맺은 upstream connection 수를 나타내며 정상 상태에서는 보통 1입니다. DNS 장애처럼 pilot-agent가 istiod와 연결하지 못해 gRPC stream이 계속 닫히는 경우에는 0으로 떨어지거나 Pod 자체 scraping이 안 되면 시계열이 소실될 수 있습니다. 이 값도 Envoy ↔ pilot-agent UDS connection을 봅니다. istiod 연결 상태 자체는 아니지만 pilot-agent 에러가 Envoy stream close로 전파되는 케이스에서는 실용적인 신호가 됩니다.

2.6 readinessProbe로 감지하기

메트릭 수집은 alert에는 쓸모가 있지만 Kubernetes Service endpoint에서 해당 gateway/waypoint를 빼 주지는 못합니다. 그 대신 readinessProbe에 xDS 연결 상태를 반영하는 방안을 검토했습니다. 기본 /healthz/readycontrol_plane.connected_state를 함께 확인하는 형태입니다. 이렇게 하면 xDS stream이 일정 시간 0으로 떨어졌을 때 Pod readiness가 false가 되고 Service endpoint에서 제외될 수 있습니다.

여기에도 함정이 있습니다. 기본 successThreshold1입니다.

같은 probe 결과라도 successThreshold 값에 따라 결말이 갈립니다 — 기본값 1이면 재연결 루프의 우연한 1회 성공만으로 endpoint에 다시 들어가 flapping이 납니다.
도식 텍스트
  • kubelet
  • gateway Pod
  • Service endpoint
  1. /healthz/ready + control_plane.connected_state
  2. connected_state = 0 → 실패
  3. 3회 연속 실패 = failureThreshold 3 → NotReady
  4. endpoint에서 제외
  5. 재연결 루프 중 다시 probe
  6. state 1 — 우연히 1회 성공
  7. 분기 — successThreshold = 1 (기본값)
    1. 곧바로 Ready → endpoint 재진입
    2. 다음 실패에 다시 빠진다 — flapping
  8. successThreshold = 3
    1. 연속 3회 성공까지 NotReady 유지
    2. 3회 연속 성공 후에만 재진입

단순히 failureThreshold만 두는 것보다 Ready로 복귀할 때도 연속 성공을 요구하는 편이 안전합니다.

readinessProbe:
  # 기본 /healthz/ready 와 control_plane.connected_state 를 함께 확인
  failureThreshold: 3
  successThreshold: 3
필드효과
failureThreshold33회 연속 실패해야 NotReady로 본다
successThreshold33회 연속 성공해야 Ready로 복귀한다 (기본값 1)

채널팀은 이 방법도 완벽한 해결책이 아니라고 명시합니다. 남는 한계는 이렇습니다.

  • connected_state는 Envoy의 xDS stream 상태를 보는 메트릭입니다. Envoy가 istiod에 직접 붙어 있는지를 보여 주지는 않습니다.
  • probe에서 /stats를 매번 파싱하는 방식은 다소 투박합니다.
  • 설정을 잘못하면 startup 지연이나 오히려 flapping을 만들 수 있습니다.

그래도 기본 readiness는 “최초 config 수신 여부"에 가깝습니다. gateway/waypoint 같은 중요 진입점이라면 지금 control plane과 연결돼 있는지를 따로 보는 편이 운영상 의미가 있다고 판단했습니다.

3. 사이드카 모드 챕터와의 접점

두 사례 모두 Envoy를 data plane으로 쓰면 언제든 마주칠 문제입니다. 이 레포의 상위 Istio 챕터는 전부 사이드카 모드 기준인데 대응 관계는 다음과 같습니다.

이 문서의 소재사이드카 모드에서 같은 문제가 나타나는 자리관련 문서
waypoint의 507 retry buffer사이드카 Envoy·Ingress Gateway도 동일하게 발생01 서비스 메시와 Istio 기초
xds-grpc 단절·stale config사이드카도 pilot-agent 거쳐 istiod에 붙는 구조 동일02 컨트롤 플레인 해부: istiod
xDS stream이 스스로 안 낫는다장수 gRPC 커넥션 미재분배 문제와 같은 뿌리09 istiod 스케일링과 xDS 커넥션 재분배
retry를 켤 수밖에 없게 만든 resetAmbient 데이터 경로에서의 503 추적03-1편 503과 Half-open Connection

Envoy config 레벨에서 xds-grpc cluster나 buffer limit이 실제로 어떻게 박혀 있는지는 2편 Envoy config로 해부하는 Ambient mode에서 확인할 수 있습니다.

이 문서에서 가져갈 것

  • status code를 액면가로 읽지 마라. 507 Insufficient Storage는 디스크와 무관했습니다. Envoy가 만든 local reply인지 애플리케이션 응답인지부터 구분하고 response_code_details를 봐야 실제 원인(request_payload_exceeded_retry_buffer_limit)에 닿습니다. access log에 이 필드를 남기지 않으면 이 사례는 추적 자체가 불가능합니다.
  • retry를 켜면 body 크기에 보이지 않는 상한이 생깁니다. 명시적으로 request size 제한을 걸지 않았어도 per_connection_buffer_limit_bytes(기본 1MB)가 retry 경로에서 경계로 작동합니다. large payload API는 Envoy에 재시도를 맡기지 말고 멱등성 키를 갖춰 애플리케이션 레벨에서 재시도하는 편이 경계가 명확합니다.
  • “한 번 Ready였다"와 “지금 연결되어 있다"는 다릅니다. Istio 기본 readinessProbe는 receivedFirstUpdate 기반이라 startup 게이트로는 맞지만 사후 단절을 못 잡습니다. 이미 받은 config로 트래픽은 계속 흐르므로 겉보기 정상 상태에서 stale config가 누적됩니다.
  • flapping을 막는 건 failureThreshold가 아니라 successThreshold입니다. 재연결 루프는 connected_state를 0↔1로 진동시키므로 기본값 1이면 한 번의 우연한 성공으로 Ready에 재진입합니다. 복귀에도 연속 성공을 요구해야 합니다.
  • 메트릭은 켜뒀다고 수집되지 않습니다. proxyStatsMatcher.inclusionRegexps는 Prometheus 이름(envoy_control_plane_connected_state)이 아니라 Envoy 내부 stat 이름(control_plane.connected_state)으로 매칭합니다. 채널팀은 이 설정이 틀려 정작 장애 사후 분석에 메트릭을 쓰지 못했습니다. 알람용 메트릭은 평시에 실제로 나오는지 확인해둬야 합니다.
  • 원인 규명과 탐지 설계는 별개의 결론이 될 수 있습니다. 채널팀은 DNS root cause를 특정하지 못한 채로 태스크를 “재발 시 빠르게 탐지·완화한다"로 전환했습니다. 원인을 못 찾았다는 사실을 명시하고 방어선을 세우는 것도 유효한 종료 조건입니다.

소스

마지막 수정 일자