쿠버네티스 학습 노트 목차

트러블슈팅 — 증상에서 원인으로

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/memoryrequests 합이 노드 여유를 넘는다
untolerated taint테인트를 견디지 못한다
didn't match Pod's node affinity/selector라벨 조건 불일치
didn't match pod anti-affinity rules같은 노드 회피 규칙
pod has unbound immediate PersistentVolumeClaimsPVC 가 아직 바인딩 안 됨
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 명령이 없다

디스크 압박이 만드는 연쇄

디스크가 차면

  • kubelet이미지 GC 를 돌린다
  • 그래도 부족하면 파드를 퇴출한다 (BestEffort 부터)
  • 새 파드가 이미지를 못 받는다
  • 노드가 NotReady 로 빠진다

원인 대부분이 로그다

  • 컨테이너 로그 로테이션 미설정
  • 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 는 여유로운데 느리다 → 스로틀링을 의심한다 파드 안에서

  • cat /sys/fs/cgroup/cpu.stat — # nr_throttled · throttled_usec 또는 컨테이너 런타임 지표에서 throttling 을 본다

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. 증상 → 확인 지점 표

증상먼저 볼 것
Pendingdescribe pod 의 FailedScheduling 문구
ImagePullBackOff이미지 이름·태그, imagePullSecrets
CrashLoopBackOfflogs --previous, Exit Code
0/1 Runningreadiness 프로브 실패 내용
Terminating 고착finalizer, PID 1 시그널 처리
서비스 접속 불가EndpointSlice 가 비었는지
이름 해석 실패CoreDNS 파드 상태, egress 정책의 53/UDP
노드 NotReadykubelet 로그, 디스크, CNI
kubectl 무응답static pod, etcd, 인증서 만료
롤아웃 멈춤새 파드의 Ready 여부, progressDeadlineSeconds
drain 멈춤PDB 의 minAvailable 과 replicas
볼륨 Multi-AttachRWO 볼륨을 다른 노드에서 붙이려는 것
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 Runningreadiness 실패. 엔드포인트에 안 들어간다
  • 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%)

스토리지와 설정 — 데이터·설정·비밀·RBAC