본문으로 건너뛰기

CKAD 개념 노트: 모니터링, 로그, 디버깅

  • kubernetes
  • ckad
  • kubectl
  • logging
  • debugging

기준: Kubernetes v1.35 (kind kindest/node:v1.35.8에서 명령어·YAML 확인)

목차

개념

무엇을 보면 되나

보고 싶은 것명령출처
Pod 목록과 STATUS, RESTARTSkubectl get podsAPI server
컨테이너 State, Reason, Restart Count, 이벤트kubectl describe pod <pod>API server (Pod + Event)
시스템이 가진 전체 정보kubectl get pod <pod> -o yamlAPI server
네임스페이스 이벤트kubectl events, kubectl get eventsEvent 리소스
CPU/메모리 사용량kubectl top pod, kubectl top nodeMetrics API (metrics-server)
애플리케이션 출력kubectl logskubelet이 노드에 저장한 stdout/stderr
컨테이너 내부kubectl exec, kubectl debug실행 중인 컨테이너 / 새로 붙인 컨테이너

Pod phase와 kubectl의 STATUS는 다른 값

  • phase는 API 필드. 값은 Pending, Running, Succeeded, Failed, Unknown 다섯 개뿐
  • kubectl get pods의 STATUS 열은 사람이 보기 좋게 만든 표시값. CrashLoopBackOff, Terminating, OOMKilled가 여기 뜨지만 phase 값은 아님
  • 실제 원인은 컨테이너 단위로 봐야 함. 컨테이너 state는 Waiting, Running, Terminated 셋이고, Waiting과 Terminated에는 Reason이 붙음

컨테이너 로그는 어디에 있나

  • 컨테이너가 stdout/stderr로 쓴 출력을 container runtime이 받아 kubelet이 노드의 /var/log/pods 아래에 파일로 저장
  • 컨테이너가 재시작되면 kubelet은 기본적으로 종료된 컨테이너 하나와 그 로그를 남김 → --previous로 읽는 대상
  • Pod가 노드에서 evict되면 컨테이너와 로그가 같이 삭제됨
  • 로그 rotation은 kubelet 담당. containerLogMaxSize 기본 10Mi, containerLogMaxFiles 기본 5
  • kubectl logs는 가장 최근 로그 파일만 읽음. 40MiB를 쓰고 10MiB마다 rotation되면 최대 10MiB만 보임
  • 파일로만 로그를 쓰는 앱이면 kubectl logs에 아무것도 안 나옴. 이 경우 streaming sidecar가 파일을 tail해서 자기 stdout으로 내보내는 패턴이 공식 문서에 있음

ephemeral container

  • 이미 만들어진 Pod에 임시로 추가하는 컨테이너. 트러블슈팅 전용
  • Pod spec에 직접 넣는 게 아니라 API의 ephemeralcontainers 핸들러로 추가 → kubectl edit로는 못 붙임
  • 제약
    • ports, livenessProbe, readinessProbe 불가
    • resources 불가 (Pod의 리소스 할당은 immutable)
    • 자동 재시작 없음, 리소스·실행 보장 없음
    • 한 번 추가하면 수정·삭제 불가
    • static Pod에서는 지원 안 함

동작 방식

흔한 실패 상태 읽기

표시어디서 보이나의미먼저 볼 것
Pendingphase스케줄 대기 또는 이미지 다운로드 중describe의 Events. FailedScheduling이면 리소스 부족, nodeSelector 불일치, hostPort 충돌
ImagePullBackOff컨테이너 Waiting Reason이미지를 못 가져옴. 이미지 이름 오류, private registry인데 imagePullSecret 없음 등이미지 이름·태그, registry 접근
CrashLoopBackOff컨테이너 Waiting Reason시작 → 종료를 반복하고 있고 재시작 back-off가 걸린 상태logs --previous, describe의 Last State와 Exit Code
OOMKilled컨테이너 Terminated Reasonmemory limit을 넘겨 커널이 종료. exitCode: 137resources.limits.memory, top pod

back-off 타이밍

  • 컨테이너 재시작: 10s, 20s, 40s … 식으로 늘어나고 300초(5분)에서 멈춤
  • 10분 동안 문제 없이 돌면 back-off 타이머 초기화
  • 이미지 pull 재시도도 같은 식으로 늘어나며 상한은 300초
  • 그래서 고친 직후에도 한동안 CrashLoopBackOff로 보일 수 있음. 바로 확인하려면 Pod를 다시 만드는 게 빠름

