🏠 목록 Egress TCP 병목 재현 랩 — 한계를 줄여서 부딪힌다 📄 MD 원본 🌓 테마
istioegresstcpreproductionlabconnection-poolport-exhaustionconntrack

Egress TCP 병목 재현 랩 — 한계를 줄여서 부딪힌다

NOTE

TCP 병목 정본의 한계 수치(Envoy 1024, 포트 28k, conntrack 26만)는 그대로 재현하려면 연결 수만 개가 필요하다. 이 랩의 기법은 반대다 — 한계를 5~20으로 줄여서, 같은 메커니즘을 연결 수십 개로 관찰한다. 재현 4종 각각이 운영에서 만날 실패 시그니처(UO / UF / 무응답 / 정시 절단) 하나씩을 직접 떠올리고, 그 시그니처 구분이 곧 reset 분기 런북이 된다.

대상 환경: Istio 1.30.0, Egress 신원 기반 통제 구성의 테스트 클러스터(ns istio-egress의 egressgateway + ns egress-test의 netshoot-a)가 떠 있는 상태. 대상 독자: 도입 보고서의 병목 표를 "봤다"에서 "겪었다"로 바꾸고 싶은 사람. 범위: 병목 1·2·3·4 재현 + 복구. 완화 운영값 자체는 정본 §06으로 위임.


0. 원칙 — 왜 줄여서 부딪히나

28,000개 연결을 만들어 한계에 도달하는 게 아니라, 한계를 5~20으로 줄여 같은 메커니즘을 소규모로 관찰한다.

[운영 한계]                          [랩 재현]
 maxConnections: 1024(기본)           maxConnections: 5
 ephemeral ports: 28,232              ip_local_port_range: 20개
 nf_conntrack_max: ~260k              nf_conntrack_max: 200
 idle timeout(FW): 30~60min           idleTimeout: 30s
        |                                   |
        +--- 같은 메커니즘, 같은 시그니처 ---+

공유 테스트 클러스터에서 안전하고, 외부 sandbox 엔드포인트에 가는 부하도 연결 수십 개 수준이라 무해하다. (그래도 대상 LB에 이상탐지가 있다면 사전 공유 권장.)

비유 하나: 둑의 높이를 1m로 낮추고 물 한 양동이로 범람을 관찰하는 것. 한계: 비율이 다른 현상은 못 본다 — 예컨대 연결 수만 개 규모에서만 나타나는 Envoy 메모리 압박·FD 고갈은 이 기법으로 재현되지 않는다.


1. 관찰 도구 준비

# gw pod에 디버그 컨테이너 부착 (ss, conntrack 등 사용)
GW=$(kubectl get pod -n istio-egress -l istio=egressgateway -o jsonpath='{.items[0].metadata.name}')
kubectl debug -n istio-egress $GW -it --image=registry.example.com/netshoot:latest \
  --target=istio-proxy -- bash
# 이 셸에서: ss -tan state time-wait | wc -l  등 실행

# Envoy 통계 조회 (별도 터미널, 반복 사용할 함수)
stats() {
  kubectl exec -n istio-egress deploy/egressgateway -c istio-proxy -- \
    pilot-agent request GET stats | grep "api-a" | grep -E "$1"
}

# 부하 발생용 alias
A="kubectl exec -n egress-test deploy/netshoot-a -c netshoot --"

kubectl debug --target=istio-proxy로 붙이는 이유: ephemeral 컨테이너가 istio-proxy와 같은 프로세스/네트워크 네임스페이스를 공유해야 그 pod의 소켓(ss)이 보인다.


2. 재현 1 — Envoy cluster 연결 상한 (운영 기본 1024 → 5)

부딪히는 순서 1번, 가장 먼저 맞을 함정. 상한을 5로 줄이고 10개 연결을 시도한다.

# dr-api-a-tiny.yaml — 외부 호스트용 DR. gw의 upstream cluster에 적용됨
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
  name: api-a-external
  namespace: istio-egress
spec:
  host: api-a.example.com
  trafficPolicy:
    connectionPool:
      tcp:
        maxConnections: 5        # 재현용. 미설정 시 기본 1024
kubectl apply -f dr-api-a-tiny.yaml

# 동시 연결 10개 보유 (sleep이 stdin을 잡아 5분간 연결 유지)
$A bash -c 'for i in $(seq 1 10); do
  (sleep 300 | openssl s_client -connect api-a.example.com:443 \
   -servername api-a.example.com -quiet > /tmp/c$i.log 2>&1) &
done; sleep 15; grep -l "BEGIN CERT" /tmp/c*.log | wc -l'
관찰 명령 기대
성공 연결 수 위 마지막 출력 5 (6번째부터 handshake 실패)
거부 카운터 stats upstream_cx_overflow 5 이상 증가
활성 연결 stats upstream_cx_active 5에서 고정
access log gw 로그의 response flag UO (Upstream Overflow)

핵심 체감: 클라이언트 증상이 AuthorizationPolicy 거부와 똑같은 "TLS 실패/reset"이다. flag(UO vs rbac 로그)로만 구분 가능 — 런북에 들어갈 내용이 바로 이것.

복구: kubectl delete dr api-a-external -n istio-egress (운영에서는 정본 §06-1의 운영값으로 재생성).


3. 재현 2 — Ephemeral 포트 고갈 + TIME_WAIT (28,232개 → 20개)

ip_local_port_range는 K8s safe sysctl이라 pod 단위로 바로 설정 가능하다(kubelet allowlist 불필요).

