- Published on
EKS iptables-legacy/nft 충돌로 네트워크 먹통 해결
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버가 멀쩡히 떠 있고 Pod도 Running인데, 어느 순간부터 Pod 간 통신이 끊기고, Service ClusterIP가 안 붙고, NodePort/Ingress까지 연쇄적으로 죽는 상황이 있습니다. EKS에서 꽤 자주 만나는 원인 중 하나가 iptables 백엔드(legacy vs nft) 혼용입니다. 특히 AMI 교체/OS 업그레이드, 컨테이너 런타임 변경, 보안 에이전트 설치 등으로 노드 내부의 iptables 구현이 바뀌면서, kube-proxy와 CNI가 기대하는 체인/테이블과 실제 커널 규칙이 어긋나 네트워크가 “먹통”처럼 보일 수 있습니다.
이 글에서는 (1) 어떤 증상으로 의심할지, (2) 노드에서 어떻게 판별할지, (3) EKS에서 안전하게 해결/재발 방지하는 방법을 단계별로 정리합니다.
> 참고로 네트워크 장애가 Ingress 502로만 보일 때는 헬스체크/타겟그룹부터 확인하는 게 빠른 경우도 많습니다. 증상이 애매하면 함께 비교해보세요: EKS Ingress 502인데 Pod 로그가 비면? ALB/NLB 헬스체크부터
1) 대표 증상: “쿠버네티스는 살아있는데 통신만 죽음”
iptables 백엔드 충돌은 컨트롤 플레인 장애처럼 보이지 않는 경우가 많습니다. 흔히 아래처럼 나타납니다.
- Pod 상태는 Running, 노드도 Ready인데
kubectl exec로 들어가서 DNS/HTTP가 안 됨- Pod → Pod(다른 노드) 통신 실패
- Pod → Service(ClusterIP) 통신 실패
- NodePort/LoadBalancer가 간헐적으로만 됨
- CoreDNS가 갑자기 타임아웃 (
i/o timeout)을 뿜거나, 외부 egress가 끊김 - kube-proxy 로그에 iptables 관련 오류(체인 없음/규칙 적용 실패)가 간헐적으로 등장
이때 운영자가 흔히 하는 오해는 “CNI가 죽었나?”, “보안그룹인가?”인데, 실제로는 노드 내부에서 iptables 명령이 어떤 백엔드로 동작하느냐가 핵심일 수 있습니다.
2) 원인: iptables-legacy와 iptables-nft 혼용이 왜 위험한가
리눅스에서 iptables는 크게 두 계열로 동작합니다.
- iptables-legacy: 전통적인 xtables 기반
- iptables-nft: nftables 커널 API 위에 iptables 호환 레이어로 동작
문제는 다음 패턴에서 터집니다.
- kube-proxy(iptables 모드) 또는 CNI가 iptables 규칙을 생성
- 그런데 어떤 프로세스/스크립트는
iptables-legacy로, 다른 프로세스는iptables-nft로 규칙을 조회/추가 - 결과적으로 “같은 iptables 명령처럼 보이지만” 실제로는 서로 다른 규칙 세트를 만지게 되어
- kube-proxy가 만든 체인을 다른 쪽에서 못 찾거나, NAT/필터 규칙이 반쪽만 적용되어 통신이 붕괴
이 혼용은 다음 이벤트에서 자주 발생합니다.
- EKS 노드 AMI를 AL2 → AL2023, Ubuntu 버전 업 등으로 교체
- 패키지 업데이트로
iptables기본 대체(alternatives)가 바뀜 - 보안 에이전트/네트워크 에이전트가 iptables를 직접 조작
- 사용자 데이터(UserData)에서 iptables를 만졌는데 노드 부팅 시점/순서가 꼬임
3) 빠른 판별: “지금 이 노드는 legacy인가 nft인가?”
노드에 SSM/SSH로 들어가 아래를 확인합니다.
3.1 iptables 버전과 백엔드 확인
iptables --version
# 예: iptables v1.8.7 (nf_tables)
# 예: iptables v1.8.7 (legacy)
(nf_tables)면 nft 백엔드, (legacy)면 legacy 백엔드입니다.
3.2 alternatives(심볼릭 링크) 상태 확인
Ubuntu/Debian 계열:
update-alternatives --display iptables
update-alternatives --display ip6tables
RHEL/AL2 계열은 alternatives --display iptables 또는 심볼릭 링크를 직접 확인합니다.
readlink -f $(which iptables)
readlink -f $(which ip6tables)
3.3 “혼용”의 흔적 찾기
혼용은 보통 다음처럼 나타납니다.
iptables -S로는 kube-proxy 체인이 보이는데iptables-legacy -S또는iptables-nft -S로 보면 다르게 보임
# 두 명령의 결과가 다르면 혼용 가능성 높음
iptables -t nat -S | head
iptables-legacy -t nat -S | head
iptables-nft -t nat -S | head
kube-proxy 관련 대표 체인:
KUBE-SERVICESKUBE-NODEPORTSKUBE-POSTROUTINGKUBE-MARK-MASQ
CNI(예: Calico)라면 cali- 체인이 보이기도 합니다.
3.4 kube-proxy 모드 확인
EKS 기본은 보통 iptables 모드입니다.
kubectl -n kube-system get cm kube-proxy -o yaml | grep -E "mode|iptables" -n
kubectl -n kube-system logs ds/kube-proxy -c kube-proxy --tail=200
로그에서 iptables-restore 실패, 체인 missing, lock contention 같은 메시지가 단서가 됩니다.
4) 응급 복구: 노드 단위로 정상 백엔드로 “통일”
운영 중인 클러스터에서 가장 안전한 접근은 문제 노드만 격리 → 수정 → 재조인 순서입니다.
4.1 문제 노드 격리/드레인
kubectl cordon <node-name>
kubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data
DaemonSet(kube-proxy, CNI)는 남아있어야 하므로 --ignore-daemonsets를 사용합니다.
4.2 iptables 백엔드를 한쪽으로 고정
Ubuntu/Debian: legacy로 고정(예시)
sudo update-alternatives --set iptables /usr/sbin/iptables-legacy
sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
sudo update-alternatives --set arptables /usr/sbin/arptables-legacy || true
sudo update-alternatives --set ebtables /usr/sbin/ebtables-legacy || true
iptables --version
ip6tables --version
Ubuntu/Debian: nft로 고정(예시)
sudo update-alternatives --set iptables /usr/sbin/iptables-nft
sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-nft
iptables --version
ip6tables --version
> 어떤 쪽이 “정답”인지는 환경에 따라 다릅니다. 핵심은 kube-proxy/CNI가 기대하는 백엔드와 일치시키고, 클러스터 전체 노드에서 일관성을 유지하는 것입니다.
4.3 남아있는 규칙 찌꺼기 정리(주의)
백엔드를 바꾼 직후, 이전 백엔드 규칙이 남아 혼선을 주는 경우가 있습니다. 다만 iptables flush는 SSH/SSM 접속까지 끊을 수 있어 위험합니다. 권장 패턴은:
- 노드를 drain한 상태에서
- kubelet/kube-proxy/CNI가 다시 규칙을 재생성하도록
- 재부팅 또는 해당 컴포넌트 재시작을 활용
예:
sudo systemctl restart kubelet
# 또는 안전하게 재부팅
sudo reboot
재부팅 후 iptables -t nat -S | grep KUBE-SERVICES 등으로 체인이 정상 생성되는지 확인합니다.
4.4 노드 언코든
kubectl uncordon <node-name>
5) 근본 해결: “노드 그룹/AMI 레벨에서” 재발 방지
한두 노드에서 끝나는 문제가 아니라면, 결국 노드 프로비저닝 단계에서 iptables 백엔드를 고정해야 합니다.
5.1 Launch Template UserData에 고정 스크립트 추가
Ubuntu 기반 노드라면 아래처럼 부팅 시점에 alternatives를 고정합니다.
#!/bin/bash
set -euo pipefail
# iptables backend pinning (choose one)
if command -v update-alternatives >/dev/null 2>&1; then
# Example: pin to legacy
update-alternatives --set iptables /usr/sbin/iptables-legacy || true
update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy || true
update-alternatives --set arptables /usr/sbin/arptables-legacy || true
update-alternatives --set ebtables /usr/sbin/ebtables-legacy || true
fi
iptables --version || true
ip6tables --version || true
이미 Terraform로 EKS/노드그룹을 관리한다면, 업그레이드 과정에서 Launch Template/UserData가 바뀌며 재발하기 쉽습니다. 변경 후 노드 Join이 꼬이는 케이스도 있으니, 업그레이드 작업 시 체크리스트로 함께 묶어두면 좋습니다: Terraform로 EKS 업그레이드 후 aws-auth 꼬임으로 노드 Join 실패 해결
5.2 혼용 탐지용 SSM 런북/데몬 체크
운영에서는 “장애가 나고 나서” 찾기보다, 혼용 상태를 조기에 탐지하는 게 효과적입니다.
- SSM Run Command로 전 노드에
iptables --version수집 - 결과에
(nf_tables)와(legacy)가 섞이면 경고
간단 예시:
#!/bin/bash
set -euo pipefail
echo "node=$(hostname)"
iptables --version
ip6tables --version
5.3 kube-proxy를 IPVS로 바꾸면 해결되나?
일부는 kube-proxy를 IPVS 모드로 바꾸면 iptables 의존이 줄어든다고 생각하지만, 현실적으로는:
- IPVS도 여전히 일부 iptables 규칙을 사용하고
- CNI/NetworkPolicy 구현이 iptables에 의존하는 경우가 많아
백엔드 혼용 문제를 근본적으로 회피하기 어렵습니다. 가장 확실한 건 노드에서 iptables 백엔드를 일관되게 유지하는 것입니다.
6) 장애 시나리오별 체크리스트(현장용)
아래 순서로 보면 원인 분리가 빠릅니다.
- 증상 범위 확인
- 특정 노드에 스케줄된 Pod만 통신 실패? → 노드 로컬 문제 가능성↑
- 전체 클러스터? → CNI/라우팅/보안그룹/엔드포인트 등 범위 확장
- 문제 노드에서 iptables 백엔드 확인
iptables --version이 노드마다 다르면 거의 확정
- kube-proxy/CNI 체인 존재 여부 확인
iptables -t nat -S | grep KUBE- | head
- 노드 drain 후 백엔드 통일 + 재부팅
- 재발 방지: UserData/이미지 표준화 + 탐지 자동화
네트워크 장애가 TLS handshake timeout, DNS timeout처럼 보일 때도 결국 노드 레벨 iptables 꼬임인 경우가 있습니다. 특히 “IRSA/VPC/CoreDNS”를 의심하기 전에 노드 통신 경로부터 분해해보는 게 도움이 됩니다: EKS TLS handshake timeout 해결 - IRSA·VPC·CoreDNS
7) 마무리: 결론은 “일관성”과 “프로비저닝 단계 고정”
EKS에서 iptables-legacy/nft 충돌은 겉으로는 CNI나 kube-proxy 문제처럼 보이지만, 본질은 노드 OS 레벨의 iptables 백엔드가 섞여서 규칙이 분리되는 현상입니다. 단기적으로는 문제 노드를 격리해 백엔드를 통일하고 재부팅/재조인으로 복구할 수 있고, 장기적으로는 Launch Template(UserData) 또는 커스텀 AMI 단계에서 백엔드를 고정해 재발을 막는 게 가장 확실합니다.
장애 대응 관점에서 기억할 한 줄:
- 노드별
iptables --version이 섞이면, kube-proxy/CNI 규칙도 섞여 네트워크가 죽을 수 있다.