CKA — 쿠버네티스 관리자 · 트러블슈팅과 etcd 백업·복구 · 이론
Ready 와 동작한다는 다른 명제다
한 줄 요약
트러블슈팅은 CKA 배점의 30%, 다섯 도메인 중 가장 큽니다. 그리고 요령은 하나입니다. 상태를 위에서 아래로 한 층씩 내려가며 확인하고, 어느 층에서 이야기가 끊기는지 찾는 것. 파드 상태가 진단의 출발점입니다.
왜 이게 필요했나
증상만 보고 원인을 맞히려 들면 틀립니다. 같은 "접속이 안 된다" 가 셀렉터 오타일 수도, 스케줄 실패일 수도, PVC 미바인딩일 수도 있습니다. 그래서 순서를 정해 둡니다.
| 파드 상태 | 어디서 막혔나 | 먼저 볼 것 |
| --- | --- | --- |
| Pending | 스케줄러 filter 단계 | describe 의 Events, 자원 요청, 테인트, nodeSelector, PVC 바인딩 |
| ContainerCreating | kubelet 의 볼륨/네트워크 준비 | 볼륨 마운트, CNI, 이미지 |
| ImagePullBackOff | 이미지 가져오기 | 태그 오타, 레지스트리 인증 |
| CrashLoopBackOff | 컨테이너가 계속 죽음 | 로그, 설정, 프로브 |
| Running 인데 트래픽 없음 | 서비스 층 | 셀렉터, Ready, 포트 |
이 표에서 가장 자주 나오는 것이 Pending 이고, 그 안에서 가장 자주 나오는 것이 자원 요청 단위 실수입니다. cpu: 500 은 500 밀리코어가 아니라 500 코어입니다. 500m 이라고 써야 합니다. memory: 2000Gi 도 2000Mi 의 오타이기 쉽습니다. 스케줄러 이벤트는 이렇게 말합니다.
0/3 nodes are available: 3 Insufficient cpu.어떻게 동작하나
etcd 백업은 컨트롤 플레인 DR 의 마지막 보험입니다.
ETCDCTL_API=3 etcdctl snapshot save /backup/etcd-$(date +%Y%m%d%H%M).db --endpoints=https://127.0.0.1:2379 --cacert=... --cert=... --key=...etcdctl snapshot status /backup/etcd-*.db --write-out=table복구의 원칙은 셋입니다.
1. 기존 data-dir 을 덮어쓰지 않는다. --data-dir 로 새 경로에 풀고, 설정을 그 경로로 바꿔 기동합니다.
2. 복구 중 apiserver 를 내린다. 살아 있는 apiserver 가 계속 쓰면 복구본과 어긋납니다.
3. --initial-cluster 와 peer URL(2380)을 정확히 맞춘다. 쿼럼을 잃은 상황이라면 우선 단일 노드로 복구한 뒤 멤버를 하나씩 다시 붙입니다.
그리고 반드시 짚어야 할 것: 쿼럼은 장애 대비이지 실수 대비가 아닙니다. 잘못된 삭제는 즉시 모든 멤버에 복제됩니다. 시점 복구를 할 수 있는 것은 스냅샷뿐입니다. 또한 etcd 스냅샷에는 PV 안의 데이터가 없습니다. 애플리케이션 데이터는 Velero 같은 별도 수단이 필요합니다.
현장에서 만나는 모습
사례 1 — 전부 Ready 인데 안 된다. 홈랩에 KubeVirt 를 올렸을 때 컴포넌트 상태는 전부 AllComponentsReady 였는데 VM 은 뜨지 않았습니다. virt-launcher 파드 명세를 뜯어보니 init 컨테이너가 실행할 바이너리를 담은 볼륨 마운트가 누락돼 있었습니다. 저자 표현을 그대로 옮기면 "상태가 Ready 와 실제로 동작한다는 다른 명제" 라는 걸 그 클러스터에서만 세 번째 확인한 순간이었습니다. 상위 status 필드는 그 컨트롤러가 아는 범위만 말합니다.
사례 2 — 바이너리가 있다와 설정이 됐다는 다르다. GPU Operator 를 설치하면서 노드에 nvidia-ctk 바이너리가 있는 것을 보고 toolkit 설치를 껐습니다. 결과는 이랬습니다.
Failed to create pod sandbox: rpc error: code = Unknown desc = failed to get sandbox runtime: no runtime for "nvidia" is configured파드가 시작조차 못 했습니다. 컨테이너 안에서 뭔가 실패한 게 아니라 샌드박스를 만드는 단계에서 막힌 것입니다. 확인해 보니 kubectl get runtimeclass 에는 nvidia 가 있는데, 노드에서는 grep -c nvidia /etc/containerd/config.toml 이 0 이었습니다. RuntimeClass 는 handler: nvidia 라는 이름표일 뿐이고 실체는 노드의 런타임 설정에 있어야 하는데, 재구축하며 런타임을 cri-dockerd 에서 containerd 로 바꾸면서 그 설정이 사라진 것입니다. 호스트의 바이너리는 과거의 잔재였습니다.
고친 뒤에도 config.toml 은 여전히 0 이었습니다. 설정은 /etc/containerd/conf.d/99-nvidia.toml 이라는 drop-in 파일에 들어가 있었습니다. grep 한 곳이 전부라고 믿은 것이 두 번째 함정이었습니다.
사례 3 — 쿼럼이 있다고 백업이 필요 없는 게 아니다. 컨트롤 플레인을 3대로 늘려 etcd 멤버 3개를 확보한 뒤에도 남은 과제 목록의 두 번째 줄은 이것이었습니다. "etcd 정기 스냅샷 — 쿼럼은 장애 대비이지 실수(오삭제) 대비가 아님." 데이터가 세 벌 복제돼 있어도 잘못된 kubectl delete 한 번은 세 벌 모두에 즉시 반영됩니다.
다음 실습에서 할 것
첫 실습에서 etcd 스냅샷을 실제로 뜨고, 백업 전후 리소스를 대조해 스냅샷에 담기지 않은 것이 무엇인지 눈으로 확인하고, 복구 계획을 문서로 남깁니다. 두 번째 실습에서는 다섯 가지 고장을 직접 만들고 직접 고칩니다. 잘못된 이미지 태그, 자원 요청 단위 실수, 톨러레이션 누락, 라벨 불일치, ResourceQuota 초과, PDB 로 막힌 drain. 고치기 전에 증상을 파일로 남기는 것이 채점 대상입니다.