1. 현상 (Issue)

  • Windows(VM 호스트)가 알 수 없는 이유로 블루스크린으로 강제 종료되어 Vmware로 띄운 kubernetes cluster 가 강제 shutdown 되었다.
  • kubernetes cluster 내 노드들을 기동해도 컨트롤 플래인이 계속 비정상 동작함.

2. 원인 (Root Cause)

분석과정

  1. etcd 컨테이너 id를 확인한다.
# containerd 가 관리하는 컨테이너 목록에서 etcd의 id를 확인해 조회한다.
ETCD_ID=$(sudo crictl --runtime-endpoint unix:///run/containerd/containerd.sock ps -a --name etcd -q | head -1) 
sudo crictl --runtime-endpoint unix:///run/containerd/containerd.sock logs "$ETCD_ID" | tail -200
  1. 로그를 확인한다.

panic: freepages: failed to get all reachable pages (page 1209: multiple references (stack: [5946 6314 6313 1209]))

Screenshot 2026-04-07 at 12.33.54.png

결론

  • 비정상 종료로 인해 etcd가 쓰는 backend DB 손상으로 etcd 데이터 파일이 깨짐 (/var/lib/etcd의 기존 member 데이터를 읽다가 panic)
  • kubeadm은 local etcd를 static Pod 로 띄우지만 etcd가 계속 죽고 → kube-apiserver도 같이 못 뜨는 상태가 됨

3. 해결 방법(Solution)

1) etcd snapshot 백업이 있는 경우

공식 절차는 API 서버를 먼저 멈추고 → etcd를 복원하고 → API 서버를 다시 올리는 순서이다.

# 0. 복구용 디렉터리
sudo mkdir -p /root/recovery
 
# 1. kube-apiserver 정지
sudo mv /etc/kubernetes/manifests/kube-apiserver.yaml /root/recovery/
sleep 20
sudo crictl ps | grep kube-apiserver
 
# 2. 새 data-dir로 restore
sudo rm -rf /var/lib/etcd-restored
sudo etcdutl snapshot restore /backup/etcd-snapshot.db --data-dir /var/lib/etcd-restored
 
# 3. etcd manifest 백업 (반드시 manifests 바깥)
sudo cp /etc/kubernetes/manifests/etcd.yaml /root/recovery/etcd.yaml.bak
 
# 4. /etc/kubernetes/manifests/etcd.yaml 수정
#    - --data-dir=/var/lib/etcd-restored
#    - volumes.hostPath.path: /var/lib/etcd-restored
sudo vi /etc/kubernetes/manifests/etcd.yaml
 
# 5. kubelet 재시작
sudo systemctl restart kubelet
 
# 6. etcd health 확인
ETCDCTL_API=3 sudo 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
 
# 7. kube-apiserver 다시 올리기
sudo mv /root/recovery/kube-apiserver.yaml /etc/kubernetes/manifests/
sudo systemctl restart kubelet

2) etcd snapshot 백업이 없는 경우 재구축

Snapshot 백업이 없다면 복구보다 ‘재구축’이 더 현실적인 방법이다. 이 경우는 솔직히 말해 현재 cluster state를 안전하게 되살릴 보장된 방법이 없기 때문이다. 단일 control plane에서 local etcd가 깨졌다면, 중요 워크로드가 아니라면 master 재구축 후 worker 재조인이 가장 빠른 경우가 많다고 가이드하고 있다. 이 경우 kubeadm reset 후 kubeadm 을 통해 다시 복구를 진행한다.

# 정말 재구축으로 갈 때만
sudo cp -a /etc/kubernetes /root/recovery/etc-kubernetes-$(date +%F-%H%M)
sudo cp -a /var/lib/etcd /root/recovery/etcd-raw-$(date +%F-%H%M)
 
sudo kubeadm reset -f
# 이후 kubeadm init 다시 수행
# 새 join token 생성 후 worker 재조인
 
# 이 경로는 기존 Kubernetes 오브젝트 상태를 잃는다.

4. 재발 방지 및 개선

이런 뭣같은 상황을 방지하려면 어떻게 해야 할까? 크게 아키텍처 개선 방량, snapshot 자동화, 그리고 부수적이지만 control plane의 자원 보호가 있다.

1. 아키텍처 개선

공식 문서도 single-node etcd는 테스트용이라고 보고, etcd는 홀수 개 멤버, 가능하면 멀티노드, 그리고 전용 또는 격리된 환경에서 운영하라고 권장하고 있다. (물론 집에서 가지고 노는 나는 적용하기 힘든 구조이다.) 또한 리소스 부족이나 디스크 I/O 경쟁만으로도 heartbeat timeout이 나고, 그 상태면 etcd leader가 불안정해져 Kubernetes가 변경을 못 받는다. 지금처럼 모든 VM이 한 대의 Windows 호스트에 매달린 구조는 host failure 한 번에 전부 영향받는다.

kubeadm은 기본적으로 local etcd를 쓰지만, external etcd 토폴로지도 공식 지원한다.

2. etcd snapshot을 자동화

etcd 안에는 Kubernetes 오브젝트 상태가 모두 들어 있으므로, 공식 문서도 주기적 백업을 강하게 권장한다. snapshot은 live member에서 뜰 수 있고, etcdutl snapshot status로 검증할 수 있다.

다음과 같이 적용한다.

  • 주기적 snapshot: 예를 들면 30분~1시간 간격
  • 보관 위치 분리: master VM 내부가 아니라 다른 VM / NAS / 다른 물리 장비

백업 예시는 아래 형태이다.

ETCDCTL_API=3 sudo 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 \
  snapshot save /backup/etcd/etcd-$(date +%F-%H%M).db
 
sudo etcdutl --write-out=table snapshot status /backup/etcd/etcd-YYYY-MM-DD-HHMM.db

3. control plane 자원 보호

Kubernetes는 system daemons와 pod가 자원을 서로 갉아먹지 않도록 Node Allocatable, kubeReserved, systemReserved 설정을 권장한다. 특히 한 대의 master VM에 여러 부하가 몰리면 kubelet, container runtime, etcd가 같이 흔들릴 수 있어 master VM 에는 하기 작업을 권장한다.

  • 일반 워크로드 스케줄링 억제
  • CPU/메모리 여유 확보
  • 디스크 여유 및 I/O 병목 완화
  • swap 비활성, 디스크 fullness 감시