CrashLoopBackOff 원인 (공식 문서 목록)

  • 애플리케이션 에러로 종료
  • 설정 오류: 잘못된 환경 변수, 없는 설정 파일
  • 메모리·CPU 부족
  • liveness probe 또는 startup probe 실패

kubectl debug의 세 가지 모드

kubectl debug <pod> --image=...               # 기존 Pod에 ephemeral container 추가
kubectl debug <pod> --copy-to=<new> ...       # Pod를 복사해 새 Pod 생성 (원본은 그대로)
kubectl debug node/<node> --image=...         # 노드에 디버깅용 Pod 생성
  • ephemeral container 모드: --target=<container>로 대상 컨테이너의 process namespace를 공유. runtime이 지원하지 않으면 ps에 대상 프로세스가 안 보일 수 있음
  • copy 모드: 새 컨테이너 추가, --container로 기존 컨테이너의 command 교체, --set-image로 이미지 교체 가능. --share-processes로 Pod 안 컨테이너끼리 프로세스를 봄
  • node 모드: 노드 root filesystem이 /host에 마운트, host IPC/Network/PID namespace 사용. privileged는 아니라서 필요하면 --profile=sysadmin
  • --profile 값: legacy, general, baseline, restricted, netadmin, sysadmin. 지정 안 하면 legacy가 쓰이고, kubectl 1.35는 이때 --profile=legacy is deprecated and will be removed in the future라는 경고와 함께 --profile=general을 명시하라고 안내함
  • -i를 주면 새 컨테이너에 자동 attach. 연결이 끊기면 kubectl attach로 다시 붙음
  • --container를 안 주면 컨테이너 이름은 자동 생성 (debugger-xxxxx)

예시

상태와 이벤트

kubectl get pods -o wide
kubectl get pods --sort-by='.status.containerStatuses[0].restartCount'
kubectl describe pod <pod>

# 이벤트는 namespace 단위
kubectl get events --sort-by='.lastTimestamp'
kubectl get events -n my-namespace
kubectl events --for pod/<pod> --watch
kubectl events --types=Warning

# 메트릭 (metrics-server 필요)
kubectl top node
kubectl top pod --sort-by=memory
kubectl top pod <pod> --containers

종료 사유만 뽑기

kubectl get pod <pod> -o jsonpath='{.status.containerStatuses[0].lastState.terminated.reason}'
kubectl get pod <pod> -o go-template='{{range .status.containerStatuses}}{{.lastState.terminated.message}}{{end}}'
  • termination message는 컨테이너가 terminationMessagePath(기본 /dev/termination-log)에 쓴 내용
  • terminationMessagePolicy: FallbackToLogsOnError면 파일이 비어 있고 에러로 종료됐을 때 로그 마지막 부분(2048바이트 또는 80줄 중 작은 쪽)을 대신 사용

로그

kubectl logs <pod>
kubectl logs <pod> -c <container>          # multi-container Pod
kubectl logs <pod> --previous              # 직전에 종료된 컨테이너 (-p)
kubectl logs -f <pod>                      # 스트리밍
kubectl logs <pod> --all-containers=true
kubectl logs -l app=nginx --all-containers=true
kubectl logs deployment/nginx -c nginx-1
kubectl logs --tail=20 <pod>
kubectl logs --since=1h <pod>
kubectl logs <pod> --timestamps=true --prefix

streaming sidecar

공식 문서 예제를 줄인 형태. 앱은 파일로만 로그를 쓰고, sidecar가 그 파일을 자기 stdout으로 흘려보냄.

apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
    - name: count
      image: busybox:1.28
      args:
        - /bin/sh
        - -c
        - >
          i=0;
          while true;
          do
            echo "$i: $(date)" >> /var/log/1.log;
            i=$((i+1));
            sleep 1;
          done
      volumeMounts:
        - name: varlog
          mountPath: /var/log
    - name: count-log-1
      image: busybox:1.28
      args: [/bin/sh, -c, "tail -n+1 -F /var/log/1.log"]
      volumeMounts:
        - name: varlog
          mountPath: /var/log
  volumes:
    - name: varlog
      emptyDir: {}
