트러블슈팅 — 증상에서 원인으로
CKA 배점의 30% 가 이 영역이다. 실무 비중도 그쯤 된다. 이 편은 증상별 진단 경로를 정리한다. 앞 편들이 여기서 쓰인다.
1. 무엇을 먼저 보나 — 공통 순서
- ① kubectl get pods -o wide 상태·재시작 횟수·노드
- ② kubectl describe pod <name> Events 를 '아래에서 위로' 읽는다
- ③ kubectl logs <name> [-c container] [--previous]
- ④ kubectl get events --sort-by=.lastTimestamp -A | tail -30
describe 의 Events 가 가장 정보가 많다 스케줄링 실패 이유 · 이미지 pull 실패 · 프로브 실패가 문장으로 나온다
--previous 는 '직전에 죽은 컨테이너' 의 로그다 CrashLoopBackOff 에서는 현재 컨테이너 로그가 비어 있으므로 이것을 본다
2. 파드 상태별 진단
Pending — 아직 노드에 배치되지 않았다
kubectl describe pod web-xxx | grep -A10 Events
# 0/5 nodes are available: 3 Insufficient cpu,
# 2 node(s) had untolerated taint {node-role.kubernetes.io/control-plane}
| 이벤트 문구 | 원인 |
|---|---|
Insufficient cpu/memory | requests 합이 노드 여유를 넘는다 |
untolerated taint | 테인트를 견디지 못한다 |
didn't match Pod's node affinity/selector | 라벨 조건 불일치 |
didn't match pod anti-affinity rules | 같은 노드 회피 규칙 |
pod has unbound immediate PersistentVolumeClaims | PVC 가 아직 바인딩 안 됨 |
node(s) exceed max volume count | 노드의 볼륨 부착 한도 초과 |
자원 부족이면 실제 여유를 확인한다
- kubectl describe node <name> | grep -A8 'Allocated resources'
- 스케줄러가 보는 것은 requests 합이지 실제 사용량이 아니다
ImagePullBackOff · ErrImagePull
-
① 이미지 이름·태그 오타 — 가장 흔하다
-
② 프라이빗 레지스트리 인증 — imagePullSecrets 누락
-
③ 레지스트리 접근 불가 — 네트워크·프록시
-
④ 아키텍처 불일치 — exec format error 는 실행 단계에서 난다
-
⑤ rate limit — Docker Hub 익명 pull 제한
-
확인
- kubectl describe pod | grep -A5 Events
- "pull access denied" 인가 "not found" 인가로 갈린다
spec:
imagePullSecrets:
- name: regcred
CrashLoopBackOff — 뜨자마자 죽는다
BackOff 는 '재시작을 점점 늦추고 있다' 는 뜻이다 (10초 → 20 → 40 … 최대 5분) 원인은 컨테이너가 계속 죽는 것이지 쿠버네티스가 아니다
- kubectl logs <pod> --previous — ← 여기에 답이 있다
- kubectl describe pod <pod> — ← Last State 의 Exit Code
Exit Code
0 정상 종료했는데 재시작됐다 → 커맨드가 바로 끝나는 것 (foreground 로 돌아야 한다)
1 애플리케이션 예외
137 OOMKilled 또는 강제 종료
127 명령을 못 찾음 (경로 · libc 불일치)
126 실행 권한 없음
로그가 아예 비어 있다면
- 애플리케이션이 시작조차 못 했다 (커맨드 오류 · 파일 없음)
- 표준 출력이 아니라 파일에 쓴다
- kubectl describe 의 Exit Code 로 판단한다
Running 인데 트래픽이 안 온다
Ready 가 0/1 인지 확인한다
kubectl get pods
NAME READY STATUS RESTARTS
web-xxx 0/1 Running 0 ← readiness 실패
kubectl describe pod | grep -A3 Readiness
-
Readiness probe failed: HTTP probe failed with statuscode: 500
-
엔드포인트에 안 들어간다 → 서비스가 트래픽을 안 보낸다
Terminating 에서 안 사라진다
원인
- terminationGracePeriodSeconds 를 기다리는 중 (정상)
- PID 1 이 SIGTERM 을 무시한다 → 유예 시간을 꽉 채운다
- preStop 훅이 안 끝난다
- finalizer 가 남아 있다 ← 이건 영원히 안 사라진다
확인 kubectl get pod <name> -o jsonpath='{.metadata.finalizers}' kubectl get pod <name> -o jsonpath='{.metadata.deletionTimestamp}'
finalizer 는 '지우기 전에 할 일' 을 등록한 것이다 그 컨트롤러가 죽었으면 아무도 지워 주지 않는다
- 원인을 확인한 뒤 마지막 수단으로 finalizer 를 제거한다
--force --grace-period=0을 습관적으로 쓰지 않는다. StatefulSet 에서는 같은 이름의 파드가 두 개 도는 상태를 만들 수 있고, 볼륨 손상으로 이어진다.
3. 노드 문제
kubectl get nodes
kubectl describe node node1 | grep -A15 Conditions
Conditions
Ready False 면 kubelet 이 보고를 못 하고 있다
MemoryPressure 메모리 부족 → 파드 퇴출이 시작된다
DiskPressure 디스크 부족 → 이미지 GC · 파드 퇴출
PIDPressure PID 고갈
NotReady 의 흔한 원인
· kubelet 이 죽었다 systemctl status kubelet
· CNI 플러그인이 준비 안 됨 노드가 NotReady 로 고정된다
· 디스크가 찼다 이미지·로그가 원인인 경우가 많다
· 인증서 만료 kubelet 이 API 서버와 못 붙는다
노드에 직접 들어가 볼 때
journalctl -u kubelet -f --since '10 min ago'
crictl ps -a # 컨테이너 목록 (docker ps 대신)
crictl logs <container-id>
df -h /var/lib/kubelet /var/lib/containerd
crictl 을 쓰는 이유 — 런타임이 containerd 라 docker 명령이 없다
디스크 압박이 만드는 연쇄
디스크가 차면
원인 대부분이 로그다
- 컨테이너 로그 로테이션 미설정
- emptyDir 에 큰 파일을 쌓는 워크로드
- 안 쓰는 이미지 누적
4. 컨트롤 플레인 문제
kubectl get pods -n kube-system
kubectl -n kube-system logs kube-apiserver-master1
kubeadm 클러스터라면 컨트롤 플레인이 static pod 로 뜬다
- /etc/kubernetes/manifests/*.yaml
- kubelet 이 이 디렉터리를 감시해 자동으로 띄운다
- API 서버가 죽어 kubectl 이 안 되면 이 파일과 컨테이너를 직접 본다
crictl ps -a | grep apiserver
crictl logs <id>
kubectl 이 아예 안 될 때 순서
① API 서버 컨테이너가 떠 있는가 crictl ps
② 매니페스트에 문법 오류가 없는가 최근 수정 시각 확인
③ etcd 가 살아 있는가 crictl logs etcd
④ 인증서가 만료되지 않았는가
kubeadm certs check-expiration
⑤ kubeconfig 가 맞는가 kubectl config view --minify
etcd
etcd 가 곧 클러스터의 진실이다. 여기가 깨지면 전부 잃는다
- 정기 스냅숏 백업이 1순위 운영 과제다
- ETCDCTL_API=3 etcdctl snapshot save snap.db \
- --endpoints=https://127.0.0.1:2379 \
- --cacert=... --cert=... --key=...
- 홀수 노드(3·5)로 구성한다 — Raft 합의 정족수 때문이다
- 3대면 1대까지, 5대면 2대까지 견딘다
- 디스크 지연에 매우 민감하다 → SSD 필수
- 느려지면 리더 선출이 반복되고 클러스터 전체가 불안정해진다
5. 서비스·네트워킹 진단
-
① 엔드포인트가 비어 있다 ← 가장 흔하다
- kubectl get endpointslices -l kubernetes.io/service-name=web
- 원인: 라벨 불일치 또는 readiness 실패
-
② 이름이 안 풀린다
- kubectl -n kube-system get pods -l k8s-app=kube-dns
- CoreDNS 가 죽었거나 CrashLoop 인지 본다
-
③ 포트가 안 맞는다
-
④ NetworkPolicy 가 막는다
- kubectl get netpol -A
- 특히 egress 정책에서 DNS(53/UDP)를 안 열어 전부 깨지는 사례
-
⑤ 밖에서 안 들어온다
- 인그레스/게이트웨이 컨트롤러 파드 로그
- LoadBalancer 서비스의 EXTERNAL-IP 가 <pending> 이면
- 클라우드 컨트롤러가 없거나 권한이 없는 것이다
# 임시 진단 파드
kubectl run tmp --rm -it --image=nicolaka/netshoot -- bash
nslookup web
curl -v http://web
nc -zv web 80
셸이 없는 이미지는 임시 컨테이너로 붙는다
- kubectl debug -it pod/web --image=nicolaka/netshoot --target=app
- 같은 네임스페이스를 공유해 그 파드의 네트워크에서 진단한다
6. 자원과 성능
kubectl top nodes
kubectl top pods -A --sort-by=memory
metrics-server 가 없으면 top 이 동작하지 않는다
- error: Metrics API not available
- 설치돼 있는지부터 확인한다. HPA 도 같이 안 된다
CPU 는 여유로운데 느리다 → 스로틀링을 의심한다 파드 안에서
limits 가 낮으면 사용률은 낮게 보이면서 지연만 튄다
7. 이벤트와 로그를 모으는 법
# 최근 이벤트를 시간순으로
kubectl get events -A --sort-by=.lastTimestamp | tail -40
# 특정 객체의 이벤트만
kubectl get events --field-selector involvedObject.name=web-xxx
# 여러 파드의 로그를 한꺼번에 (라벨로)
kubectl logs -l app=web --tail=100 --prefix -f
# 이전 컨테이너
kubectl logs web-xxx -c app --previous
이벤트는 기본적으로 1시간만 보관된다
- 지난 장애를 조사하려면 로그·이벤트를 외부로 수집해 둬야 한다
- "이벤트가 없어서 원인을 못 찾았다" 는 수집 체계의 문제다
8. 증상 → 확인 지점 표
| 증상 | 먼저 볼 것 |
|---|---|
Pending | describe pod 의 FailedScheduling 문구 |
ImagePullBackOff | 이미지 이름·태그, imagePullSecrets |
CrashLoopBackOff | logs --previous, Exit Code |
0/1 Running | readiness 프로브 실패 내용 |
Terminating 고착 | finalizer, PID 1 시그널 처리 |
| 서비스 접속 불가 | EndpointSlice 가 비었는지 |
| 이름 해석 실패 | CoreDNS 파드 상태, egress 정책의 53/UDP |
노드 NotReady | kubelet 로그, 디스크, CNI |
kubectl 무응답 | static pod, etcd, 인증서 만료 |
| 롤아웃 멈춤 | 새 파드의 Ready 여부, progressDeadlineSeconds |
drain 멈춤 | PDB 의 minAvailable 과 replicas |
| 볼륨 Multi-Attach | RWO 볼륨을 다른 노드에서 붙이려는 것 |
top 실패 | metrics-server 설치 여부 |
9. 조사할 때의 원칙
- ① 이벤트를 먼저 읽는다 — 대부분 답이 문장으로 적혀 있다
- ② 계층을 좁힌다 — 파드 → 노드 → 컨트롤 플레인 순으로
- ③ 최근 변경을 확인한다
- kubectl rollout history · git log · 클러스터 업그레이드 이력
- ④ 한 번에 하나만 바꾼다 — 여러 개를 동시에 고치면 원인을 못 배운다
- ⑤ 재현 조건을 적어 둔다 — 다음에 같은 증상이 오면 시간이 절반으로 준다
한눈에 정리
- 공통 순서 — get → describe(Events) → logs --previous → get events
- Pending — 스케줄링 실패. 이유가 문장으로 나온다. requests 합을 본다
- ImagePull — 이름·태그 · imagePullSecrets · rate limit
- CrashLoop — --previous 로그와 Exit Code (0 이면 커맨드가 바로 끝난 것)
- 0/1 Running — readiness 실패. 엔드포인트에 안 들어간다
- Terminating — 유예 시간 · PID 1 · finalizer. force 는 마지막 수단
- 노드 — kubelet 로그 · 디스크 · CNI · 인증서
- 컨트롤 플레인 — static pod 매니페스트 · crictl · etcd · kubeadm certs
- 네트워크 — EndpointSlice 부터. egress 정책의 DNS 를 잊지 않는다
- etcd — 백업이 1순위. 홀수 노드. SSD 필수
- 이벤트 — 기본 1시간 보관 — 외부 수집이 없으면 지난 장애를 못 본다
출처 — Kubernetes Docs: "Troubleshooting Applications" · "Troubleshooting Clusters" · "Debug Running Pods" · "Debug Services" · "Pod Lifecycle" · "Operating etcd clusters for Kubernetes" · CKA Curriculum v1.35 (Troubleshooting 30%)