Published on

Kubernetes apiserver i/o timeout 원인과 해결

Authors

서버 운영 중 어느 날부터 kubectl get pods가 느려지거나, 아예 다음과 같은 에러로 멈추는 상황을 자주 봅니다.

Unable to connect to the server: dial tcp <apiserver-ip>:443: i/o timeout

i/o timeout은 “요청을 보냈는데 정해진 시간 내에 응답을 못 받았다”는 뜻이라, 원인이 하나로 고정되지 않습니다. 네트워크 경로 문제일 수도 있고, API Server가 살아있지만 너무 바빠서 응답을 못 하는 것일 수도 있으며, 중간의 DNS/프록시/보안장비가 끊는 경우도 있습니다.

이 글은 원인을 계층별로 분해하고, 증상별로 바로 확인할 커맨드해결책을 제시합니다. (EKS 같은 관리형도 포함)

1) 증상 분류: “연결 자체가 안 됨” vs “느리다/간헐적”

먼저 같은 i/o timeout이라도 아래 두 부류로 나뉩니다.

  • A. 연결 자체가 성립하지 않음: TCP 443 연결이 안 됨(라우팅/보안그룹/NACL/방화벽/DNS 등)
  • B. 연결은 되지만 응답이 늦음/끊김: control plane 부하, etcd 지연, admission webhook 지연, 네트워크 패킷 손실, MTU 문제 등

가장 빠른 분기점은 TCP connectTLS handshake가 되는지 확인하는 것입니다.

# 1) kubeconfig에서 apiserver endpoint 확인
kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}'; echo

# 2) TCP 연결 확인(도메인/포트)
# -v로 연결 단계 확인
curl -vk https://<apiserver-endpoint>/healthz --max-time 5

# 3) DNS 문제 분리(도메인이면)
nslookup <apiserver-hostname>
  • curlconnect 단계에서 타임아웃이면 A에 가깝습니다.
  • connect는 되는데 TLS/응답에서 지연이면 B 가능성이 큽니다.

2) 원인 1: 클라이언트 네트워크 경로(라우팅/VPN/프록시)

사내망에서만 접근 가능한 프라이빗 클러스터(EKS private endpoint 등)에서 흔합니다.

체크리스트

  • VPN이 끊겼거나 split tunneling 정책으로 apiserver CIDR이 제외됨
  • 회사 프록시가 443을 가로채거나, CONNECT 정책으로 차단
  • 로컬 방화벽/보안 에이전트가 특정 도메인/포트를 드롭

빠른 확인

# 라우팅 테이블 확인(리눅스)
ip route get <apiserver-ip>

# 프록시 환경변수 확인
env | egrep -i 'http_proxy|https_proxy|no_proxy'

# 프록시를 우회해서 확인(필요 시)
HTTPS_PROXY= HTTP_PROXY= NO_PROXY='*' curl -vk https://<apiserver-endpoint>/healthz --max-time 5

해결 팁

  • NO_PROXY에 apiserver 도메인/대역을 포함
  • VPN 라우팅 정책에 apiserver 네트워크 포함
  • 관리형(EKS)이라면 endpoint 접근 정책(퍼블릭/프라이빗, 허용 CIDR)을 재확인

3) 원인 2: DNS 장애(CoreDNS/클라이언트 DNS/사설 존)

apiserver endpoint가 도메인인 경우(특히 EKS), DNS가 느리거나 실패하면 dial tcp까지 가기 전에 지연이 생기거나, 잘못된 IP로 연결 시도하다 타임아웃이 납니다.

확인

# 로컬
nslookup <apiserver-host>
dig +time=1 +tries=1 <apiserver-host>

# 클러스터 내부에서 확인(파드에서)
kubectl run -it --rm dnsutils --image=registry.k8s.io/e2e-test-images/jessie-dnsutils:1.3 --restart=Never -- \
  sh -c "nslookup kubernetes.default.svc && nslookup <apiserver-host>"

해결

  • 사설 DNS(zone) 충돌 여부 확인(동일 도메인 suffix)
  • CoreDNS가 불안정하다면 리소스/레플리카 확장, upstream DNS 점검
  • 노드의 /etc/resolv.conf가 잘못된 DNS를 가리키는지 확인

