Published on

EKS kube-proxy CrashLoopBackOff iptables 오류 해결

Authors

서버리스가 아닌 EKS(EC2 노드) 환경에서 어느 날부터 kube-proxyCrashLoopBackOff에 빠지고, 로그에는 iptables 관련 에러가 반복되는 경우가 있습니다. 이 증상은 “kube-proxy 자체 문제”라기보다 노드 OS/커널, iptables 백엔드(nft/legacy), 권한/보안 설정, 혹은 클러스터 버전-애드온 불일치가 겹치면서 터지는 경우가 대부분입니다.

이 글에서는 현장에서 가장 자주 만나는 iptables 오류 패턴을 기준으로 원인 분류 → 즉시 복구 → 근본 해결(재발 방지) 순으로 정리합니다.

> 참고로, 네트워크 계열 이슈는 증상이 비슷하게 보일 수 있습니다. 서비스는 503인데 Pod는 Running인 경우의 체크는 EKS에서 Pod는 Running인데 503가 뜰 때 점검도 함께 보시면 진단 속도가 빨라집니다.

1) 증상 확인: kube-proxy가 왜 죽는지 “정확한 로그”부터

먼저 kube-proxy가 어떤 메시지로 죽는지 확인해야 합니다. iptables 오류는 메시지에 따라 원인이 갈립니다.

# kube-proxy 상태
kubectl -n kube-system get pods -l k8s-app=kube-proxy -o wide

# 특정 파드 로그
kubectl -n kube-system logs -l k8s-app=kube-proxy --tail=200

# CrashLoopBackOff면 이전 컨테이너 로그도 확인
kubectl -n kube-system logs -l k8s-app=kube-proxy --previous --tail=200

# 이벤트(스케줄/권한/이미지 풀 등)
kubectl -n kube-system describe pod -l k8s-app=kube-proxy | sed -n '/Events:/,$p'

자주 보이는 로그 패턴은 다음과 같습니다.

  • iptables v1.8.x (nf_tables): RULE_APPEND failed (No such file or directory)
  • Can't initialize iptables table 'nat': Table does not exist (do you need to insmod?)
  • modprobe: FATAL: Module ip_tables not found 또는 nf_nat 관련 모듈 로드 실패
  • permission denied (특히 SELinux/AppArmor/PSP/PodSecurity/컨테이너 권한 문제)

이제부터는 메시지별로 분기해서 해결합니다.

2) 원인 A: iptables 백엔드(nft vs legacy) 불일치

2-1) 가장 흔한 케이스

EKS 노드의 iptables가 nf_tables(nft) 백엔드를 쓰는데, kube-proxy/커널 조합 또는 다른 컴포넌트가 legacy를 기대하면서 체인이 꼬이는 경우가 있습니다. 특히 다음 상황에서 자주 발생합니다.

  • 커스텀 AMI(특히 Ubuntu/Debian 계열)에서 iptables-nft가 기본
  • 노드 부팅 시점/업데이트로 iptables 대안이 바뀜
  • kube-proxy는 iptables 모드인데, 노드는 nft로 동작하며 일부 체인 생성/조회가 실패

2-2) 노드에서 현재 백엔드 확인

문제 노드에 접속해서 확인합니다.

# 노드에서
sudo iptables --version
sudo update-alternatives --display iptables 2>/dev/null || true
sudo alternatives --display iptables 2>/dev/null || true

# nft 사용 여부
sudo iptables-save | head
sudo nft list ruleset | head
  • iptables v1.8.x (nf_tables) 라고 나오면 nft 백엔드입니다.

2-3) 빠른 복구: legacy로 고정(권장되는 경우에 한함)

EKS에서 일반적으로 안정적으로 운영하려면(특히 레거시 구성/툴체인과 섞여 있다면) iptables legacy로 고정하는 것이 빠른 해결이 됩니다. 다만 노드 OS/커널 및 보안 정책에 따라 nft가 더 적합한 환경도 있으니 “일괄 적용” 전에는 소수 노드에서 검증하세요.

Ubuntu/Debian:

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 2>/dev/null || true
sudo update-alternatives --set ebtables /usr/sbin/ebtables-legacy 2>/dev/null || true

sudo iptables --version

Amazon Linux 2는 대체로 legacy 기반이지만, 커스텀 이미지/패키지 업데이트로 변경될 수 있어 확인은 동일하게 하세요.

2-4) 재발 방지: Launch Template/UserData로 고정

노드가 교체될 때마다 다시 터지지 않도록 UserData에 넣어 고정합니다.

#!/bin/bash
set -euxo pipefail

if command -v update-alternatives >/dev/null 2>&1; then
  update-alternatives --set iptables /usr/sbin/iptables-legacy || true
  update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy || true
fi

적용 후에는 문제 노드를 drain하고 교체하거나, kube-proxy를 재시작합니다.

