KubeEdge 엣지 노드 장애 대응 가이드

KubeEdge 엣지 노드 장애 대응 가이드

1. 배경

최근 프로젝트에서 프로그램을 운영하는 과정에서 ‘kubectl get nodes’에서 ‘NotReady’ 상태이거나, ‘kubectl exec’이 먹통이 되거나, Pod가 갑자기 Evicted 상태로 바뀌는 상황 등 다양한 이슈를 접하게 되었습니다. 이에 따라 실제 운영 중 마주친 네 가지 장애 유형에 따라 사례 중심으로 다음과 같이 KubeEdge 환경에서의 이슈 대응 가이드를 작성합니다.

2. 들어가며

Kubernetes 기반의 일반적인 클러스터와 달리, KubeEdge는 클라우드와 엣지 노드 사이에 CloudCore ↔ EdgeCore 터널이 추가로 존재합니다. 그 덕분에 네트워크가 불안정한 현장 환경에서도 엣지 노드를 운용할 수 있지만, 동시에 장애 원인도 한층 더 복잡해집니다.

3. 장애유형

장애 유형 1

kubectl exec 실패 — "unexpected EOF"

(1) 증상

$ kubectl exec -it my-app-6jf2m -n iot-edge -- /bin/bash
error: Internal error occurred: error sending request:
Post "https://192.168.1.3:10351/exec/...": unexpected EOF

‘kubectl exec’를 실행했는데 연결 자체가 끊기면서 아무것도 안 되는 상황입니다. 같은 노드에 있는 다른 Pod는 Running 상태로 보이는데 exec만 안 되는 경우도 있습니다.

(2) 원인 분석

KubeEdge에서 ‘kubectl exec’가 동작하려면 EdgeStream 기능이 활성화되어 있어야 합니다. 이 기능이 꺼져 있으면 API Server → CloudCore → EdgeCore로 이어지는 exec 터널 자체가 성립되지 않습니다.

일반 Kubernetes는 API Server가 kubelet의 10250 포트에 직접 접근해서 exec를 처리하지만, KubeEdge는 이 경로가 없습니다. EdgeStream 없이는 exec, logs, port-forward 모두 동작하지 않습니다.

[kubectl exec flow in KubeEdge]
API Server
    ↓
CloudCore (streamPort: 10003)
    ↓ ← without this tunnel, unexpected EOF occurs
EdgeCore (edgeStream.server)
    ↓
컨테이너

(3) 해결 방법

CloudCore 설정 확인 및 활성화:

# cloudcore.yaml
cloudStream:
  enable: true
  streamPort: 10003
  tlsTunnelCAFile: /etc/kubeedge/ca/rootCA.crt
  tlsTunnelCertFile: /etc/kubeedge/certs/server.crt
  tlsTunnelPrivateKeyFile: /etc/kubeedge/certs/server.key

EdgeCore 설정 확인 및 활성화:

# edgecore.yaml
edgeStream:
  enable: true
  handshakeTimeout: 30
  readDeadline: 15
  server: <CloudCore_IP>:10004
  tlsTunnelCAFile: /etc/kubeedge/ca/rootCA.crt
  tlsTunnelCertFile: /etc/kubeedge/certs/server.crt
  tlsTunnelPrivateKeyFile: /etc/kubeedge/certs/server.key
  writeDeadline: 15

설정 변경 후에는 양쪽 모두 재시작이 필요합니다:

# On the master node where CloudCore is running
systemctl restart cloudcore.

# On the edge node
systemctl restart edgecore

장애 유형 2

DiskPressure로 인한 Pod Eviction

(1) 증상

$ kubectl get po -A -o wide | grep <NodeName>
iot-edge chronograf-xxxxx 0/1 Evicted 0 2m
iot-edge influxdb-xxxxx 0/1 Evicted 0 9m
iot-edge my-app-xxxxx 0/1 Evicted 0 14m

특정 노드에 있는 Pod들이 일괄적으로 Evicted 상태가 됩니다. 한두 개가 아니라 해당 노드의 모든 Pod가 퇴출되는 경우 노드 레벨의 리소스 문제를 의심해야 합니다.

(2) 원인 분석

Eviction은 kubelet(KubeEdge에서는 edgecore)이 노드 리소스 부족을 감지했을 때 Pod를 강제 종료하는 메커니즘입니다. ‘kubectl describe node’에서 직접 원인을 확인할 수 있습니다:

kubectl describe node <NodeName> | grep -A5 "Conditions"

실제 사례에서는 아래와 같은 메시지가 나타났습니다:

DiskPressure True ...
Message: The node was low on resource: ephemeral-storage.
  Threshold: 37556267476 (~35GB)
  Available: 36674584Ki (~34.9GB)

가용 디스크가 임계값 아래로 떨어지는 순간 kubelet이 해당 노드의 Pod를 강제 퇴출합니다

디스크 사용량 확인
엣지 노드에 SSH 접속하여 어디서 디스크를 잡아먹고 있는지 확인합니다:

df -h
du -sh /* 2>/dev/null | sort -rh | head -15

실제 사례에서는 특정 애플리케이션의 로그 디렉토리 하나가 전체 디스크의 89%를 차지하고 있었습니다.

$ du -sh /var/app/logs/*/
166G /var/app/logs/device-001.ASSET/

(3) 해결 방법

오래된 로그 정리 (삭제 전 보존 필요 여부 확인 필수!)

# Delete files older than N days
find /var/app/logs -mtime +7 -type f -delete

사용하지 않는 컨테이너 이미지 정리

crictl rmi --prune

edgecore 재시작으로 DiskPressure 재평가

systemctl restart edgecore

Evicted Pod 정리 및 재배포

# On the master node
kubectl get pods -A | grep Evicted | awk '{print "kubectl delete pod " $2 " -n " $1}' | sh

장애 유형 3

Init Container ImagePullBackOff — DNS 장애

(1) 증상

$ kubectl describe pod my-app-qn6r8 -n iot-edge
Init Containers:
  init-snapshot:
    State: Waiting
      Reason: ImagePullBackOff
    Image: registry.company.internal:5000/infra/busybox:latest

메인 컨테이너가 아니라 Init Container에서 이미지 Pull이 실패하는 경우입니다. Init Container가 실패하면 메인 컨테이너는 시작조차 되지 않습니다.

이 경우 메인 컨테이너의 이전 종료 상태를 보면 아래와 같이 나타납니다:

Last State: Terminated
  Reason: Unknown
  Exit Code: 255

Exit Code 255는 컨테이너 런타임이 프로세스를 강제 종료했다는 의미로, 대부분 노드 수준의 문제(이전 DiskPressure, 네트워크 단절 등)로 인한 결과입니다.

(2) 원인 분석 — 사설 레지스트리 DNS 장애

ImagePullBackOff의 원인은 다양하지만, 사내 사설 레지스트리를 사용하는 IoT 환경에서는 DNS 장애가 특히 자주 발생합니다.

확인 방법:

# On the edge node
nslookup registry.company.internal

DNS SERVFAIL이 나타나면 도메인 자체를 해석하지 못하는 상태입니다.

server can't find registry.company.internal: SERVFAIL

(3) 해결 방법

빠른 임시 해결 — /etc/hosts 등록
다른 정상 노드에서 IP를 확인한 후 ‘/etc/hosts’에 직접 등록합니다:

# Resolve IP from a healthy node
nslookup registry.company.internal
# Add entry to /etc/hosts on the affected node
echo "<IP> registry.company.internal" >> /etc/hosts
# Test image pull
crictl pull registry.company.internal:5000/infra/busybox:latest

DNS 근본 원인 해결

임시 조치 후 ‘systemd-resolved’ 상태를 확인하고 재시작합니다:

systemctl status systemd-resolved
systemctl restart systemd-resolved

# Verify DNS resolution is restored
nslookup registry.company.internal

DNS 복구 후 Pod 재시작

# Force delete and recreate the Pod from the master node
kubectl delete pod my-app-qn6r8 -n iot-edge --force --grace-period=0

# Watch Pod status
kubectl get pod -n iot-edge -w | grep my-app

4. 정리

- ‘kubectl exec’가 안 된다면 kubelet 문제가 아니라 EdgeStream 설정 문제일 수 있다.

- Pod가 대량 Evicted된다면 노드의 디스크 사용량부터 확인한다.

- ImagePullBackOff는 이미지 문제가 아니라 DNS 장애일 수 있다.

엣지 환경은 데이터센터와 달리 현장 네트워크 불안정, 제한된 디스크 용량, 다수의 분산된 노드 관리라는 복합적인 어려움이 있습니다. 위 체크리스트를 팀 내에서 공유해 두면 누가 온콜을 받더라도 대응 시간을 줄일 수 있습니다.

Jsia

Site footer