4) 원인 3: Control Plane/API Server 과부하(정상인데 응답 못함)

연결은 되는데 kubectl전반적으로 느려지고 간헐적으로 timeout이 뜬다면, control plane이 바쁘거나 etcd가 느릴 가능성이 큽니다.

대표 징후

  • kubectl get은 되지만 kubectl apply/kubectl logs/kubectl exec에서 timeout 증가
  • 이벤트 폭증, 오브젝트 수 급증, 컨트롤러 루프 폭주
  • apiserver 로그에 request took too long, context deadline exceeded

(자체 운영 클러스터) apiserver/etcd 상태 확인

# control-plane 노드에서(또는 접근 가능한 곳에서)
# apiserver health
curl -k https://127.0.0.1:6443/readyz?verbose

# etcd health (etcdctl 필요)
ETCDCTL_API=3 etcdctl \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt \
  --key=/etc/kubernetes/pki/etcd/healthcheck-client.key \
  endpoint health

해결 방향

  • etcd 성능/디스크: etcd는 디스크 지연에 매우 민감합니다. 컨트롤플레인 노드 디스크 IOPS/latency를 확인하고, 느리면 스토리지 업그레이드/분리 고려
  • 오브젝트 폭증: 이벤트/리스/엔드포인트/CRD 오브젝트가 과도하면 apiserver와 etcd에 부담
  • 클라이언트 QPS 폭주: 특정 컨트롤러/오퍼레이터가 과도한 watch/list를 반복

관리형(EKS)에서는 etcd를 직접 보긴 어렵지만, CloudWatch/Control plane metrics(가능한 경우)와 함께 클러스터 내 이벤트 폭증이나 문제 컨트롤러를 먼저 의심하는 게 현실적입니다.

5) 원인 4: Admission Webhook 지연(특히 apply/create에서 timeout)

kubectl get은 잘 되는데 applycreate에서 timeout이 난다면, Validating/Mutating Webhook이 느리거나 죽어있는 경우가 많습니다. API Server는 오브젝트 생성/변경 시 webhook 응답을 기다리다가 타임아웃이 나면 전체 요청이 실패합니다.

확인

# webhook 구성 확인
kubectl get validatingwebhookconfigurations,mutatingwebhookconfigurations

# 특정 네임스페이스/리소스에서만 느린지 확인
kubectl apply -f <manifest.yaml> -v=8
  • -v=8 로그에서 어떤 webhook 호출에서 멈추는지 힌트를 얻을 수 있습니다.

해결

  • webhook 대상 서비스/파드가 정상인지 확인(레디니스, 엔드포인트)
  • failurePolicy: Ignore로 임시 완화(보안/정합성 영향 고려)
  • timeoutSeconds 조정(근본 해결은 아님)

AWS Load Balancer Controller 같은 구성요소의 webhook 타임아웃은 별도 케이스가 많습니다. 유사 증상이라면 이 글도 함께 보세요: EKS AWS Load Balancer Controller 500 Webhook 타임아웃 해결

6) 원인 5: 노드/네트워크 플러그인 문제로 인한 간헐적 패킷 손실

apiserver timeout이 “클러스터가 전체적으로 불안정”할 때 동반되기도 합니다. 특히 CNI 문제, 노드 커널/iptables 문제, kube-proxy 이상 등으로 패킷 드롭이 생기면 watch 스트림이 끊기고 재연결이 반복되며, 체감상 kubectl이 매우 느려질 수 있습니다.

노드 상태와 핵심 로그

kubectl get nodes -o wide
kubectl describe node <node>

# 시스템 파드 상태
kubectl -n kube-system get pods -o wide

노드가 NotReady를 오가거나, kubelet이 불안정하면 apiserver 접근 문제처럼 보일 수 있습니다. 특히 EKS에서 kubelet/PLEG 관련 이슈가 있으면 노드가 간헐적으로 죽은 것처럼 보이며, 전체 경험을 악화시킵니다. 관련 점검은 다음 글이 도움이 됩니다: EKS kubelet NotReady - PLEG is not healthy 7가지