kubectl -n kube-system rollout restart ds/kube-proxy

3) 원인 B: 커널 모듈(iptables/nat/conntrack) 미로드 또는 커널 기능 부족

로그에 아래처럼 나오면 커널 모듈/기능 문제일 가능성이 큽니다.

  • Table does not exist (do you need to insmod?)
  • Can't initialize iptables table 'nat'

kube-proxy는 iptables NAT 테이블과 conntrack에 의존합니다.

3-1) 노드에서 모듈/기능 확인

# NAT/conntrack 관련 모듈
sudo lsmod | egrep 'ip_tables|iptable_nat|nf_nat|nf_conntrack|br_netfilter' || true

# 필요한 모듈 로드 시도
sudo modprobe br_netfilter || true
sudo modprobe ip_tables || true
sudo modprobe iptable_nat || true
sudo modprobe nf_nat || true
sudo modprobe nf_conntrack || true

# 커널 sysctl 확인
sudo sysctl net.bridge.bridge-nf-call-iptables
sudo sysctl net.ipv4.ip_forward

3-2) 즉시 조치: sysctl/모듈 로드

대부분의 EKS 최적화 AMI는 설정이 되어 있지만, 커스텀 AMI나 하드닝 이미지에서 빠져있는 경우가 있습니다.

# sysctl 적용
cat <<'EOF' | sudo tee /etc/sysctl.d/99-kubernetes-cri.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF

sudo sysctl --system

모듈 영구 로드는 배포판마다 다르지만, 예를 들어:

cat <<'EOF' | sudo tee /etc/modules-load.d/k8s.conf
br_netfilter
ip_tables
iptable_nat
nf_nat
nf_conntrack
EOF

이후 kube-proxy를 재시작합니다.

kubectl -n kube-system rollout restart ds/kube-proxy

3-3) 근본 해결: “지원되는 AMI/커널”로 회귀

커널이 너무 커스텀되어 모듈이 없거나, 컨테이너 최적화 OS에서 iptables 기능이 제한되는 경우가 있습니다. 이때는 **EKS Optimized AMI(또는 Bottlerocket의 권장 네트워크 모드)**로 회귀하는 것이 가장 빠른 근본 해결입니다.

4) 원인 C: kube-proxy 모드/클러스터 설정 불일치 (iptables vs ipvs)

kube-proxy는 iptables 모드 또는 ipvs 모드로 동작할 수 있습니다. ipvs 모드를 쓰려면 노드에 ipvs 커널 모듈이 필요합니다. 설정은 ipvs인데 모듈이 없으면 실패할 수 있습니다.

4-1) kube-proxy 설정 확인

EKS에서는 kube-proxy가 ConfigMap으로 설정되는 경우가 많습니다.

kubectl -n kube-system get cm kube-proxy -o yaml | sed -n '1,200p'

mode: "ipvs" 인지, iptables인지 확인하세요.

4-2) ipvs 모드라면 노드 모듈 확인

sudo lsmod | egrep 'ip_vs|nf_conntrack' || true
sudo modprobe ip_vs || true
sudo modprobe ip_vs_rr || true
sudo modprobe ip_vs_wrr || true
sudo modprobe ip_vs_sh || true
  • ipvs를 쓸 계획이 없다면, ConfigMap에서 mode: "iptables"로 바꾸고 롤아웃하는 것이 단순합니다.
kubectl -n kube-system edit cm kube-proxy
kubectl -n kube-system rollout restart ds/kube-proxy

5) 원인 D: 권한/보안 정책으로 iptables 조작이 차단됨

kube-proxy는 노드 네임스페이스에서 네트워크 규칙을 만지므로, DaemonSet이 privileged에 준하는 권한이 필요합니다. 다음과 같은 환경에서 막힐 수 있습니다.

  • Pod Security Admission(Restricted) 강제
  • 커스텀 PSP(구버전) 또는 OPA/Gatekeeper 정책
  • SELinux/AppArmor 하드닝

5-1) DaemonSet 보안 컨텍스트 확인

kubectl -n kube-system get ds kube-proxy -o yaml | sed -n '1,220p'

일반적으로 kube-proxy는 hostNetwork: true, privileged 또는 NET_ADMIN capability 등이 필요합니다(배포판/이미지에 따라 차이).

정책 때문에 kube-system 네임스페이스까지 제한했다면, kube-proxy 예외 정책을 추가해야 합니다.

6) 원인 E: EKS 애드온/클러스터 버전 불일치 또는 손상

EKS에서 kube-proxy는 관리형 애드온(Add-on)으로 운영하는 경우가 많습니다. 클러스터 업그레이드 이후 kube-proxy 버전이 맞지 않거나, 애드온이 꼬이면 CrashLoopBackOff로 보일 수 있습니다.

6-1) 애드온 상태 확인 및 재설치

