Istio Graceful Termination 사내 도입 런북 (홈랩 실험 → 사내 적용 가이드)
홈랩 graceful-termination 실험 결과를 프로덕션 IGW 환경에 이식하는 6단계 런북이다. 머릿속에 담을 한 장면: LB는 backend의 살아있음 여부를 오직 health check 응답으로만 판정한다 — 그래서 LB를 직접 명령할 권한이 없어도, hc 사이드카가 preStop 동안 /health_check.html의 200/503 타이밍을 단계적으로 바꾸면 LB의 “이 backend를 DOWN 마킹할지"를 간접 조종할 수 있다. 이 한 줄이 6단계 전부를 푼다. 본 문서는 각 단계가 무엇을 검증·제어하는지 + 어디서 깨지는지에 집중하고, FSM 상세는 HC FSM 정본, grace period 산정은 프로덕션 적용을 참조한다.
대상환경: Istio 1.30 IGW(istio-ingressgateway) + 외부 L4/L7 LB(Citrix NetScaler 또는 HAProxy). 대상독자: rolling update 중 5xx/connection drop을 0으로 만들려는 SRE. 선행개념: hc 사이드카 FSM(OPEN/DRAINING/CLOSING/CLOSED/FAULT), Envoy drain. FSM 상태명은 게이트 비유를 사용한다.
1. 왜 이 런북이 존재하나 — 문제와 전제
평범한 rolling update에서 IGW pod 하나가 종료될 때 무슨 일이 벌어지는지 보자. kubelet이 SIGTERM을 보내고, 거의 동시에 Service의 endpoint에서 빠지지만, 외부 LB는 그 사실을 즉시 모른다. LB는 자기 health check 주기(inter × fall)가 돌아 backend를 DOWN으로 마킹할 때까지 계속 신규 요청을 그 pod로 보낸다. 더 나쁜 건, LB가 DOWN을 마킹하는 바로 그 순간 downStateFlush ENABLED(HAProxy로 치면 on-marked-down shutdown-sessions) 옵션이 켜져 있으면, 아직 처리 중이던 in-flight 요청까지 즉시 RST로 끊어버린다. 클라이언트 입장에서는 502/connection reset이다.
여기서 근본 제약이 하나 더 있다: 서비스 팀에는 LB에 “이 backend를 빼라"고 명령할 API 권한이 없다. LB는 인프라 팀 소유다. 그러면 어떻게 무중단 종료를 만드나?
핵심 관찰은 이것이다 — LB가 backend의 생사를 판정하는 유일한 입력은 health check 응답 코드다. 즉 우리가 health 응답을 쥐고 있으면, LB의 backend pool membership을 간접적으로 제어할 수 있다. 이 레버를 쥐는 주체가 IGW pod 안의 hc 사이드카이고, preStop 동안 응답을 어떤 순서로 바꾸느냐가 graceful termination의 전부다. 이 런북은 그 레버를 사내 LB(Citrix)·사내 Helm chart·사내 Prometheus에 안전하게 배선하는 절차다.
원본은 행동·검증·위험신호 체크리스트 중심이므로, 여기서는 각 단계가 어떤 메커니즘을 검증·제어하는지와 LB 동작 모델(HAProxy ↔ Citrix 대응)을 보충한다.
2. 핵심 메커니즘 — health 응답 순서가 곧 LB 제어 레버
앵커: “먼저 다 흘려보내고, 그 다음에 503”
런북에서 알아야 할 인과는 단 하나다 — LB의 backend 판정은 health check 응답에만 의존한다. 그래서 hc 사이드카는 preStop 동안 /health_check.html 응답을 단계적으로 바꿔 LB를 간접 제어한다. 정정된 시퀀스의 핵심은 직관과 정반대다:
“503을 먼저 띄우지 말고, in-flight를 다 흘려보낸 뒤(active=0)에야 503을 띄운다.”
왜? 503을 먼저 띄우면 downStateFlush ENABLED LB가 backend를 DOWN 마킹하면서 in-flight를 즉시 RST하기 때문이다. 순서를 뒤집는 순간 graceful이 깨진다. 그래서 graceful의 진짜 안전 구간은 health 200을 유지하는 DRAINING이고, 503 flip은 보호할 게 없어진 뒤에만 일어난다.
3단계 응답표 — 각 단계가 답하는 질문
| 단계 | /health_check.html |
hc가 하는 일 | LB 거동 | 이 단계가 보장하는 것 |
|---|---|---|---|---|
| DRAINING | 200 유지 | downstream_rq_active + upstream_rq_active == 0을 폴링 |
UP 유지 — 기존 요청 정상 종료 | in-flight 보호 (RST 없음) |
| CLOSING | active=0 확인 후 503 flip(/close-lb) |
LB가 DOWN 마킹하도록 신호 | inter × fall(예: 2s×2=4s) 만에 DOWN → 신규 차단 |
신규 유입 차단 |
| CLOSED | /health(K8s readiness) 503 |
LB_BUFFER 대기 후 readiness까지 내림 → pod 종료 |
per-node 판정도 DOWN | endpoint 완전 제거 후 SIGTERM |
읽는 법: 각 행은 “이 단계가 어떤 위험을 막는가"로 보면 된다. DRAINING은 RST를, CLOSING은 신규 요청 유입을, CLOSED는 조기 종료를 막는다. 세 위험이 서로 다른 시점에 발생하므로 단계가 셋으로 나뉜다.
즉 graceful의 심장은 DRAINING(health 200 유지) 이며, 이 200 유지가 본 문서 핵심 결론(“health 200 유지로 in-flight 보호”)의 실제 메커니즘이다. OPEN(정상 200)과 FAULT(drain timeout 초과 등 비정상)를 포함한 상태별 health 응답표·전이 제약, 그리고 POST /reopen abort 경로와 Warning: 199 헤더(CLOSING→OPEN 재투입) 메커니즘은 HC FSM 정본이 정본이다.
sequenceDiagram
participant K as kubelet
participant H as hc sidecar
participant LB as LB (Citrix/HAProxy)
K->>H: SIGTERM / preStop graceful-drain.sh
Note over H,LB: DRAINING — /health_check.html = 200
loop until active == 0 (or DRAIN_TIMEOUT)
H->>H: poll downstream+upstream_rq_active
LB->>H: GET /health_check.html -> 200 (stay UP)
end
Note over H,LB: CLOSING — /close-lb flips 503
H->>LB: /health_check.html = 503
LB->>LB: mark DOWN after inter x fall
Note over H,LB: LB_BUFFER wait
Note over H: CLOSED — /close flips /health (readiness) 503
H->>K: exit -> pod terminates왜 두 종류의 timeout이 필요한가
이 메커니즘에는 시간 변수가 둘 있고, 혼동하면 그대로 장애가 된다.
terminationDrainDuration— Envoy(ProxyConfig)가 drain을 끝낼 때까지 기다리는 시간. Envoy가 drain.sh보다 먼저 죽으면 보호할 데이터 경로가 사라진다.terminationGracePeriodSeconds— kubelet이 SIGKILL을 보내기 전 유예. 이게 drain·LB_BUFFER 합보다 작으면, drain이 끝나기 전에 pod가 강제로 죽는다.
핵심 불변식: terminationGracePeriodSeconds는 항상 terminationDrainDuration과 DRAIN_TIMEOUT + LB_BUFFER 둘 다보다 커야 한다. 이 산정식이 §5 단계 4의 본체다.
3. 6단계 도입 런북
단계 1 — Staging LB의 downStateFlush 동등 옵션 검증
이 런북의 모든 결론은 “사내 LB가 backend disable 시 in-flight를 즉시 RST한다"는 전제에 걸려 있다. 먼저 그 전제부터 확인한다. backend disable 시 in-flight를 즉시 RST하는지(=current 모드 현상), 아니면 graceful drain하는지 본다.
# long request 중에 backend를 LB에서 수동 disable
curl -o /dev/null -w "http=%{http_code} t=%{time_total}\n" "https://<staging>/sleep?seconds=60" &
# (Citrix GUI: LB Vserver → Service → Disable)
sudo tcpdump -n -i <iface> 'tcp[tcpflags] & tcp-rst != 0' -tttt -w /tmp/rst-staging.pcap
- 즉시 502/RST → downStateFlush ENABLED → drain.sh의 health 조작이 유효(이 런북이 의미 있음).
- 60s 후 정상 응답 → 이미 graceful → drain.sh 역할이 “pool에서 먼저 제외"로 축소됨.
- HTTP keepalive가 켜져 있으면 TCP session 재사용으로 RST 거동이 달라질 수 있음(위험 신호).
단계 2 — IGW Helm chart에 hc 사이드카 주입
레버를 쥘 주체(hc)를 IGW pod 안에 넣는다. 사이드카가 들어가야 health 응답을 우리가 제어할 수 있다.
아래 gateways: istio-ingressgateway: additionalContainers: [...] 중첩 구조는 IstioOperator 기반(spec.values.gateways.istio-ingressgateway.*, 구형 in-cluster gateway 컴포넌트) values 오버레이에서만 유효한 경로다. Istio는 오래전부터 게이트웨이를 컨트롤 플레인과 분리해 설치하도록 권장해 왔고, 1.30 기준 공식 권장 설치 경로는 독립 istio/gateway Helm chart(helm install istio-ingressgateway istio/gateway -n istio-ingress)다 — 이 chart의 values.yaml에서 additionalContainers는 중첩 없이 최상위 키다. 사내 Helm chart가 어느 쪽 구조인지 먼저 확인한 뒤 값 경로를 맞출 것.
gateways:
istio-ingressgateway:
additionalContainers:
- name: hc
image: <사내-registry>/service-a-hc:<tag>
ports: [{ containerPort: 18180 }]
env:
- { name: DRAIN_TIMEOUT, value: "120" }
- { name: LB_BUFFER, value: "10" }
- { name: POLL_INTERVAL, value: "2" }
readinessProbe: { httpGet: { path: /health, port: 18180 }, initialDelaySeconds: 3, periodSeconds: 5 }
livenessProbe: { httpGet: { path: /live, port: 18180 }, initialDelaySeconds: 10, periodSeconds: 10 }
lifecycle:
preStop: { exec: { command: ["/opt/hc/graceful-drain.sh"] } }
검증: kubectl get pod -l app=istio-ingressgateway -o jsonpath 로 2-container(istio-proxy hc) 확인 + hc /health_check.html → 200. 위험 신호: container 없음 → additionalContainers 병합 우선순위 / ImagePullBackOff → imagePullSecret.
단계 3 — LB → hc health endpoint 도달 경로
LB가 우리 health 응답을 실제로 읽을 경로를 깐다. 그리고 이 경로가 “그 노드의 pod 상태"를 정확히 반영해야 한다.
spec:
type: NodePort
externalTrafficPolicy: Local # 노드↔pod 1:1, source IP 보존 + 해당 노드 ready pod 없으면 LB가 그 노드 DOWN 판정
ports:
- { name: http2, port: 80, nodePort: 30080, targetPort: 8080 }
- { name: hc, port: 18180, nodePort: 30180, targetPort: 18180 }
externalTrafficPolicy: Local이 중요한 이유: Cluster면 NodePort가 어느 노드로 들어와도 kube-proxy가 임의 pod로 분산해, health check가 “그 노드의 pod 상태"를 반영하지 못한다 — 즉 우리가 한 pod에서 503을 띄워도 LB는 다른 노드의 200을 받아 계속 UP으로 보고 레버가 먹히지 않는다. Local은 해당 노드의 pod만 응답하므로 LB의 per-node 판정이 정확해진다. 위험 신호: 30180 timeout → 그 노드에 ready pod 없음(Local 특성) → IGW readiness 확인.
단계 4 — terminationGracePeriodSeconds 산정
§2에서 본 두 timeout 불변식을 실제 숫자로 푼다. long request p99을 측정해 drain window를 정한다. 산정식은 프로덕션 적용 정본 §3-4와 통일한다.
DRAIN_TIMEOUT = ceil(p99 × 1.2)
LB_BUFFER = <monitor interval> × <fall count> + 5s
# terminationDrainDuration: Envoy가 drain.sh보다 먼저 죽지 않도록,
# Envoy drain 완료 최대 대기를 >= DRAIN_TIMEOUT로 둔다(고정 마진 임의 가산 X).
terminationDrainDuration >= DRAIN_TIMEOUT # 최소 여유만 추가 가능
terminationGracePeriodSeconds = max(DRAIN_TIMEOUT + LB_BUFFER, terminationDrainDuration) + 30
예: p99=30s → DRAIN_TIMEOUT=36, LB_BUFFER=2×2+5=9, terminationDrainDuration=36
→ max(36+9, 36)+30 = max(45,36)+30 = 75s
(w6 실측은 DRAIN_TIMEOUT 대비 terminationDrainDuration=150을 사용 — 실측 p99에 맞춰 키운 사례)
terminationDrainDuration을 어디에 설정하나 (Istio 1.30, ProxyConfig 필드):
- gateway Deployment의 pod annotation으로 개별 설정:
proxy.istio.io/config: '{"terminationDrainDuration":"150s"}' - 또는 mesh 전역
MeshConfig.defaultConfig.terminationDrainDuration: 150s. - 커스텀 Deployment/Helm 사이드카 주입 시에는 annotation 방식이 게이트웨이 단위로 명시적이라 권장.
WebSocket/long-lived connection이 p99을 끌어올리면 별도 처리 전략 필요(단계 6 참고).
단계 5 — Observability 연결
레버가 잘 먹는지 눈에 보이게 한다. drain이 시간 안에 수렴하는지, 안 되면 알람이 뜨는지.
Envoy 15090(또는 istio-agent 병합 엔드포인트 15020 /stats/prometheus)이 이미 Prometheus로 scrape 중이더라도, 아래에서 쓸 envoy_http_downstream_rq_active·envoy_cluster_upstream_rq_active는 Istio 기본(minimal) Envoy stats 집합에 포함되지 않아 추가 설정 없이는 노출되지 않는다 — proxyStatsMatcher.inclusionRegexps(mesh 전역이면 MeshConfig.defaultConfig, 워크로드 단위면 proxy.istio.io/config annotation)로 해당 stat 이름 패턴을 명시적으로 추가해야 /stats/prometheus에 나타난다. Istio 표준 scrape 대상은 보통 15020(병합)이고 15090은 Envoy 자체 prometheus 포트이므로, 사내가 어느 포트를 긁는지 + proxyStatsMatcher가 설정돼 있는지를 함께 확인한다. 실제 수집할 메트릭 목록은 §6 모니터링 메트릭 표를 정본으로 두고, 여기서는 배선(어디서 무엇을 긁어 어떤 alert로 묶나) 만 다룬다.
drain timeout 초과를 잡는 alert:
- alert: IGWDrainTimeoutExceeded
# 발화 조건: pod 삭제 후 DRAIN_TIMEOUT(=120) 초과해도 in-flight가 남아 있음
expr: |
(envoy_http_downstream_rq_active > 0)
and on(pod) (time() - kube_pod_deletion_timestamp > 120)
for: 30s
<DRAIN_TIMEOUT>은 단계 4에서 산정한 실제 초(예: 120)로 치환.kube_pod_deletion_timestamp는 kube-state-metrics가 켜져 있어야 존재하는 메트릭이다(미설치 시 expr가 항상 빈 결과 → alert 무력화).- 검증: pod 삭제 직후 Grafana에서
envoy_http_downstream_rq_active시계열이 DRAIN_TIMEOUT 안에 0으로 수렴하는지 패널로 확인.
단계 6 — Rollout 정책 + canary
레버가 한 pod에서 동작해도, rollout 정책이 잘못되면 전체 IGW가 동시에 빠져 무용지물이 된다.
spec:
strategy:
rollingUpdate: { maxUnavailable: 1, maxSurge: 1 } # 0→1 (deadlock 방지)
template:
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution: # required는 노드수≥replicas 보장 시만
- weight: 100
podAffinityTerm:
topologyKey: kubernetes.io/hostname
labelSelector: { matchLabels: { app: istio-ingressgateway } }
주의: maxUnavailable=0 + anti-affinity required + replicas=N노드면 새 pod가 앉을 노드가 없어 rollout이 deadlock된다. maxUnavailable=1로 두되, in-flight 보호는 graceful-drain.sh가 담당한다(maxUnavailable은 “동시 종료 수” 제약이지 “in-flight 보호” 제약이 아니다).
canary: staging에서 S1/S3 재현(connection_err=0 확인) → 프로덕션 1% → 10% → 50% → 100%. Flagger/Argo Rollouts로 weight 기반 가능.
4. 실험이 입증한 결과 (이 런북이 작동한다는 증거)
홈랩에서 이 레버를 켰을 때(improved)와 껐을 때(current)의 측정값이다. 추상적 주장 대신 숫자로 본다.
| 시나리오 | current | improved |
|---|---|---|
| S1 long-request (replicas=1) | delete@T+5s → T+9s HAProxy DOWN → RST → 502 / 8.25s | drain.sh가 active=0까지 health 200 유지 → DOWN 안 됨 → 200 / 60.01s |
| S3 continuous (replicas=2→3, rollout) | 5xx=0, connection_err=9 (retries 3이 5xx 흡수, RST는 기록) | 5xx=0, connection_err=0 |
| S4 streaming (replicas=1) | chunks=12/60, curl_exit=92 (HTTP/2 스트림 에러 — CANCEL 여부는 verbose 메시지로 별도 확인 필요) | chunks=59/60, curl_exit=0 |
읽는 법:
- S1이 메커니즘을 가장 깨끗하게 보여준다. current는 health 200을 유지하지 않아 T+9s에 DOWN→RST→502(8.25s에 끊김). improved는 active=0까지 health 200을 잡아 60s 요청을 끝까지 흘려보낸다(60.01s, 200).
- S3은 “5xx=0이 곧 무중단이 아니다"의 증거다. current에서도 5xx=0이지만 connection_err=9 — HAProxy
retries 3이 backend RST를 재시도로 흡수해 클라이언트 5xx는 0이 됐을 뿐, 그 사이 RST가 9번 발생했다. improved에서 connection_err=0. - S4 streaming은 별개 세계 신호다. improved도 chunks 59/60(완벽 60은 아님) —
downstream_rq_active가 connection 수명 내내>0인 무한 stream은 DRAIN_TIMEOUT으로 못 덮는다는 한계를 드러낸다(단계 6/§What 참조).
근본 결론: LB에 명령 권한이 없어도 health check 응답을 조작하면 LB 동작을 간접 제어할 수 있다. 사내 Citrix가 downStateFlush ENABLED(= backend DOWN 시 in-flight 즉시 RST, HAProxy on-marked-down shutdown-sessions와 동치)라면 이 결론이 직접 적용된다.
5. 떴는지 한 번 확인 (장애 대응 점검 순서: rolling update 중 5xx burst)
레버가 어디서 풀렸는지 위에서 아래로 좁힌다. 각 줄은 §2~§3의 어느 단계가 깨졌는지로 매핑된다.
echo show stat | sudo socat /run/haproxy/admin.sock stdio | awk -F, '{print $1,$2,$18,$19}'→ backend DOWN/UP. worker 전부 DOWN이면 hc endpoint 문제(단계 3).kubectl logs <igw-pod> -c hc | grep "event=transition"→ DRAINING→CLOSING이 너무 빠르면 current 모드(drain.sh 미사용, 단계 2).- Envoy
/stats?filter=downstream_rq_active|upstream_rq_active→ drain 중 active>0이면 아직 처리 중(DRAINING 정상 동작). tcpdump 'tcp[tcpflags] & tcp-rst != 0' -tttt→ RST 시점 ↔ HAProxy DOWN 마킹 시점 일치 여부(503 선행 여부, §2 앵커).kubectl describe pod→ preStop 실행 여부, grace period 초과 여부(단계 4).
drain timeout 초과(active>0가 DRAIN_TIMEOUT 넘김) 시: DRAIN_TIMEOUT 증가(grace period도 함께) vs 강제 종료 허용(초과 in-flight RST 감수). WebSocket이 원인이면 별도 전략.
6. 모니터링 메트릭 (disruption 지표 수집 원칙)
| 메트릭 | 소스 | 의미 |
|---|---|---|
envoy_http_downstream_rq_active |
Envoy 15090 | IGW in-flight 수 |
envoy_cluster_upstream_rq_active |
Envoy 15090 | IGW→backend in-flight |
haproxy_server_status |
haproxy_exporter | 1=UP, 0=DOWN |
haproxy_backend_connection_errors_total |
haproxy_exporter | TCP 연결 에러 (5xx보다 민감) |
핵심 함정: 5xx rate만 보면 disruption이 invisible할 수 있다. HAProxy retries 3 같은 LB retry가 backend RST를 재시도로 흡수하면 클라이언트는 5xx를 안 받지만, 그 사이 connection error/지연이 발생한다(S3에서 5xx=0, connection_err=9로 입증). 그래서 5xx + connection error rate를 둘 다 수집해야 한다. 클라이언트 측에서는 curl exit 7(connection refused), 92(HTTP/2 스트림 에러 — 정확한 원인은 curl -v 메시지로 확인) 같은 non-200·non-5xx도 별도 집계.
핵심 정리
- LB의 backend 판정 = health check 응답 한 입력뿐. 그래서 LB 제어권 없이도 health 200/503 타이밍으로 backend 판정을 제어 = graceful termination의 핵심 레버. Citrix
downStateFlush ENABLED환경에 직접 이식 가능. - 순서가 곧 메커니즘: DRAINING(health 200 유지로 in-flight 보호) → CLOSING(active=0 후 503 flip) → CLOSED(readiness 503). 503을 먼저 띄우면 in-flight가 RST된다.
externalTrafficPolicy: Local은 per-node health 판정 정확성의 전제. Cluster면 분산 때문에 health가 노드 상태를 반영 못 함(다른 노드 200을 LB가 받아 레버 무력화).- 두 timeout을 구분하라:
terminationGracePeriodSeconds(kubelet SIGKILL 유예)는 항상terminationDrainDuration(Envoy drain 대기)·DRAIN_TIMEOUT+LB_BUFFER보다 커야 한다. - maxUnavailable=0 + anti-affinity required + replicas=N노드 = rollout deadlock. maxUnavailable=1 + graceful-drain.sh면 disruption 0 유지(maxUnavailable은 “동시 종료 수” 제약이지 “in-flight 보호” 제약이 아니다).
- disruption은 5xx만으로 안 보인다. LB retry가 흡수하므로 connection error rate를 반드시 병행 수집.
What you might be missing
- 상태 순서를 거꾸로 외우기 쉽다. “drain하면 바로 LB에서 빼야지"라는 직관과 달리, DRAINING에서는 health 200을 유지해 in-flight를 끝까지 흘려보낸 뒤(active=0) CLOSING에서야 503으로 flip한다. 503 선행 =
downStateFlush ENABLEDLB가 in-flight 즉시 RST = graceful 실패. - terminationDrainDuration vs terminationGracePeriodSeconds 혼동. 전자는 Envoy(ProxyConfig)의 drain 대기, 후자는 kubelet의 강제 kill 전 유예. terminationGracePeriodSeconds < terminationDrainDuration이면 Envoy가 drain을 끝내기 전에 SIGKILL 당한다. 그래서 grace는 항상 drain·LB_BUFFER 합보다 크게 잡는다.
- alert가 조용히 무력화될 수 있다.
kube_pod_deletion_timestamp는 kube-state-metrics 의존 메트릭이라, 미설치 환경에선 expr가 빈 결과를 내며 IGWDrainTimeoutExceeded가 절대 발화하지 않는다. alert “정상(미발화)“과 “메트릭 부재"를 구분하려면absent()보조 alert를 함께 둔다. - 5xx=0이 곧 무중단은 아니다. LB retry(HAProxy
retries 3)가 backend RST를 흡수하면 클라이언트 5xx는 0이지만 connection error·지연은 발생한다(S3 실측). disruption SLO는 5xx + connection error rate를 함께 본다. - streaming은 이 레버의 사각지대다.
downstream_rq_active가 connection 수명 내내>0인 WebSocket/gRPC bidi는 active=0 폴링이 끝나지 않아 DRAIN_TIMEOUT으로 못 덮는다(S4의 59/60이 그 경계). 무한 stream은 IGW 분리나max_stream_duration같은 별도 전략이 필요 — grace period를 키우는 건 답이 아니다.
검증 기록 (2026-07-05 · Istio 1.30.0 / k8s 1.30.6)
검증 방법: 공식 문서 대조(Istio/Envoy/HAProxy/curl 자료) + homelab 클러스터 실측(terminationDrainDuration/proxyStatsMatcher 동작 검증).
| 주장 | 판정 | 근거 |
|---|---|---|
C1. LB backend 판정은 health check 응답이 유일한 입력이며, HAProxy on-marked-down shutdown-sessions(Citrix downStateFlush ENABLED와 동치)는 DOWN 마킹 시 in-flight를 즉시 RST |
✅ 실측 확인 | haproxy.com/…/shutdown-sessions-server · T30 실측 |
C2. terminationDrainDuration은 SIGTERM~실제 proxy 종료 사이 유예이며, istio-agent가 Envoy drain을 지시·대기 후 kill |
✅ 실측 확인 | istio.io/…/istio.mesh.v1alpha1 · T30 실측 |
C3. terminationGracePeriodSeconds는 항상 terminationDrainDuration·DRAIN_TIMEOUT+LB_BUFFER 둘 다보다 커야 함 |
✅ 실측 확인 | istio.io/…/istio.mesh.v1alpha1 · T30 실측 |
C4. terminationDrainDuration은 proxy.istio.io/config annotation(JSON 형식)으로 워크로드 단위 개별 설정 가능 |
✅ 실측 확인 | istio.io/…/annotations · T30 실측 |
C5. terminationDrainDuration은 MeshConfig.defaultConfig로 mesh 전역 설정도 가능 |
✅ 문헌 확인 | istio.io/…/istio.mesh.v1alpha1 |
C6. terminationDrainDuration 미설정 시 기본값 5초 |
✅ 문헌 확인 | istio.io/…/istio.mesh.v1alpha1 |
| C7. Envoy 자체 prometheus 엔드포인트는 15090, istio-agent 병합 엔드포인트는 15020이며 표준 scrape 대상은 15020 | ✅ 문헌 확인 | istio.io/…/prometheus |
C8. 15090/15020을 이미 scrape 중이면 envoy_http_downstream_rq_active·envoy_cluster_upstream_rq_active가 추가 설정 없이 수집됨 |
❌ 오류 — 본문 교정 | istio.io/…/envoy-stats · T31 실측 |
C9. IGW Helm chart에 hc 사이드카 주입 시 gateways: istio-ingressgateway: additionalContainers: [...] 구조 사용 |
⚠️ 구버전 서술 — 갱신 | istio.io/…/gateway |
| C10. curl exit 92는 ‘HTTP/2 CANCEL’을 의미 | ❌ 오류 — 본문 교정 | curl.se/libcurl/c/libcurl-errors.html |
C11. Envoy admin /stats?filter=<regex>는 이름이 정규식에 매치되는 통계만 반환하며 기본은 부분 매치 |
✅ 문헌 확인 | envoyproxy.io/…/admin |