- Published on
EKS에서 kubectl port-forward 끊김·hang 해결
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버에 직접 접근할 수 없는 상황에서 kubectl port-forward는 가장 빠른 디버깅 도구입니다. 그런데 EKS에서는 몇 분~몇 시간 후 연결이 끊기거나, 터미널이 멈춘 듯 보이면서 아무 출력 없이 hang 되는 문제가 종종 발생합니다. 특히 사내 VPN, 프록시, NAT, VPC CNI, apiserver 부하, 노드 커널/conntrack 상태가 얽히면 원인 추적이 까다롭습니다.
이 글에서는 EKS에서 kubectl port-forward가 불안정해지는 대표 원인을 증상별로 분류하고, 진단 커맨드 + 해결/우회 전략을 한 번에 정리합니다.
port-forward 동작 원리부터: 어디가 병목이 되나
kubectl port-forward는 단순 TCP 터널이 아니라, 다음 경로를 탑니다.
- 로컬
kubectl→ Kubernetes API Server로 HTTPS 연결 - API Server → 대상 Pod가 있는 노드의 kubelet로 스트림 연결
- kubelet → Pod 네트워크 네임스페이스로 포트 포워딩
즉, 문제가 생길 수 있는 지점이 최소 3곳입니다.
- 로컬 네트워크(회사망/VPN/프록시)에서 API Server까지
- API Server 자체(부하, 타임아웃, 인증 갱신)
- 노드/kubelet/Pod(재시작, readiness, conntrack, CNI)
port-forward가 hang 되는 경우는 보통 “세션은 살아있다고 생각하지만 실제 데이터가 더 이상 흐르지 않는 상태”이고, 끊김은 스트림이 명시적으로 종료되거나 네트워크가 끊긴 상태로 보면 됩니다.
대표 증상 4가지와 빠른 분기
아래 증상으로 먼저 분기하면 원인 추적이 빨라집니다.
1) 몇 분마다 끊김: error: lost connection to pod
- VPN/프록시/방화벽의 idle timeout
- NAT 게이트웨이/중간 장비의 세션 만료
- 로컬 Wi-Fi 전환, 슬립 등
2) 특정 시점부터 hang: 요청이 멈추고 출력도 없음
- API Server 스트림이 stall
- kubelet 스트림/노드 네트워크 문제
- 대상 Pod가 살아있지만 애플리케이션이 accept/response를 못함
3) 시작부터 느리거나 timeout: i/o timeout, TLS handshake timeout
- API Server 접근성/라우팅/DNS 문제
- 보안그룹/NACL/프록시/MTU 문제
관련해서 apiserver 네트워크 타임아웃 계열은 아래 글도 함께 보면 진단에 도움이 됩니다.
4) 특정 Pod/노드에서만 발생
- 노드 conntrack 테이블 압박
- kubelet 불안정
- Pod 재시작/eviction/endpoint 변동
1단계: 문제를 “네트워크 vs Pod”로 즉시 분리
먼저 대상 Pod가 문제인지, 터널 경로가 문제인지를 나눕니다.
A. 같은 Pod에 대해 exec는 안정적인가?
kubectl -n <ns> exec -it <pod> -- sh -c 'date; sleep 1; date'
exec는 되는데port-forward만 끊기면: 스트림/네트워크/중간장비 이슈 가능성이 큼exec도 불안정하면: apiserver 접근/인증/네트워크 전반 이슈
B. Service가 아니라 Pod로 직접 포워딩하라
Service로 포워딩하면 엔드포인트가 바뀌거나(rolling update), 세션이 다른 Pod로 튈 수 있습니다. 디버깅은 Pod 직포워딩이 안전합니다.
kubectl -n <ns> port-forward pod/<pod> 18080:8080
C. Pod가 재시작/교체되는지 확인
kubectl -n <ns> get pod <pod> -o wide
kubectl -n <ns> describe pod <pod> | sed -n '/Events/,$p'
kubectl -n <ns> get pod <pod> -w
RESTARTS가 증가하거나- 노드가 바뀌거나
- 이벤트에
Killing,Evicted,Unhealthy가 보이면
끊김은 정상 동작(대상 Pod가 바뀜)일 수 있습니다.
2단계: kubectl 로그로 “어디서 끊기는지” 잡기
kubectl 자체 디버그 로그가 제일 빠릅니다.
kubectl -n <ns> port-forward pod/<pod> 18080:8080 -v=9
관찰 포인트:
Upgrade request required/SPDY/websocket관련 로그read: connection reset by peercontext deadline exceeded
EKS에서는 API Server가 스트리밍 업그레이드(웹소켓/HTTP2)로 동작하는데, 중간 프록시가 업그레이드를 제대로 통과시키지 못하면 hang/끊김이 발생할 수 있습니다.
3단계: 가장 흔한 원인 1 — 중간 네트워크 장비의 Idle Timeout
왜 port-forward는 특히 잘 끊기나
port-forward는 “오래 유지되는 스트림”입니다. 데이터가 한동안 흐르지 않으면(예: 웹 UI를 열어두기만 함) 중간 장비가 세션을 정리합니다.
해결/우회 1) 주기적으로 트래픽을 발생시키기
가장 간단한 방법은 로컬에서 keep-alive를 만드는 것입니다.
# 10초마다 한번씩 로컬 포워딩 포트에 요청
while true; do
curl -sf http://127.0.0.1:18080/healthz >/dev/null || echo "failed";
sleep 10;
done
- 앱이
/healthz가 없다면/또는 가벼운 endpoint로 대체 - 이 방식은 “끊김”은 줄이지만, 근본 해결은 아닙니다.
해결/우회 2) VPN/프록시 예외 처리
- 사내 프록시를 타는 환경이라면
HTTPS_PROXY/HTTP_PROXY/NO_PROXY설정을 점검하세요. NO_PROXY에 EKS API endpoint 도메인, VPC 엔드포인트 도메인,*.eks.amazonaws.com등을 포함해야 합니다.
예시:
export NO_PROXY="localhost,127.0.0.1,.eks.amazonaws.com,<api-endpoint-hostname>"
해결/우회 3) 안정적인 터널로 감싸기(autossh/ssm)
운영 환경에서는 kubectl port-forward를 직접 오래 열어두기보다, 더 안정적인 채널(예: SSM Session Manager, bastion + SSH tunnel) 위에서 kubectl을 실행하는 편이 낫습니다.
4단계: 가장 흔한 원인 2 — API Server/클러스터 네트워크 불안정
징후
i/o timeout,TLS handshake timeoutkubectl get pods도 간헐적으로 느림- 같은 망에서 다른 클러스터도 불안정
이 경우는 port-forward만의 문제가 아니라 apiserver까지 가는 경로가 흔들리는 것입니다. 아래 항목을 체크합니다.
A. CoreDNS/노드 DNS 문제로 endpoint 해석이 흔들리는지
nslookup <your-cluster-endpoint>
# 또는
curl -vk https://<your-cluster-endpoint>/healthz
B. VPC 엔드포인트(Private EKS) 구성 점검
Private endpoint를 쓰는 경우:
- Interface VPC Endpoint의 SG/NACL이 443을 허용하는지
- 온프레/VPN 라우팅이 올바른지
C. apiserver 타임아웃 계열 이슈를 별도로 진단
아래 글의 체크리스트(라우팅, MTU, 프록시, 보안그룹, DNS)를 그대로 적용하면 빠르게 범위를 줄일 수 있습니다.
5단계: 원인 3 — Pod/노드 문제(특정 노드에서만 끊김)
port-forward가 특정 Pod(=특정 노드)에서만 자주 죽는다면 노드 레벨을 의심해야 합니다.
A. 해당 Pod가 올라간 노드 확인
kubectl -n <ns> get pod <pod> -o wide
B. 노드 상태/이벤트
kubectl describe node <node-name> | sed -n '/Conditions/,$p'
ReadyflappingNetworkUnavailable- 디스크 pressure/memory pressure
C. conntrack 테이블 압박(증상: 네트워크가 간헐적으로 stall)
EKS 워커 노드에서 conntrack이 부족하면 TCP 세션이 이상해집니다. 노드에 SSM으로 들어갈 수 있다면:
sudo sysctl net.netfilter.nf_conntrack_max
sudo cat /proc/sys/net/netfilter/nf_conntrack_count
sudo conntrack -S | head
count가max에 근접하면 위험- 해결은 인스턴스 타입/커널 파라미터/트래픽 패턴 개선이 필요
D. Pod가 Evicted/재스케줄링 되는지
노드 디스크 문제로 Pod가 축출되면 포워딩은 당연히 끊깁니다. 특히 이미지 GC/ephemeral storage 압박이 원인인 경우가 있습니다.
6단계: 원인 4 — 애플리케이션이 “연결은 받지만 응답을 못하는” 경우
port-forward가 hang처럼 보이지만 실제로는 앱이 멈춘 경우도 많습니다.
A. 포트가 실제로 LISTEN 중인지 Pod 내부에서 확인
kubectl -n <ns> exec -it <pod> -- sh -c 'ss -lntp | grep 8080 || netstat -lntp | grep 8080'
B. Pod 내부에서 loopback 호출
kubectl -n <ns> exec -it <pod> -- sh -c 'curl -sv http://127.0.0.1:8080/healthz'
- Pod 내부에서도 느리면 앱/런타임/GC/스레드 고갈 문제
- Pod 내부는 빠른데 port-forward만 느리면 네트워크/스트림 문제
7단계: 실전에서 자주 쓰는 “안정화 패턴” 3가지
패턴 1) 포워딩 프로세스를 감시하고 자동 재시작
끊김이 잦은 환경에서는 그냥 자동 재시작이 운영 효율이 높습니다.
#!/usr/bin/env bash
set -euo pipefail
NS="default"
POD="$1" # 예: mypod-xxxxx
LOCAL_PORT="18080"
REMOTE_PORT="8080"
while true; do
echo "[INFO] starting port-forward..."
kubectl -n "$NS" port-forward "pod/$POD" "$LOCAL_PORT:$REMOTE_PORT" --address 127.0.0.1 || true
echo "[WARN] port-forward exited. restarting in 2s..."
sleep 2
done
- Pod가 교체되는 워크로드면 Pod 이름 대신 label selector로 Pod를 찾아 갱신하는 로직을 추가하세요.
패턴 2) Service 대신 Deployment의 특정 Pod로 고정
디버깅 중에는 롤링 업데이트/오토스케일링이 세션을 끊습니다.
- HPA를 잠시 멈추거나
- 특정 Pod에 포워딩(위에서 설명)
패턴 3) Ingress/ALB 내부 인증을 붙여 “정식 경로”로 접근
장시간 접속이 필요한 웹 UI(예: Grafana, Kibana 등)는 port-forward보다
- 내부 Ingress + 인증(OIDC/IAP)
- ALB Ingress + 제한된 SG
같은 “정식 트래픽 경로”가 훨씬 안정적입니다. port-forward는 어디까지나 디버깅 도구로 두는 것이 좋습니다.
체크리스트: 10분 안에 결론 내기
아래 순서대로 보면 대부분 10~20분 내에 원인이 좁혀집니다.
kubectl port-forward ... -v=9로 끊기는 지점 로그 확보- 같은 시점에
kubectl get pods가 느린지(=apiserver 경로 문제인지) 확인 - Pod 재시작/교체 여부 확인(
get pod -w,describe pod) - 특정 노드에서만 재현되는지 확인(
-o wide) - VPN/프록시/NO_PROXY/idle timeout 의심 시 keep-alive 또는 예외 처리
- 노드 conntrack/pressure/eviction 이벤트 확인
마무리
EKS에서 kubectl port-forward 끊김·hang은 대개 (1) 중간 네트워크 장비의 idle timeout, (2) apiserver까지의 네트워크 품질, (3) 특정 노드의 네트워크/리소스 압박, (4) 앱 자체의 응답 불능 중 하나로 수렴합니다. -v=9 로그와 “Pod 내부 curl vs 로컬 curl”만으로도 원인의 70%는 분리할 수 있고, 나머지는 노드 이벤트/conntrack/프록시 설정을 보면 결론이 납니다.
환경 제약상 근본 원인 제거가 어렵다면, 자동 재시작 스크립트나 더 안정적인 터널(SSM/SSH)로 감싸는 방식이 현실적인 해법이 됩니다.