# values-egress.yaml에 추가 (gateway 차트의 securityContext = pod-level)
securityContext:
  sysctls:
  - name: net.ipv4.ip_local_port_range
    value: "32768 32787"        # 재현용: 포트 20개
helm upgrade egressgateway oci://registry.example.com/charts/istio/gateway \
  --version 1.30.0 -n istio-egress -f values-egress.yaml

# short-lived 연결 반복 (요청마다 gw->외부 신규 연결 = 포트 1개 + TIME_WAIT 60s)
$A bash -c 'ok=0; fail=0; for i in $(seq 1 60); do
  curl -s -m 3 -o /dev/null https://api-a.example.com/api/ping \
    && ok=$((ok+1)) || fail=$((fail+1)); sleep 0.5
done; echo "ok=$ok fail=$fail"'
관찰 명령 (debug 컨테이너) 기대
TIME_WAIT 적체 watch 'ss -tan state time-wait \| wc -l' ~20까지 증가 후 정체
포트 소진 시점 위 curl 출력 약 20번째부터 fail 증가, 60초 지나면 일부 회복 (TIME_WAIT 만료 = 포트 반환의 직접 증거)
connect 실패 stats upstream_cx_connect_fail 증가
access log response flag UF (Upstream connection Failure)

이 실험이 보여주는 산술: 포트 20개 ÷ 60s ≈ 0.33 conn/s가 지속 가능 상한. 운영 기본값(28,232개)에 같은 분수식을 적용한 것이 정본의 470 conn/s다 — 수치는 달라도 식이 같다는 걸 직접 확인하는 게 이 재현의 목적.

복구: sysctls 블록 제거 후 helm upgrade.


4. 재현 3 — 유휴 절단 (idle timeout 1h → 30s)

중간장비(FW)가 유휴 세션을 끊는 상황을 Envoy idleTimeout 축소로 모사한다.

apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
  name: api-a-external
  namespace: istio-egress
spec:
  host: api-a.example.com
  trafficPolicy:
    connectionPool:
      tcp:
        idleTimeout: 30s         # 재현용 (tcp_proxy 기본 1h)
$A bash -c 'time (sleep 120 | openssl s_client \
  -connect api-a.example.com:443 \
  -servername api-a.example.com -quiet)'
# 기대: 120초를 못 채우고 약 30초에 연결 종료. gw access log의 duration ≈ 30000ms

이 재현에 함정 하나가 같이 들어 있다: 여기에 tcpKeepalive를 추가해도 30초 절단은 그대로다. keepalive probe는 데이터가 아니라서 Envoy의 idle timer를 리셋하지 않는다. 역할이 다르다 — keepalive는 방화벽(패킷만 보면 됨)을 깨어 있게 하고, idleTimeout은 Envoy 자신의 기준이라 채널의 최장 유휴 간격보다 길게 직접 설정해야 한다. 직접 keepalive를 넣고 다시 돌려 "그래도 끊긴다"를 확인하면 이 구분이 박힌다.

복구: DR 삭제 또는 운영값(idleTimeout: 1800s)으로 교체.


5. 재현 4 — conntrack 포화 (선택, 전용 노드에서만)

WARN

nf_conntrack_max노드 전역이라 그 노드의 모든 pod에 영향을 준다. gw 전용 테스트 노드가 분리되어 있을 때만 수행할 것.

# gw 노드에서
sudo sysctl -w net.netfilter.nf_conntrack_max=200
watch 'conntrack -C; dmesg | tail -3'
# 재현 2의 curl 루프 실행 -> dmesg에 "nf_conntrack: table full, dropping packet"
sudo sysctl -w net.netfilter.nf_conntrack_max=262144   # 복구

관찰 포인트: 증상이 reset이 아니라 무응답 timeout이다(silent drop — 커널이 패킷을 버릴 뿐 아무에게도 통보하지 않음). 재현 1·2와 클라이언트 체감은 비슷한데 gateway access log에 단서가 약하다는 것까지가 재현 내용 — 그래서 이 병목만 노드 메트릭(conntrack 사용률)으로 선행 감지해야 한다.


6. 정리 — 4개의 시그니처가 곧 런북

재현 축소한 한계 실패 시그니처 운영에서의 대응
1 maxConnections 5 flag UO, upstream_cx_overflow 외부 호스트 DR 풀 상향
2 포트 20개 flag UF, 60s 후 부분 회복 keep-alive 캠페인, replica 분산, 커널 튜닝
3 idleTimeout 30s 정확히 N초 절단, keepalive 무력 idleTimeout을 유휴 간격보다 길게
4 conntrack 200 무응답 timeout, dmesg table full 노드 sysctl + 사용률 알람

전체 분기표(rbac denied, PassthroughCluster, handshake 즉시 실패 포함)는 정본 §07에.


What you might be missing


참조

아카이브 내부 - Egress TCP 병목 정본 — 이 랩이 재현하는 병목 5종의 메커니즘·산술·완화 운영값 - Egress 신원 기반 통제 구성 — 이 랩의 선행조건인 테스트 클러스터 빌드 - Egress Gateway 도입 가이드 (사내 공유본) — 재현 결과가 도입 문서의 병목 표·체크리스트로 압축된 형태 - Egress 운영 정본 — L4 모니터링·graceful shutdown 등 운영 전반 - Envoy response flagsUO/UF 사전

외부 - Envoy circuit breaking — 재현 1의 한계값 출처