Kubernetes 워커 노드 NotReady 트러블슈팅
환경
| 항목 | 내용 |
|---|---|
| 플랫폼 | minikube (Docker 드라이버, 멀티노드) |
| OS | Debian 12 (bookworm) on WSL2 |
| Kubernetes | v1.35.1 |
| 컨테이너 런타임 | Docker 29.2.1 |
| 노드 구성 | 마스터 1 + 워커 2 |
증상
$ kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP
minikube Ready control-plane 44h v1.35.1 192.168.49.2
minikube-m02 NotReady <none> 44h v1.35.1 192.168.49.3
minikube-m03 NotReady <none> 44h v1.35.1 192.168.49.4
- 마스터 노드(minikube)만 Ready
- 워커 노드 2개(minikube-m02, m03)는 NotReady
진단 및 해결 과정
1차 원인: 호스트 iptables FORWARD 정책에 의한 네트워크 차단
진단
Step 1 — 노드 상태 상세 확인
$ kubectl describe node minikube-m02 | grep -A 10 "Conditions:"
Conditions:
Type Status Reason Message
---- ------ ------ -------
MemoryPressure Unknown NodeStatusUnknown Kubelet stopped posting node status.
DiskPressure Unknown NodeStatusUnknown Kubelet stopped posting node status.
PIDPressure Unknown NodeStatusUnknown Kubelet stopped posting node status.
Ready Unknown NodeStatusUnknown Kubelet stopped posting node status.
→ 모든 Condition이 Unknown, kubelet이 API 서버에 상태를 보고하지 못하고 있음
Step 2 — kubelet 로그 확인
$ docker exec minikube-m02 systemctl status kubelet
# 로그에서 확인된 에러:
E0327 "Unable to register node with API server"
err="Post \"https://control-plane.minikube.internal:8443/api/v1/nodes\":
dial tcp 192.168.49.2:8443: i/o timeout"
E0327 "Failed to ensure lease exists, will retry"
err="Get \"https://control-plane.minikube.internal:8443/...\":
context deadline exceeded"
→ 워커 노드에서 마스터(192.168.49.2:8443)로 TCP 연결 자체가 타임아웃
Step 3 — 네트워크 계층별 분석 (ARP 기법 활용)
# L2(ARP) 확인 — MAC 주소 해석 상태
$ docker exec minikube-m02 ip neigh show
192.168.49.2 dev eth0 lladdr 1e:d1:ad:93:13:cc REACHABLE ← ✅ L2 정상
# L3(ICMP) 확인 — ping 테스트
$ docker exec minikube-m02 ping -c 2 -W 2 192.168.49.2
2 packets transmitted, 0 received, 100% packet loss ← ❌ L3 실패
# TCP 포트 확인
$ docker exec minikube-m02 nc -zv -w 3 192.168.49.2 8443
nc: connect to 192.168.49.2 port 8443 (tcp) timed out ← ❌ TCP 실패
ARP는 성공(REACHABLE)하지만 ping/TCP가 실패 → L3(방화벽) 계층에서 차단
Step 4 — 호스트 iptables 확인
$ sudo iptables -L FORWARD -n -v | head -5
Chain FORWARD (policy DROP 26121 packets, 1582K bytes)
pkts bytes target prot opt in out source destination
27106 1672K DOCKER-USER all -- * * 0.0.0.0/0 0.0.0.0/0
27108 1672K DOCKER-FORWARD all -- * * 0.0.0.0/0 0.0.0.0/0
- FORWARD 기본 정책이 DROP
bridge-nf-call-iptables=1설정으로 Docker 브리지 내부 트래픽도 호스트 iptables를 거침
$ sudo iptables -L DOCKER-FORWARD -n -v
# minikube 브리지(br-49ca45aa3b7d)에 대한 ACCEPT 규칙 없음
# → 기본 정책 DROP에 의해 모든 컨테이너 간 트래픽 차단
해결
sudo iptables -I DOCKER-FORWARD -i br-49ca45aa3b7d -j ACCEPT
sudo iptables -I DOCKER-FORWARD -o br-49ca45aa3b7d -j ACCEPT
결과
# 네트워크 복구 확인
$ docker exec minikube-m02 ping -c 2 192.168.49.2
2 packets transmitted, 2 received, 0% packet loss ← ✅
$ docker exec minikube-m02 curl -sk https://192.168.49.2:8443/healthz
ok ← ✅
네트워크 문제는 해결되었지만, 노드는 여전히 NotReady 상태 지속.
2차 원인: 워커 노드에 CNI 설정 파일 누락
진단
Step 1 — kubelet 로그 재확인
$ docker exec minikube-m02 journalctl -u kubelet --no-pager -n 10
E0327 "Container runtime network not ready"
networkReady="NetworkReady=false
reason:NetworkPluginNotReady
message:docker: network plugin is not ready: cni config uninitialized"
→ 네트워크 자체는 복구되었지만, CNI 플러그인 설정이 초기화되지 않음
Step 2 — CNI 설정 파일 비교
# 마스터 노드
$ docker exec minikube ls /etc/cni/net.d/
1-k8s.conflist ← ✅ CNI 설정 있음
10-crio-bridge.conflist.disabled.mk_disabled
87-podman-bridge.conflist.mk_disabled
cni.lock
# 워커 노드
$ docker exec minikube-m02 ls /etc/cni/net.d/
10-crio-bridge.conflist.disabled.mk_disabled
87-podman-bridge.conflist.mk_disabled
cni.lock
← ❌ 1-k8s.conflist 없음
→ 마스터에만 1-k8s.conflist가 있고, 워커 노드들에는 존재하지 않음
Step 3 — CNI 설정 내용 확인
$ docker exec minikube cat /etc/cni/net.d/1-k8s.conflist
{
"cniVersion": "0.4.0",
"name": "bridge",
"plugins": [
{
"type": "bridge",
"bridge": "bridge",
"addIf": "true",
"isDefaultGateway": true,
"forceAddress": false,
"ipMasq": true,
"hairpinMode": true,
"ipam": {
"type": "host-local",
"subnet": "10.244.0.0/16"
}
},
{ "type": "portmap", "capabilities": { "portMappings": true } },
{ "type": "firewall" }
]
}
minikube의 기본 CNI는 별도 DaemonSet이 아니라 노드 로컬 파일로 관리되므로, 노드가 재시작되거나 파일이 유실되면 수동 복구가 필요함.
해결
마스터 노드의 CNI 설정 파일을 워커 노드들에 복사:
# minikube-m02에 복사
docker exec minikube cat /etc/cni/net.d/1-k8s.conflist | \
docker exec -i minikube-m02 tee /etc/cni/net.d/1-k8s.conflist > /dev/null
# minikube-m03에 복사
docker exec minikube cat /etc/cni/net.d/1-k8s.conflist | \
docker exec -i minikube-m03 tee /etc/cni/net.d/1-k8s.conflist > /dev/null
결과
kubelet이 CNI 설정을 자동 감지하여 약 15초 후 Ready 전환:
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
minikube Ready control-plane 45h v1.35.1
minikube-m02 Ready <none> 45h v1.35.1 ← ✅
minikube-m03 Ready <none> 45h v1.35.1 ← ✅
원인 요약
이 문제는 2가지 원인이 동시에 존재하는 복합 장애였다.
┌─────────────────────────────────────────────────────────────────┐
│ NotReady 원인 2가지 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 원인 1: 호스트 iptables FORWARD DROP │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ • FORWARD 기본 정책이 DROP │ │
│ │ • bridge-nf-call-iptables=1로 브리지 트래픽도 필터링 │ │
│ │ • minikube 브리지에 대한 ACCEPT 규칙 누락 │ │
│ │ → 워커 ↔ 마스터 간 모든 네트워크 통신 차단 │ │
│ │ → kubelet이 API 서버에 연결 불가 │ │
│ └───────────────────────────────────────────────────────┘ │
│ │ │
│ │ iptables 규칙 추가로 해결 │
│ ▼ │
│ 원인 2: 워커 노드 CNI 설정 파일 누락 │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ • /etc/cni/net.d/1-k8s.conflist 가 워커에 없음 │ │
│ │ • kubelet은 CNI 설정 없으면 NotReady 보고 │ │
│ │ • 네트워크가 복구되어도 CNI 미설정이면 Ready 안 됨 │ │
│ └───────────────────────────────────────────────────────┘ │
│ │ │
│ │ 마스터의 CNI 설정 파일 복사로 해결 │
│ ▼ │
│ ✅ 모든 노드 Ready │
│ │
└─────────────────────────────────────────────────────────────────┘| 순서 | 원인 | 에러 메시지 | 해결 |
|---|---|---|---|
| 1차 | iptables FORWARD DROP | dial tcp 192.168.49.2:8443: i/o timeout |
iptables -I DOCKER-FORWARD 규칙 추가 |
| 2차 | CNI 설정 파일 누락 | cni config uninitialized |
1-k8s.conflist 파일 복사 |
교훈
- NotReady의 원인은 복합적일 수 있다 — 하나를 해결해도 안 되면 다음 계층을 확인해야 한다.
- kubelet 로그를 반복 확인하라 — 1차 원인 해결 후 에러 메시지가 바뀌었다. 처음엔
i/o timeout, 해결 후엔cni config uninitialized. - ARP 분석으로 문제 계층을 분리할 수 있다 — ARP 성공 + ping 실패 → L3(방화벽) 문제임을 빠르게 특정.
- minikube의 기본 CNI는 파일 기반 — Calico/Flannel 같은 DaemonSet 방식이 아니므로, 노드 재시작 시 CNI 설정이 유실될 수 있다.
진단에 사용한 명령어
# ── 노드 상태 확인 ──
kubectl get nodes -o wide
kubectl describe node <노드명>
# ── kubelet 로그 ──
docker exec <노드> journalctl -u kubelet --no-pager -n 30
docker exec <노드> systemctl status kubelet
# ── 네트워크 계층별 진단 ──
docker exec <노드> ip neigh show # L2: ARP 캐시
docker exec <노드> ping -c 2 -W 2 <대상IP> # L3: ICMP
docker exec <노드> nc -zv -w 3 <대상IP> <포트> # L4: TCP 포트
docker exec <노드> curl -sk https://<IP>:<포트>/path # L7: HTTP
# ── 호스트 방화벽 ──
sudo iptables -L FORWARD -n -v
sudo iptables -L DOCKER-FORWARD -n -v
cat /proc/sys/net/bridge/bridge-nf-call-iptables
# ── CNI 설정 ──
docker exec <노드> ls /etc/cni/net.d/
docker exec <노드> cat /etc/cni/net.d/1-k8s.conflist
# ── Docker/minikube ──
docker ps --filter "name=minikube"
minikube status
docker network inspect minikube'K8S' 카테고리의 다른 글
| 사용자 및 CSR 생성 (0) | 2025.12.09 |
|---|---|
| ETCD 기본 동작 원리의 이해 (0) | 2025.12.08 |
| ETCD 명령어 정리 (0) | 2025.12.08 |
| ETCD 백업과 복원 (0) | 2025.12.08 |
| [K8S] Disk io 바운더리로 인해 데이터 처리 속도 저하 (0) | 2025.08.26 |