kubectl logs counter count-log-1

exec

kubectl exec <pod> -- ls /
kubectl exec <pod> -c <container> -- cat /etc/config/app.conf
kubectl exec -it <pod> -- sh
  • -- 뒤가 컨테이너에서 실행할 명령
  • 이미지에 sh가 없으면 executable file not found in $PATH로 실패 → kubectl debug로 넘어감

debug

# shell 없는 이미지에 ephemeral container 추가
kubectl run ephemeral-demo --image=registry.k8s.io/pause:3.1 --restart=Never
kubectl debug -it ephemeral-demo --image=busybox:1.28 --target=ephemeral-demo
kubectl describe pod ephemeral-demo        # "Ephemeral Containers:" 항목에 추가됨

# 시작하자마자 죽는 컨테이너: 복사본에서 command를 shell로 교체
kubectl run --image=busybox:1.28 myapp -- false
kubectl debug myapp -it --copy-to=myapp-debug --container=myapp -- sh

# 복사본에 디버깅 도구 컨테이너 추가 + 프로세스 공유
kubectl debug myapp -it --image=ubuntu --share-processes --copy-to=myapp-debug

# 복사본의 모든 컨테이너 이미지 교체
kubectl debug myapp --copy-to=myapp-debug --set-image=*=ubuntu

# 노드
kubectl debug node/mynode -it --image=ubuntu

# 정리
kubectl delete pod myapp myapp-debug

헷갈리는 것 비교

exec, ephemeral container, copy

kubectl execkubectl debug <pod> --imagekubectl debug <pod> --copy-to
대상실행 중인 기존 컨테이너기존 Pod에 새 컨테이너 추가새 Pod
이미지에 shell 필요필요불필요 (디버그 이미지 사용)불필요
컨테이너가 죽어 있어도불가가능가능 (command 교체)
원본 Pod 변경없음ephemeral container가 남음 (삭제 불가)없음
뒷정리없음Pod를 지우기 전까지 남음복사한 Pod 삭제

—target과 —share-processes

--target=<container>--share-processes
쓰는 모드ephemeral container--copy-to
범위지정한 컨테이너 하나의 process namespace복사한 Pod의 모든 컨테이너
조건container runtime 지원 필요Pod 설정으로 적용

logs 옵션

옵션의미주의
-c컨테이너 지정컨테이너가 하나면 생략 가능
-p, --previous직전 종료 인스턴스kubelet이 남겨 둔 종료 컨테이너 하나가 대상
-f스트리밍끝내려면 Ctrl+C
--all-containers모든 컨테이너-l과 같이 쓰면 여러 Pod의 모든 컨테이너
-llabel selector동시 요청은 --max-log-requests 기본 5개

get events와 events

kubectl get eventskubectl events
시간순 정렬--sort-by='.lastTimestamp'기본 출력이 최근 이벤트 목록
특정 리소스만describe의 Events로 대신 봄--for pod/<name>
타입 필터---types=Warning
계속 보기--watch--watch

시험 포인트

먼저 치는 명령

kubectl get pods -A | grep -v Running
kubectl describe pod <pod> | grep -A10 -E 'State|Events'
kubectl logs <pod> -c <container> --previous
kubectl get events -n <ns> --sort-by='.lastTimestamp'
  • Pending은 logs로 봐도 소용없음. 컨테이너가 시작 전이라 로그가 없음. describe의 Events부터
  • CrashLoopBackOff는 현재 컨테이너가 막 떴다가 죽는 중이라 logs가 비어 있을 수 있음. --previous
  • OOMKilled는 describe의 Last State Reason과 Exit Code 137, 그리고 resources.limits.memory를 같이 봄
  • 이벤트는 namespace 단위. -n을 빼면 default namespace만 봄
  • kubectl top이 에러를 내면 명령보다 Metrics API(metrics-server) 배포 여부부터 의심

kubectl explain

kubectl explain pod.status.containerStatuses.state            # waiting / running / terminated
kubectl explain pod.status.containerStatuses.lastState        # 재시작 전 종료 reason, exitCode
kubectl explain pod.spec.containers.terminationMessagePolicy  # File / FallbackToLogsOnError
kubectl explain pod.spec.ephemeralContainers                  # kubectl debug가 추가하는 container
kubectl explain event                                         # reason, involvedObject, type

참고 문서