# 애드온 상태
aws eks describe-addon \
  --cluster-name <CLUSTER> \
  --addon-name kube-proxy

# 권장 버전 확인
aws eks describe-addon-versions \
  --addon-name kube-proxy \
  --kubernetes-version <K8S_VERSION> \
  --query 'addons[0].addonVersions[0].addonVersion'

재설치/업데이트(운영 환경에서는 변경 창을 잡고 진행 권장):

aws eks update-addon \
  --cluster-name <CLUSTER> \
  --addon-name kube-proxy \
  --resolve-conflicts OVERWRITE

이후 롤아웃 상태를 확인합니다.

kubectl -n kube-system rollout status ds/kube-proxy
kubectl -n kube-system get pods -l k8s-app=kube-proxy -o wide

7) “노드 단위”로만 터질 때: 특정 노드 격리 후 비교

kube-proxy가 일부 노드에서만 죽는다면, 90%는 노드 이미지/패키지/커널/보안 설정 차이입니다.

7-1) 문제 노드만 골라서 cordon/drain

# 문제 노드 확인 (kube-proxy Pod가 올라간 노드)
kubectl -n kube-system get pod -l k8s-app=kube-proxy -o wide

# 노드 격리
kubectl cordon <NODE>

# 워크로드 이동 (DaemonSet 제외)
kubectl drain <NODE> --ignore-daemonsets --delete-emptydir-data

7-2) 정상 노드 vs 문제 노드 비교 체크리스트

  • iptables --version 결과가 nft/legacy 중 무엇인지
  • uname -r 커널 버전
  • lsmod에 NAT/conntrack 모듈 존재 여부
  • /etc/sysctl.d/*k8s* 설정 존재 여부
  • AMI ID, 부팅 스트랩 스크립트(UserData) 차이

8) 빠르게 끝내는 “현장용” 원인-해결 매핑표

  • iptables v1.8.x (nf_tables) ... RULE_APPEND failed:
    • nft/legacy 불일치 가능성 큼 → legacy로 고정 또는 kube-proxy/OS 조합 재검증
  • Table does not exist (do you need to insmod?):
    • 커널 모듈/기능 부족br_netfilter, iptable_nat, nf_conntrack 로드 및 sysctl
  • ipvs 관련 에러:
    • kube-proxy 모드가 ipvs인데 모듈 없음 → ipvs 모듈 로드 또는 iptables 모드로 변경
  • permission denied:
    • 보안 정책/권한 → kube-system 예외 또는 DS 권한 확인
  • 업그레이드 직후/애드온 상태 이상:
    • 관리형 애드온 버전 불일치/손상aws eks update-addon --resolve-conflicts OVERWRITE

9) 재발 방지 운영 팁

  1. 노드 표준화: EKS Optimized AMI(또는 Bottlerocket) + 검증된 UserData만 사용
  2. iptables 백엔드 정책 명시: nft/legacy를 조직 표준으로 정하고, Launch Template에 강제
  3. 클러스터 업그레이드 절차에 애드온 포함: CoreDNS/VPC CNI/kube-proxy를 함께 점검
  4. 노드 교체로 복구 가능한지 항상 확인: 특정 노드만 문제면 “수리”보다 “교체”가 빠르고 안전

네트워크 이슈가 TLS handshake timeout처럼 보이는 경우도 종종 있습니다. 클러스터 내부 DNS/라우팅 이슈와 엮여 증상이 확장될 수 있으니, 필요하면 EKS TLS handshake timeout 해결 - IRSA·VPC·CoreDNS도 함께 점검해 보세요.


부록) 진단용 원샷 스크립트(노드에서 실행)

아래는 문제 노드에 SSH로 들어가 “iptables/모듈/sysctl”을 한 번에 훑는 스니펫입니다.

#!/bin/bash
set -euo pipefail

echo "== kernel =="
uname -a

echo "\n== iptables =="
command -v iptables && iptables --version || echo "iptables not found"

echo "\n== iptables alternatives =="
(update-alternatives --display iptables 2>/dev/null || alternatives --display iptables 2>/dev/null || true)

echo "\n== modules =="
lsmod | egrep 'br_netfilter|ip_tables|iptable_nat|nf_nat|nf_conntrack|ip_vs' || echo "(none of target modules loaded)"

echo "\n== sysctl =="
sysctl net.bridge.bridge-nf-call-iptables net.bridge.bridge-nf-call-ip6tables net.ipv4.ip_forward 2>/dev/null || true

echo "\n== nft ruleset (head) =="
command -v nft && nft list ruleset | head -n 30 || echo "nft not found"

echo "\n== iptables-save (head) =="
iptables-save | head -n 50 || true

이 출력 결과를 기준으로 위의 원인 A~E 중 어디에 해당하는지 빠르게 매칭하면, kube-proxy CrashLoopBackOff의 대부분은 30분 안에 정상화할 수 있습니다.