7) 원인 6: 리소스 고갈(OOM/CPU throttling)로 apiserver 또는 핵심 컴포넌트가 느려짐

자체 운영(control-plane가 워커와 같이 있는 형태 포함)에서는 apiserver/etcd/coredns가 OOMKilled 되거나 CPU가 심하게 throttling 되면, 그 순간부터 i/o timeout이 연쇄적으로 발생할 수 있습니다.

확인

# OOMKilled 흔적
kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.status.containerStatuses[0].lastState.terminated.reason}{"\n"}{end}' \
  | grep -i oom || true

# 특정 컴포넌트 리소스 사용량(메트릭서버 필요)
kubectl top pods -A | head

OOM이나 메모리 누수 추적 관점은 이 글의 방법론을 그대로 적용할 수 있습니다: Kubernetes OOMKilled 진단과 메모리 누수 추적 실전

해결

  • control-plane 구성요소 리소스 요청/제한 재조정
  • etcd 디스크/메모리 여유 확보
  • 이벤트/로그 폭증 원인 제거(무한 재시도 컨트롤러, 실패 루프)

8) 실전 트러블슈팅 플로우(10분 내 원인 좁히기)

아래 순서대로 하면 “어디가 문제인지”를 빠르게 분리할 수 있습니다.

1단계: endpoint와 기본 연결

APISERVER=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')
echo "$APISERVER"

curl -vk "$APISERVER/healthz" --max-time 5
  • 여기서 타임아웃이면: 클라이언트↔apiserver 네트워크/보안/DNS부터

2단계: DNS vs TCP 분리

# 도메인이면
HOST=$(echo "$APISERVER" | sed -E 's#https?://##' | cut -d/ -f1 | cut -d: -f1)
nslookup "$HOST"

# IP로 직접 찍어보기(가능하면)
IP=$(getent ahosts "$HOST" | awk 'NR==1{print $1}')
curl -vk "https://$IP/healthz" --resolve "$HOST:443:$IP" --max-time 5
  • 도메인은 실패, IP는 성공이면 DNS 문제 가능성이 큼

3단계: 특정 동작에서만 느린지

kubectl get ns --request-timeout=5s
kubectl apply -f manifest.yaml --request-timeout=10s -v=8
  • get은 되는데 apply가 멈추면 webhook/admission 의심

4단계: 클러스터 내부 상태

kubectl get nodes
kubectl -n kube-system get pods
kubectl get events -A --sort-by=.metadata.creationTimestamp | tail -n 50
  • 노드 NotReady, 시스템 파드 재시작, 이벤트 폭증이 보이면 control-plane 부하/노드 문제로 확장

9) 예방: timeout을 “없애는” 게 아니라 “원인을 줄이는” 설정

--request-timeout을 늘리면 겉보기로는 덜 실패할 수 있지만, 근본적으로는 지연을 숨기는 것에 가깝습니다. 대신 아래를 권장합니다.

  • admission webhook은 **가용성(레플리카/리소스)**을 확보하고, 의존 서비스 장애 시 동작(failurePolicy)을 명확히 설계
  • CoreDNS는 적절한 리소스와 스케일링, upstream DNS 안정화
  • etcd는 빠른 디스크와 안정적인 네트워크, 오브젝트/이벤트 폭증 방지
  • 컨트롤러/오퍼레이터는 client-go QPS/Burst 튜닝 및 불필요한 list/watch 감소

10) 마무리: i/o timeout은 “증상”이고, 계층별 분리가 핵심

Kubernetes apiserver i/o timeout은 단일 원인이 아니라, 네트워크(DNS/라우팅/보안) → API Server/etcd 성능 → admission webhook → 노드/CNI 안정성 → 리소스 고갈까지 여러 층에서 발생합니다.

가장 중요한 건 “어디까지는 정상인가”를 빠르게 나누는 것입니다.

  • curl -vk /healthz연결/응답을 먼저 분리
  • kubectl apply -v=8webhook 지연을 식별
  • kube-system과 노드 상태로 클러스터 건강도를 확인

이 흐름대로 보면, 대개 10~30분 내에 범위를 좁히고 재발 방지까지 설계할 수 있습니다.