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.
같은 접속 장애라도 어느 층에서 막혔느냐에 따라 먼저 볼 곳이 다르다. 위에서 아래로 한 층씩 내려가며 이야기가 끊기는 곳을 찾는다.
- Pending: 스케줄러의 filter 단계노드가 모두 탈락해 배치되지 못한 상태다. describe 의 Events, 자원 요청, 테인트, nodeSelector, PVC 바인딩을 먼저 본다. Insufficient cpu 같은 이벤트가 탈락 사유 집계표다.
- ContainerCreating, ImagePullBackOff, CrashLoopBackOff: 노드 위배치는 끝났고 노드에서 막힌 상태다. 볼륨 마운트, CNI, 이미지 태그와 레지스트리 인증, 컨테이너가 계속 죽는 이유(로그, 설정, 프로브)를 본다.
- Running 인데 트래픽이 없다: 서비스 층파드는 멀쩡하다. 셀렉터, Ready, 포트를 본다.
여기서 구분할 것 Pending 의 단골은 자원 요청의 단위 실수다. cpu: 500 은 500 밀리코어가 아니라 500 코어이고, 500m 이라고 써야 한다. 이때도 이벤트는 Insufficient cpu 라고만 말한다.
잠깐, 예측해 보세요 파드가 ContainerCreating 에 오래 머문다. 스케줄러 이벤트의 탈락 사유부터 파고들면 될까?
설명 확인 · 채점 없는 자가 점검
아니다. ContainerCreating 은 이미 노드에 배치된 뒤의 상태다. 스케줄러의 filter 는 지나갔으니 볼륨 마운트, CNI, 이미지 같은 kubelet 쪽 준비를 본다.
어떻게 동작하나
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
복구의 원칙은 셋입니다.
- 기존 data-dir 을 덮어쓰지 않는다.
--data-dir로 새 경로에 풀고, 설정을 그 경로로 바꿔 기동합니다. - 복구 중 apiserver 를 내린다. 살아 있는 apiserver 가 계속 쓰면 복구본과 어긋납니다.
--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 한 번은 세 벌 모두에 즉시 반영됩니다.
본문의 GPU Operator 사례다. 컨테이너가 실패한 것이 아니라 컨테이너를 담을 샌드박스를 만드는 단계에서 막혔다.
- 이름표는 쿠버네티스 안에 있다RuntimeClass nvidia 가 handler: nvidia 로 등록돼 있고 RuntimeClass 목록에도 보인다. 이 오브젝트는 이름표일 뿐이다.
- 실체는 노드의 런타임 설정에 있어야 한다파드를 시작하려고 노드의 컨테이너 런타임이 샌드박스를 만들 때, handler 이름에 해당하는 설정을 노드의 런타임 설정에서 찾는다.
- 없으면 컨테이너가 뜨기 전에 멈춘다Failed to create pod sandbox: no runtime for 'nvidia' is configured. 컨테이너 안의 실패가 아니라 샌드박스 단계의 실패다.
여기서 구분할 것 설정이 한 파일에만 있다고 믿지 않는다. 본문에서는 config.toml 의 grep 결과가 0 이었지만 설정은 conf.d 아래 drop-in 파일에 들어 있었다.
잠깐, 예측해 보세요 노드의 config.toml 에서 nvidia 를 grep 했더니 0건이다. 이 노드에는 nvidia 런타임 설정이 없다고 결론 내려도 될까?
설명 확인 · 채점 없는 자가 점검
안 된다. 설정이 conf.d 아래 drop-in 파일에 있을 수 있다. 0건은 그 파일 하나에 없다는 것만 말하므로 drop-in 까지 함께 확인한다.
다음 실습에서 할 것
첫 실습에서 etcd 스냅샷을 실제로 뜨고, 백업 전후 리소스를 대조해 스냅샷에 담기지 않은 것이 무엇인지 눈으로 확인하고, 복구 계획을 문서로 남깁니다. 두 번째 실습에서는 다섯 가지 고장을 직접 만들고 직접 고칩니다. 잘못된 이미지 태그, 자원 요청 단위 실수, 톨러레이션 누락, 라벨 불일치, ResourceQuota 초과, PDB 로 막힌 drain. 고치기 전에 증상을 파일로 남기는 것이 채점 대상입니다.