Kubernetes Pod 트러블슈팅 완벽 가이드 | CrashLoopBackOff
이 글의 핵심
Kubernetes Pod 트러블슈팅 CrashLoopBackOff, ImagePullBackOff, Pending 상태 해결. kubectl logs, describe, events로 원인 파악. 실무 체크리스트.
들어가며
Kubernetes Pod 트러블슈팅은 실무에서 가장 자주 마주치는 작업입니다. Pod가 CrashLoopBackOff, ImagePullBackOff, Pending 상태에 빠지면 서비스가 중단되므로 빠른 진단과 해결이 필요합니다. 비유로 말씀드리면, Pod 트러블슈팅은 자동차 고장 진단과 비슷합니다. 시동이 안 걸리면(CrashLoopBackOff), 연료가 없는지(ImagePullBackOff), 주차 공간이 없는지(Pending) 체크리스트를 따라 확인합니다.
이 글을 읽으면
- Pod 상태별 원인과 해결 방법을 파악합니다
- kubectl 명령어로 진단하는 방법을 익힙니다
- 실무 체크리스트를 활용할 수 있습니다
- 자주 발생하는 에러를 빠르게 해결합니다
Pod 상태 이해
Pod 라이프사이클 상세 분석
Kubernetes Pod는 여러 단계를 거쳐 실행됩니다. 각 상태의 내부 동작 원리를 이해하면 트러블슈팅이 훨씬 쉬워집니다.
Pod 생성부터 실행까지 흐름 (내부 메커니즘)
1. API Server: Pod 생성 요청 수신
kubectl apply -f pod.yaml
↓
- API Server가 YAML 검증
- Authentication/Authorization 확인
- Admission Controller 실행
* ResourceQuota 확인
* PodSecurityPolicy 검증
* MutatingWebhook (sidecar 주입 등)
2. etcd: Pod 정보 저장
↓
- API Server가 etcd에 Pod Spec 저장
- Key: /registry/pods/default/myapp-abc
- Value: Pod 전체 정의 (YAML)
3. Scheduler: 적절한 노드 선택
↓
Scheduling 알고리즘:
1) Filtering Phase (후보 노드 선별):
✅ 통과 조건:
- CPU/Memory 리소스 충분
- PVC가 노드에서 접근 가능
- nodeSelector 일치
- Taint/Toleration 일치
- Port 충돌 없음
❌ 제외 조건:
- 리소스 부족
- Disk Pressure
- Network Unreachable
2) Scoring Phase (점수 계산):
각 노드에 점수 부여:
- BalancedResourceAllocation: 10점
- LeastRequestedPriority: 8점
- NodeAffinityPriority: 5점
최고 점수 노드 선택
3) Binding:
API Server에 노드 할당 알림
Pod.spec.nodeName = "node1"
4. Kubelet: Pod 시작 (선택된 노드에서)
↓
Kubelet이 API Server를 watch:
- 자신의 노드에 할당된 Pod 감지
- Pod Spec 가져오기
1) 이미지 풀 (Container Runtime 호출):
CRI (Container Runtime Interface):
Kubelet → gRPC → containerd/Docker
ImageService.PullImage():
- 레지스트리 인증 (imagePullSecrets)
- 레이어별 다운로드
- 캐시 확인 (/var/lib/containerd/)
2) 컨테이너 생성:
RuntimeService.CreateContainer():
- Namespace 생성 (PID, Network, Mount)
- Cgroup 설정 (CPU/Memory 제한)
- Root Filesystem 마운트
3) 볼륨 마운트:
- hostPath: 호스트 경로 바인드
- emptyDir: tmpfs 또는 디스크
- PVC: CSI Plugin 호출
* CSI Driver가 볼륨 attach
* 파일시스템 마운트 (/var/lib/kubelet/pods/)
4) Network 설정 (CNI Plugin):
CNI (Container Network Interface):
- veth pair 생성
- IP 주소 할당 (IPAM)
- Bridge/Overlay 네트워크 연결
- iptables 규칙 추가 (Service)
5. Container Runtime: 컨테이너 실행
↓
RuntimeService.StartContainer():
containerd 실행 과정:
1) runc (OCI Runtime) 호출
2) Linux Namespace 격리:
- PID: 독립 프로세스 공간
- NET: 독립 네트워크 스택
- MNT: 독립 파일시스템
- UTS: 독립 호스트명
- IPC: 독립 IPC
3) Cgroup 제한 적용:
/sys/fs/cgroup/cpu/kubepods/pod<uid>/<container-id>/
- cpu.shares: CPU 비율
- memory.limit_in_bytes: 메모리 제한
4) Capabilities 제한:
- CAP_NET_ADMIN (제거)
- CAP_SYS_ADMIN (제거)
- 필요한 것만 허용
5) Seccomp Profile 적용:
- 위험한 syscall 차단
- 허용된 syscall만 실행
6) 애플리케이션 프로세스 실행:
ENTRYPOINT + CMD 실행
PID 1로 애플리케이션 시작
6. Pod 상태 업데이트
↓
Kubelet이 Container Runtime 상태 폴링:
- 1초마다 RuntimeService.ListContainers()
- Container 상태 확인:
* Created
* Running
* Exited
상태 변화:
Pending → ContainerCreating → Running
API Server로 상태 보고:
- Kubelet → API Server (PATCH /api/v1/pods/<name>/status)
- etcd 업데이트
- kubectl get pods에 반영
Exit Code와 재시작:
- Exit 0: 정상 종료 → restartPolicy 확인
- Exit 1-255: 에러 → 재시작 (BackOff)
- OOM Killed (137): 메모리 초과 → 재시작
- SIGTERM (143): Graceful shutdown
BackOff 재시작 간격:
1st: 0s (즉시)
2nd: 10s
3rd: 20s
4th: 40s
5th: 80s
6th+: 300s (5분 최대)
Pod 상태 전체 목록
| 상태 | 의미 | Kubernetes 내부 동작 | 일반적 원인 |
|---|---|---|---|
| Pending | 스케줄링 대기 중 | Scheduler가 적절한 노드를 찾는 중 | 리소스 부족, PVC 바인딩 실패, nodeSelector 불일치 |
| ContainerCreating | 컨테이너 생성 중 | Kubelet이 이미지 풀 및 컨테이너 초기화 중 | 이미지 다운로드 중, 볼륨 마운트 중 |
| Running | 실행 중 | 컨테이너가 정상 실행 중 | 정상 상태 |
| Succeeded | 완료 (Job, CronJob) | 컨테이너가 exit code 0으로 종료 | Job이 성공적으로 완료 |
| Failed | 실패 | 컨테이너가 non-zero exit code로 종료 | 애플리케이션 오류로 종료 |
| Unknown | 상태 불명 | API Server가 Kubelet과 통신 불가 | 노드 다운, 네트워크 장애 |
| CrashLoopBackOff | 반복 크래시 | 컨테이너가 시작→크래시를 반복, 재시작 대기 중 | 애플리케이션 에러, 설정 오류 |
| ImagePullBackOff | 이미지 가져오기 실패 | Container Runtime이 이미지 풀 재시도 대기 중 | 이미지 없음, 인증 실패, 레지스트리 다운 |
| Error | 에러 상태 | 컨테이너 실행 중 에러 발생 | 설정 오류, 런타임 에러 |
| Terminating | 종료 중 | Pod가 종료 신호(SIGTERM) 받고 graceful shutdown 중 | 정상적인 종료 과정 |
| Init:0/1 | Init Container 실행 중 | Init Container가 실행 중 (0/1 = 0개 완료, 1개 총) | Init Container 초기화 중 |
Pod 상태 확인 명령어
기본 확인:
kubectl get pods
# 출력 예시:
NAME READY STATUS RESTARTS AGE
myapp-5d4b7c8f9-abc12 1/1 Running 0 5m
myapp-5d4b7c8f9-def34 0/1 CrashLoopBackOff 5 3m
myapp-5d4b7c8f9-ghi56 0/1 ImagePullBackOff 0 2m
myapp-5d4b7c8f9-jkl78 0/1 Pending 0 1m
출력 컬럼 의미:
- NAME: Pod 이름 (ReplicaSet이 생성한 경우 해시 포함)
- READY: 준비된 컨테이너 수 / 전체 컨테이너 수
1/1: 1개 중 1개 준비 완료 (정상)0/1: 1개 중 0개 준비 (문제 있음)2/3: 3개 중 2개만 준비 (일부 문제)
- STATUS: 현재 Pod 상태
- RESTARTS: 재시작 횟수 (높을수록 문제 있음)
- AGE: Pod가 생성된 후 경과 시간
상세 정보 확인:
# -o wide: 더 많은 정보 (IP, Node 등)
kubectl get pods -o wide
# 출력:
NAME READY STATUS RESTARTS AGE IP NODE
myapp-abc 1/1 Running 0 5m 10.244.1.5 node1
# -o yaml: 전체 YAML 정의 확인
kubectl get pod myapp-abc -o yaml
# -o json: JSON 형식 (jq로 파싱 가능)
kubectl get pod myapp-abc -o json | jq '.status'
특정 상태의 Pod만 필터링:
# Running 상태가 아닌 모든 Pod
kubectl get pods --field-selector=status.phase!=Running
# Pending 상태 Pod만
kubectl get pods --field-selector=status.phase=Pending
# 재시작이 5회 이상인 Pod (복잡한 필터는 jq 사용)
kubectl get pods -o json | jq '.items[] | select(.status.containerStatuses[0].restartCount > 5) | .metadata.name'
실시간 모니터링 (watch):
# 2초마다 상태 갱신
kubectl get pods -w
# 또는 watch 명령 사용
watch -n 2 kubectl get pods
READY 컬럼 이해하기
1/1 (정상):
1개 컨테이너 중 1개가 Ready 상태
→ Readiness probe 통과
→ 트래픽 받을 준비 완료
0/1 (문제):
1개 컨테이너 중 0개가 Ready
→ 컨테이너는 실행 중이지만 Readiness probe 실패
→ Service에서 트래픽 안 받음
→ 원인: 헬스체크 실패, 초기화 지연
2/3 (부분 문제):
3개 컨테이너 중 2개만 Ready
→ 멀티 컨테이너 Pod에서 1개 실패
→ 사이드카 컨테이너 문제일 가능성
실무 예시:
kubectl get pods
NAME READY STATUS RESTARTS AGE
app-abc 2/3 Running 0 5m
# 어느 컨테이너가 문제인지 확인
kubectl get pod app-abc -o jsonpath='{.status.containerStatuses[*].name}'
# main sidecar init-db
kubectl get pod app-abc -o jsonpath='{.status.containerStatuses[*].ready}'
# true true false
# ↑ init-db 컨테이너가 Ready 아님
RESTARTS 컬럼 해석
RESTARTS 의미
0 정상 (한 번도 재시작 안 함)
1-2 일시적 문제 (네트워크 지연, 일회성 에러)
3-5 반복 문제 (설정 오류, 간헐적 에러)
10+ 심각한 문제 (CrashLoopBackOff, 근본 원인 해결 필요)
재시작 원인 확인:
kubectl describe pod myapp-abc | grep -A 10 "Last State"
# 출력:
Last State: Terminated
Reason: Error
Exit Code: 1
Started: Mon, 17 Apr 2026 10:00:00 +0900
Finished: Mon, 17 Apr 2026 10:00:05 +0900
Exit Code 의미:
- 0: 정상 종료
- 1: 일반 에러
- 2: 잘못된 사용 (Misuse of shell builtins)
- 126: 명령 실행 불가 (Permission denied)
- 127: 명령을 찾을 수 없음 (Command not found)
- 130: Ctrl+C로 종료 (SIGINT)
- 137: OOM Killed (Out of Memory)
- 143: SIGTERM (정상적인 종료 신호)
Pod 상태 전환 다이어그램
[Pod 생성 요청]
↓
[Pending]
↓ (스케줄링 성공)
[ContainerCreating]
↓ (이미지 풀 실패)
[ImagePullBackOff] ← 재시도
↓ (이미지 풀 성공)
[Running]
↓ (liveness probe 실패 또는 크래시)
[CrashLoopBackOff] ← 재시작 반복
↓ (수정 후 재배포)
[Running]
↓ (정상 종료 또는 삭제)
[Terminating]
↓
[Terminated]
상태별 대응 전략
| 상태 | 긴급도 | 즉시 확인할 것 | 다음 단계 |
|---|---|---|---|
| Pending | 🟡 중간 | kubectl describe pod Events 섹션 | 리소스/PVC/nodeSelector 확인 |
| ContainerCreating | 🟢 낮음 | 정상 (이미지 다운로드 중), 너무 오래 걸리면 describe | 이미지 크기 확인, 네트워크 확인 |
| ImagePullBackOff | 🔴 높음 | kubectl describe pod → 이미지 이름, 인증 | 이미지 존재 여부, imagePullSecrets |
| CrashLoopBackOff | 🔴 높음 | kubectl logs pod --previous → 에러 로그 | 설정, 환경 변수, 의존성 확인 |
| Running (READY 0/1) | 🟡 중간 | Readiness probe 설정 확인 | 헬스체크 엔드포인트, 초기화 시간 |
| Error | 🔴 높음 | kubectl logs → 즉각적인 에러 확인 | 설정 수정, 재배포 |
| Unknown | 🔴 높음 | kubectl get nodes → 노드 상태 | 노드 재시작, 네트워크 확인 |
진단 도구
kubectl logs - 애플리케이션 로그 분석
kubectl logs는 컨테이너 내부의 stdout/stderr 출력을 확인하는 핵심 도구입니다.
kubectl logs가 로그를 가져오는 원리:
kubectl logs 내부 동작 흐름:
1. kubectl → API Server
요청: "myapp-abc Pod의 로그를 주세요"
2. API Server → Kubelet (Pod가 실행 중인 노드)
요청: "이 Pod의 컨테이너 로그를 주세요"
3. Kubelet → Container Runtime (containerd, Docker 등)
요청: "컨테이너 로그 파일 경로를 알려주세요"
4. Container Runtime:
- 로그 파일 위치: /var/log/pods/<namespace>_<pod-name>_<pod-uid>/<container-name>/0.log
- 실제로는 JSON 형식으로 저장 (타임스탬프 + 로그 내용)
5. Kubelet → API Server → kubectl
로그 스트림 전송
중요 제약:
- 로그는 컨테이너 재시작 시 삭제됨 (--previous로 이전 로그 1회만 접근 가능)
- 로그 파일 크기 제한 (기본 10MB, log-max-size 설정)
- 로그 파일 개수 제한 (기본 5개, log-max-files 설정)
기본 사용법
현재 실행 중인 컨테이너 로그:
kubectl logs myapp-abc
# 내부 동작:
# Kubelet이 /var/log/pods/.../myapp-abc/0.log 파일 읽음
# JSON 라인을 파싱하여 실제 로그 메시지만 추출
# kubectl에 스트림으로 전송
# 출력 예시:
2026-04-17 10:30:00 INFO Starting application...
2026-04-17 10:30:01 INFO Database connected
2026-04-17 10:30:02 INFO Server listening on port 8080
이전 컨테이너 로그 (재시작된 경우):
# --previous: 크래시 직전 로그 확인 (가장 중요!)
kubectl logs myapp-abc --previous
# 이유: 현재 컨테이너는 방금 재시작됨
# 크래시 원인은 이전 로그에 있음
# 출력 예시:
2026-04-17 10:29:55 INFO Starting application...
2026-04-17 10:29:56 ERROR Database connection failed
2026-04-17 10:29:56 ERROR Cannot connect to postgresql://db:5432
2026-04-17 10:29:57 FATAL Exiting with code 1
실시간 로그 (tail -f와 동일):
kubectl logs myapp-abc -f
# Ctrl+C로 종료
# 실시간 로그 스트림 확인
# 요청 처리, 에러 발생을 즉시 확인 가능
멀티 컨테이너 Pod:
# Pod 내 컨테이너 목록 확인
kubectl get pod myapp-abc -o jsonpath='{.spec.containers[*].name}'
# main sidecar
# 특정 컨테이너 로그
kubectl logs myapp-abc -c main
kubectl logs myapp-abc -c sidecar
# 모든 컨테이너 로그 (--all-containers)
kubectl logs myapp-abc --all-containers
# 컨테이너별 접두사 표시
kubectl logs myapp-abc --all-containers --prefix
최근 N줄만 확인:
# 최근 100줄
kubectl logs myapp-abc --tail=100
# 최근 10줄 + 실시간
kubectl logs myapp-abc --tail=10 -f
시간 범위로 필터:
# 최근 1시간 로그
kubectl logs myapp-abc --since=1h
# 최근 5분 로그
kubectl logs myapp-abc --since=5m
# 특정 시간 이후
kubectl logs myapp-abc --since-time=2026-04-17T10:00:00Z
고급 패턴
에러만 필터링:
# ERROR 문자열 포함된 줄만
kubectl logs myapp-abc | grep -i error
# 여러 키워드
kubectl logs myapp-abc | grep -iE "error|fatal|exception"
# 컨텍스트 포함 (앞뒤 5줄)
kubectl logs myapp-abc | grep -C 5 -i error
로그 저장:
# 파일로 저장
kubectl logs myapp-abc > myapp.log
# 이전 로그도 저장
kubectl logs myapp-abc --previous > myapp-previous.log
# 타임스탬프 포함
kubectl logs myapp-abc --timestamps > myapp.log
여러 Pod 로그 동시 확인:
# Label로 여러 Pod 선택
kubectl logs -l app=myapp --all-containers --prefix
# 출력:
[pod/myapp-abc/main] 2026-04-17 10:30:00 INFO Request received
[pod/myapp-def/main] 2026-04-17 10:30:01 INFO Request received
로그가 없을 때:
kubectl logs myapp-abc
# 출력: (빈 화면)
# 원인 1: 애플리케이션이 stdout에 출력 안 함
# 원인 2: 로그가 파일에만 저장됨
# 해결: 컨테이너에 직접 접속
kubectl exec -it myapp-abc -- cat /var/log/app.log
로그 수집 모범 사례
1. 애플리케이션은 항상 stdout/stderr에 출력:
// ❌ 나쁜 예: 파일에만 로그
const fs = require('fs');
fs.appendFileSync('/var/log/app.log', 'message\n');
// ✅ 좋은 예: stdout 출력
console.log('message');
// ✅ 더 좋은 예: JSON 형식 (파싱 용이)
console.log(JSON.stringify({
timestamp: new Date().toISOString(),
level: 'INFO',
message: 'Request received',
userId: 123
}));
2. 타임스탬프와 레벨 포함:
✅ 좋은 로그:
2026-04-17T10:30:00.123Z INFO [UserService] User logged in: user_id=123
❌ 나쁜 로그:
User logged in
3. 에러 시 스택 트레이스 포함:
try {
// ...
} catch (error) {
console.error('Error processing request:', error.message);
console.error(error.stack); // 스택 트레이스 필수!
}
kubectl describe - 상세 정보 확인
kubectl describe는 Pod의 전체 상태와 이벤트를 확인하는 가장 중요한 도구입니다.
kubectl describe의 내부 동작:
kubectl describe pod myapp-abc 흐름:
1. kubectl → API Server
요청: "myapp-abc Pod의 모든 상태 정보를 주세요"
2. API Server → etcd
etcd에 저장된 Pod 스펙 조회:
- metadata (이름, 네임스페이스, 레이블, 어노테이션)
- spec (컨테이너 정의, 볼륨, 리소스 요청/제한)
- status (현재 상태, IP, 컨테이너 상태)
3. API Server → Event API
Pod와 관련된 모든 이벤트 조회:
- Scheduled, Pulling, Pulled, Created, Started
- Warning (Unhealthy, BackOff, FailedMount 등)
- 최근 1시간 이내 이벤트만 유지
4. kubectl이 모든 정보를 사람이 읽기 쉬운 형식으로 포맷팅
중요한 차이점:
- kubectl get: 요약 정보만 (1줄)
- kubectl describe: 모든 상세 정보 + 이벤트 히스토리
- Events는 API Server가 별도로 관리 (Event 리소스)
기본 사용법
kubectl describe pod myapp-abc
# 내부 동작:
# 1. Pod 객체 전체 조회 (etcd)
# 2. Pod와 관련된 Event 객체 조회 (Event API)
# 3. 모든 정보를 통합하여 출력
출력 섹션별 해석 (Kubernetes 내부 관점)
1. Name and Namespace:
Name: myapp-5d4b7c8f9-abc12
Namespace: default
Priority: 0
Node: node1/192.168.1.10
- Node: 어느 노드에서 실행 중인지 (노드 문제 파악 시 중요)
2. Labels and Annotations:
Labels: app=myapp
pod-template-hash=5d4b7c8f9
Annotations: <none>
- Service/Selector가 이 Pod를 찾는 기준
3. Status:
Status: Running
IP: 10.244.1.5
IPs:
IP: 10.244.1.5
- IP: Pod의 Cluster IP (Service와 통신)
4. Containers (가장 중요!):
Containers:
main:
Container ID: containerd://abc123...
Image: myapp:v1.0.0
Image ID: docker-pullable://myapp@sha256:def456...
Port: 8080/TCP
Host Port: 0/TCP
State: Running
Started: Mon, 17 Apr 2026 10:30:00 +0900
Last State: Terminated
Reason: Error
Exit Code: 1
Started: Mon, 17 Apr 2026 10:25:00 +0900
Finished: Mon, 17 Apr 2026 10:29:57 +0900
Ready: True
Restart Count: 5
Limits:
cpu: 500m
memory: 512Mi
Requests:
cpu: 100m
memory: 128Mi
Liveness: http-get http://:8080/health delay=30s timeout=5s period=10s #success=1 #failure=3
Readiness: http-get http://:8080/ready delay=5s timeout=3s period=5s #success=1 #failure=3
Environment:
DATABASE_URL: postgresql://postgres:5432/mydb
API_KEY: <set to the key 'api-key' in secret 'api-secret'> Optional: false
핵심 정보:
- Last State: 이전 종료 이유 (Error, OOMKilled 등)
- Exit Code: 종료 코드 (137=OOM, 1=에러)
- Restart Count: 재시작 횟수 (높으면 문제)
- Limits/Requests: 리소스 설정 (OOM 시 중요)
- Liveness/Readiness: 헬스체크 설정
- Environment: 환경 변수 (설정 오류 확인)
5. Conditions:
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Condition 해석:
- PodScheduled: 노드에 스케줄됨
- Initialized: Init Container 완료
- ContainersReady: 모든 컨테이너 Ready
- Ready: Pod가 트래픽 받을 준비
False인 경우 문제:
Conditions:
Type Status Reason
Initialized True
Ready False ContainersNotReady
ContainersReady False
PodScheduled True
→ 컨테이너가 Ready 아님 → Readiness probe 실패
6. Volumes:
Volumes:
config-volume:
Type: ConfigMap (a volume populated by a ConfigMap)
Name: app-config
Optional: false
secret-volume:
Type: Secret (a volume populated by a Secret)
SecretName: api-secret
Optional: false
7. Events (가장 중요! 트러블슈팅의 핵심):
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 5m default-scheduler Successfully assigned default/myapp-abc to node1
Normal Pulling 5m kubelet Pulling image "myapp:v1.0.0"
Normal Pulled 4m30s kubelet Successfully pulled image "myapp:v1.0.0"
Normal Created 4m30s kubelet Created container main
Normal Started 4m30s kubelet Started container main
Warning Unhealthy 3m (x3 over 4m) kubelet Liveness probe failed: HTTP probe failed with statuscode: 500
Normal Killing 3m kubelet Container main failed liveness probe, will be restarted
Warning BackOff 2m (x5 over 3m) kubelet Back-off restarting failed container
Event 타입:
- Normal: 정상 동작 (Scheduled, Pulled, Started)
- Warning: 문제 발생 (Failed, Unhealthy, BackOff)
Event 읽는 법:
- 시간 순서대로 읽기 (Age 컬럼)
- Warning만 필터링
- 반복되는 메시지 확인 (x3, x5 등)
특정 섹션만 추출
# Events만 보기
kubectl describe pod myapp-abc | grep -A 20 "Events:"
# Containers 섹션만
kubectl describe pod myapp-abc | grep -A 50 "Containers:"
# 환경 변수만
kubectl describe pod myapp-abc | grep -A 10 "Environment:"
kubectl get events - 클러스터 이벤트 히스토리
kubectl get events는 클러스터 전체의 이벤트를 시간 순으로 확인합니다.
기본 사용법
# 모든 이벤트 (최신순)
kubectl get events --sort-by=.metadata.creationTimestamp
# 출력:
LAST SEEN TYPE REASON OBJECT MESSAGE
2m Warning BackOff pod/myapp-abc Back-off restarting failed container
3m Warning Unhealthy pod/myapp-abc Liveness probe failed
5m Normal Scheduled pod/myapp-abc Successfully assigned to node1
특정 네임스페이스:
kubectl get events -n production --sort-by=.metadata.creationTimestamp
특정 Pod 이벤트만:
kubectl get events --field-selector involvedObject.name=myapp-abc
# 더 구체적으로
kubectl get events \
--field-selector involvedObject.kind=Pod,involvedObject.name=myapp-abc
Warning만 필터:
kubectl get events --field-selector type=Warning
실시간 모니터링:
kubectl get events -w
# 새 이벤트가 발생할 때마다 즉시 표시
고급 패턴
특정 시간 이후 이벤트:
# 최근 10분
kubectl get events --sort-by=.metadata.creationTimestamp | head -n 50
# jq로 필터 (10분 이내)
kubectl get events -o json | jq '.items[] | select(.lastTimestamp > (now - 600))'
이벤트를 파일로 저장:
kubectl get events --sort-by=.metadata.creationTimestamp -o wide > events.txt
모든 네임스페이스 이벤트:
kubectl get events --all-namespaces --sort-by=.metadata.creationTimestamp
진단 워크플로 (순서가 중요!)
트러블슈팅 시 다음 순서로 진행하면 효율적입니다:
# 1단계: Pod 상태 확인
kubectl get pods
# → STATUS 컬럼 확인
# 2단계: 로그 확인 (애플리케이션 레벨 에러)
kubectl logs <pod-name>
kubectl logs <pod-name> --previous # 재시작된 경우 필수!
# 3단계: 상세 정보 확인 (Kubernetes 레벨 정보)
kubectl describe pod <pod-name>
# → Events 섹션 집중
# 4단계: 클러스터 이벤트 확인 (전체 맥락)
kubectl get events --sort-by=.metadata.creationTimestamp
# 5단계: 리소스 확인 (리소스 문제 의심 시)
kubectl top nodes
kubectl top pods
실무 예시:
# CrashLoopBackOff 발생
kubectl get pods
# myapp-abc 0/1 CrashLoopBackOff 5 3m
# 즉시 이전 로그 확인 (90% 여기서 원인 파악)
kubectl logs myapp-abc --previous
# Error: DATABASE_URL is not set
# describe로 환경 변수 확인
kubectl describe pod myapp-abc | grep -A 10 "Environment:"
# Environment:
# API_KEY: <set to the key 'api-key' in secret 'api-secret'>
# → DATABASE_URL 누락 확인!
# ConfigMap/Secret 확인
kubectl get configmap
kubectl get secret
# 해결: DATABASE_URL 추가
kubectl edit deployment myapp
# 또는 kubectl set env
CrashLoopBackOff 해결
CrashLoopBackOff란?
CrashLoopBackOff는 Pod가 시작 → 크래시 → 재시작 → 다시 크래시를 반복하는 상태입니다.
내부 동작 원리
시간축 →
10:00:00 Pod 시작
10:00:05 크래시 (Exit Code 1)
10:00:10 재시작 (1회)
10:00:15 크래시
10:00:25 재시작 (2회, 10초 대기)
10:00:30 크래시
10:00:50 재시작 (3회, 20초 대기)
10:00:55 크래시
10:01:35 재시작 (4회, 40초 대기) ← BackOff 시간 증가
10:01:40 크래시
...
BackOff 시간 증가:
- 1회: 즉시
- 2회: 10초 대기
- 3회: 20초 대기
- 4회: 40초 대기
- 5회+: 80초 대기 (최대 5분까지)
왜 BackOff인가?
- 계속 즉시 재시작하면 시스템 리소스 낭비
- 일시적 문제(DB 재시작 등)가 해결될 시간 제공
- 무한 재시작 방지
원인 Top 5 (빈도순)
| 순위 | 원인 | 빈도 | 진단 방법 |
|---|---|---|---|
| 1 | 애플리케이션 에러 | 40% | kubectl logs --previous |
| 2 | 환경 변수 누락 | 25% | kubectl describe → Environment |
| 3 | 의존성 연결 실패 | 20% | 로그에서 “connection refused” |
| 4 | 메모리 OOM | 10% | kubectl describe → OOMKilled |
| 5 | Liveness Probe 실패 | 5% | kubectl describe → Events |
진단 순서 (필수 체크리스트)
1단계: 이전 컨테이너 로그 확인 (가장 중요!)
# ❌ 잘못된 방법: 현재 로그
kubectl logs myapp-abc
# 출력:
2026-04-17 10:05:00 INFO Starting application...
# → 방금 재시작됨, 크래시 이유 모름
# ✅ 올바른 방법: 이전 로그
kubectl logs myapp-abc --previous
# 출력:
2026-04-17 10:04:55 INFO Starting application...
2026-04-17 10:04:56 ERROR Failed to connect to database
2026-04-17 10:04:56 ERROR Connection refused: postgresql://db:5432
2026-04-17 10:04:57 FATAL Exiting with code 1
# → 크래시 원인 파악!
로그에서 찾아야 할 키워드:
ERROR,FATAL,EXCEPTIONCannot connect,Connection refusedEnvironment variable ... not setENOENT(파일 없음)EACCES(권한 없음)Out of memory,Killed
2단계: describe로 Exit Code 확인
kubectl describe pod myapp-abc
# 핵심 정보:
Last State: Terminated
Reason: Error
Exit Code: 1
Started: Mon, 17 Apr 2026 10:04:55 +0900
Finished: Mon, 17 Apr 2026 10:04:57 +0900
Restart Count: 12 # 재시작 횟수
Exit Code 해석:
| Exit Code | 의미 | 일반적 원인 |
|---|---|---|
| 0 | 정상 종료 | Job 완료 (Pod는 Succeeded 상태로) |
| 1 | 일반 에러 | 애플리케이션 에러, 예외 처리 안 됨 |
| 2 | 잘못된 사용 | 명령어 인자 오류 |
| 126 | 실행 불가 | 파일 권한 없음 (chmod +x 필요) |
| 127 | 명령 없음 | 컨테이너에 명령어가 없음 (typo, PATH 문제) |
| 130 | Ctrl+C | SIGINT (수동 종료) |
| 137 | OOM Killed | 메모리 부족으로 강제 종료 |
| 143 | SIGTERM | 정상적인 종료 신호 (Pod 삭제 시) |
| 255 | Exit Code 범위 초과 | 예상치 못한 종료 |
3단계: Events 섹션 분석
kubectl describe pod myapp-abc | grep -A 20 "Events:"
# 패턴 1: 반복 재시작
Events:
Warning BackOff 1m (x12 over 5m) kubelet Back-off restarting failed container
↑ 12번 재시작됨 ↑ 5분간 계속 발생
# 패턴 2: Liveness Probe 실패
Events:
Warning Unhealthy 2m (x5 over 3m) kubelet Liveness probe failed: HTTP probe failed
Normal Killing 2m kubelet Container myapp failed liveness probe
# 패턴 3: OOM
Events:
Warning OOMKilling 1m kubelet Memory cgroup out of memory
해결 패턴 (실무 케이스 기반)
패턴 1: 애플리케이션 에러 (Exit Code 1)
증상:
kubectl logs myapp-abc --previous
# TypeError: Cannot read property 'name' of undefined
# at /app/server.js:45:20
원인: 코드 버그, 예외 처리 누락
해결:
// ❌ 나쁜 코드
const user = await getUser(userId);
console.log(user.name); // user가 null이면 크래시
// ✅ 개선된 코드
const user = await getUser(userId);
if (!user) {
console.error('User not found:', userId);
return res.status(404).json({ error: 'User not found' });
}
console.log(user.name);
임시 방지책:
# Pod가 크래시해도 계속 실행되게 (권장 안 함!)
command: ["/bin/sh", "-c"]
args: ["node server.js || sleep 3600"] # 실패 시 1시간 대기
패턴 2: 환경 변수 누락 (Exit Code 1)
증상:
kubectl logs myapp-abc --previous
# Error: Environment variable DATABASE_URL is not set
# at /app/config.js:10:11
진단:
# 환경 변수 확인
kubectl describe pod myapp-abc | grep -A 10 "Environment:"
# 출력:
Environment:
API_KEY: <set to the key 'api-key' in secret 'api-secret'>
# → DATABASE_URL 누락!
# 또는 직접 확인
kubectl exec myapp-abc -- env | grep DATABASE
# (빈 출력)
해결 방법 1: Deployment 수정
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: myapp
env:
- name: DATABASE_URL
value: "postgresql://postgres:5432/mydb"
- name: API_KEY
valueFrom:
secretKeyRef:
name: api-secret
key: api-key
해결 방법 2: kubectl set env (빠른 수정)
kubectl set env deployment/myapp DATABASE_URL=postgresql://postgres:5432/mydb
해결 방법 3: ConfigMap 사용
# ConfigMap 생성
kubectl create configmap app-config \
--from-literal=DATABASE_URL=postgresql://postgres:5432/mydb \
--from-literal=REDIS_URL=redis://redis:6379
# Deployment에 적용
kubectl patch deployment myapp -p '
{
"spec": {
"template": {
"spec": {
"containers": [{
"name": "myapp",
"envFrom": [{
"configMapRef": {"name": "app-config"}
}]
}]
}
}
}
}'
패턴 3: 의존성 연결 실패 (Exit Code 1)
증상:
kubectl logs myapp-abc --previous
# Error: connect ECONNREFUSED 10.96.0.10:5432
# Error: getaddrinfo ENOTFOUND postgres
원인: 데이터베이스/Redis 등 의존 서비스에 연결 불가
진단:
# 1. Service 존재 확인
kubectl get svc
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
# postgres ClusterIP 10.96.0.10 <none> 5432/TCP
# 2. Service DNS 확인
kubectl exec myapp-abc -- nslookup postgres
# Server: 10.96.0.1
# Address: 10.96.0.1#53
# Name: postgres.default.svc.cluster.local
# Address: 10.96.0.10
# 3. 연결 테스트
kubectl exec myapp-abc -- nc -zv postgres 5432
# postgres (10.96.0.10:5432) open
# 4. 데이터베이스 Pod 상태 확인
kubectl get pods -l app=postgres
해결 방법 1: Init Container로 대기
spec:
initContainers:
- name: wait-for-db
image: busybox:1.35
command: ['sh', '-c']
args:
- |
until nc -z postgres 5432; do
echo "Waiting for postgres..."
sleep 2
done
echo "Postgres is ready!"
해결 방법 2: 애플리케이션에서 재시도 로직
// ❌ 즉시 실패
const db = await connectDB();
// ✅ 재시도 로직
async function connectWithRetry(maxRetries = 10) {
for (let i = 0; i < maxRetries; i++) {
try {
const db = await connectDB();
console.log('Database connected');
return db;
} catch (error) {
console.log(`Connection attempt ${i+1} failed, retrying...`);
await new Promise(resolve => setTimeout(resolve, 5000));
}
}
throw new Error('Failed to connect to database after retries');
}
패턴 4: 메모리 OOM (Exit Code 137)
증상:
kubectl describe pod myapp-abc
# Last State:
Last State: Terminated
Reason: OOMKilled
Exit Code: 137
Started: Mon, 17 Apr 2026 10:00:00 +0900
Finished: Mon, 17 Apr 2026 10:02:30 +0900
# Events:
Events:
Warning OOMKilling Memory cgroup out of memory: Killed process 1234 (node)
원인 확인:
# 리소스 사용량 확인
kubectl top pod myapp-abc
# NAME CPU(cores) MEMORY(bytes)
# myapp-abc 50m 512Mi
# Limits 확인
kubectl get pod myapp-abc -o jsonpath='{.spec.containers[0].resources}'
# {"limits":{"memory":"512Mi"},"requests":{"memory":"256Mi"}}
# → 512Mi 제한에 도달하여 종료됨
해결 방법 1: Limits 증가
resources:
requests:
memory: "256Mi"
limits:
memory: "1Gi" # 512Mi → 1Gi 증가
해결 방법 2: 메모리 누수 수정
// ❌ 메모리 누수
const cache = {};
app.get('/api/data', (req, res) => {
cache[req.params.id] = largeData; // 계속 쌓임
});
// ✅ LRU 캐시 사용
const LRU = require('lru-cache');
const cache = new LRU({ max: 500, maxAge: 1000 * 60 * 60 });
해결 방법 3: Node.js 힙 크기 조정
env:
- name: NODE_OPTIONS
value: "--max-old-space-size=512" # 512MB 힙
패턴 5: Liveness Probe 실패
증상:
kubectl describe pod myapp-abc
# Events:
Events:
Warning Unhealthy Liveness probe failed: HTTP probe failed with statuscode: 500
Normal Killing Container myapp failed liveness probe, will be restarted
원인:
- 헬스체크 엔드포인트가 500 에러 반환
initialDelaySeconds가 너무 짧아서 앱 시작 전에 체크timeoutSeconds가 너무 짧아서 응답 전에 타임아웃
진단:
# 헬스체크 엔드포인트 직접 테스트
kubectl exec myapp-abc -- curl -v http://localhost:8080/health
# 또는 포트포워딩으로
kubectl port-forward myapp-abc 8080:8080
curl http://localhost:8080/health
해결 방법 1: initialDelaySeconds 증가
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 60 # 30 → 60초 (앱 시작 충분히 대기)
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
해결 방법 2: 헬스체크 엔드포인트 수정
// ❌ 나쁜 헬스체크
app.get('/health', async (req, res) => {
const dbHealth = await db.ping(); // DB 느리면 타임아웃
if (!dbHealth) return res.status(500).send('Unhealthy');
res.send('OK');
});
// ✅ 좋은 헬스체크
app.get('/health', (req, res) => {
// 앱 자체가 살아있는지만 체크
res.send('OK');
});
// ✅ 의존성 체크는 별도 엔드포인트
app.get('/ready', async (req, res) => {
// Readiness probe에서 사용
const dbHealth = await db.ping();
if (!dbHealth) return res.status(503).send('Not ready');
res.send('Ready');
});
패턴 6: 파일 권한 문제 (Exit Code 126)
증상:
kubectl logs myapp-abc --previous
# /app/start.sh: Permission denied
해결:
# Dockerfile에서 권한 부여
COPY start.sh /app/
RUN chmod +x /app/start.sh
CMD ["/app/start.sh"]
패턴 7: 명령어 없음 (Exit Code 127)
증상:
kubectl logs myapp-abc --previous
# /bin/sh: node: not found
원인: node가 PATH에 없거나 설치 안 됨
해결:
# 절대 경로 사용
command: ["/usr/local/bin/node"]
args: ["server.js"]
# 또는 Dockerfile 확인
# FROM node:18-alpine
빠른 디버깅 팁
즉시 재시작 방지 (디버깅용):
# 크래시해도 컨테이너 유지
command: ["/bin/sh"]
args: ["-c", "node server.js || sleep 3600"]
컨테이너 내부 접속 (실행 중일 때):
kubectl exec -it myapp-abc -- /bin/sh
# 파일 시스템 확인, 환경 변수, 수동 명령 실행
로그 실시간 모니터링:
# 터미널 1: 상태 감시
watch -n 1 kubectl get pods
# 터미널 2: 로그 실시간
kubectl logs myapp-abc -f
ImagePullBackOff 해결
원인
- 이미지가 존재하지 않음
- 이미지 태그 오류
- 레지스트리 인증 실패
- 네트워크 문제
- 레지스트리 다운
진단 순서
1단계: 이미지 이름 확인
kubectl describe pod <pod-name>
Events:
Type Reason Message
---- ------ -------
Normal Pulling pulling image "myregistry.com/myapp:v1.0.0"
Warning Failed Failed to pull image "myregistry.com/myapp:v1.0.0": rpc error: code = NotFound
Warning Failed Error: ErrImagePull
2단계: 이미지 존재 확인
# Docker Hub
docker pull myapp:v1.0.0
# 프라이빗 레지스트리
docker pull myregistry.com/myapp:v1.0.0
해결 패턴
패턴 1: 이미지 태그 오류
# 잘못된 예
image: myapp:v1.0.0 # 태그가 존재하지 않음
# 올바른 예
image: myapp:latest
패턴 2: 레지스트리 인증 Secret 생성:
kubectl create secret docker-registry regcred \
--docker-server=myregistry.com \
--docker-username=myuser \
--docker-password=mypassword \
[email protected]
Deployment에 적용:
spec:
imagePullSecrets:
- name: regcred
containers:
- name: myapp
image: myregistry.com/myapp:v1.0.0
패턴 3: 이미지 정책
# 항상 최신 이미지 가져오기
imagePullPolicy: Always
# 로컬에 없을 때만 가져오기
imagePullPolicy: IfNotPresent
# 로컬 이미지만 사용
imagePullPolicy: Never
Pending 상태 해결
원인
- 리소스 부족 (CPU/메모리)
- PVC 바인딩 실패
- 노드 선택 실패 (nodeSelector, affinity)
- Taint/Toleration 불일치
진단 순서
1단계: Events 확인
kubectl describe pod <pod-name>
Events:
Type Reason Message
---- ------ -------
Warning FailedScheduling 0/3 nodes are available: insufficient cpu
해결 패턴
패턴 1: 리소스 부족
# 노드 리소스 확인
kubectl top nodes
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
node1 1950m 97% 7500Mi 93%
node2 1900m 95% 7200Mi 90%
해결:
- 리소스 요청 줄이기
- 노드 추가
- 불필요한 Pod 삭제
# 실행 예제
resources:
requests:
cpu: "100m" # 1000m → 100m
memory: "128Mi" # 512Mi → 128Mi
패턴 2: PVC 바인딩 실패
kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES
myapp-pvc Pending - - -
kubectl describe pvc myapp-pvc
Events:
Type Reason Message
---- ------ -------
Warning ProvisioningFailed storageclass "fast" not found
해결:
# 올바른 StorageClass 사용
storageClassName: standard # 또는 gp2, ebs-sc 등
패턴 3: 노드 선택 실패
kubectl describe pod <pod-name>
Events:
Warning FailedScheduling 0/3 nodes are available: node(s) didn't match node selector
해결:
# nodeSelector 제거 또는 수정
nodeSelector:
disktype: ssd # 이 레이블을 가진 노드가 없음
실무 체크리스트
빠른 진단 체크리스트
# 1. Pod 상태 확인
kubectl get pods
# 2. 로그 확인
kubectl logs <pod-name>
kubectl logs <pod-name> --previous
# 3. 상세 정보 확인
kubectl describe pod <pod-name>
# 4. 이벤트 확인
kubectl get events --sort-by=.metadata.creationTimestamp
# 5. 리소스 확인
kubectl top nodes
kubectl top pods
CrashLoopBackOff 체크리스트
- 애플리케이션 로그 확인 (
kubectl logs) - 환경 변수 확인 (ConfigMap, Secret)
- 의존성 확인 (데이터베이스, Redis 등)
- 헬스체크 설정 확인 (liveness/readiness probe)
- 리소스 제한 확인 (메모리 OOM)
- 컨테이너 명령 확인 (CMD, ENTRYPOINT)
ImagePullBackOff 체크리스트
- 이미지 이름 확인 (오타, 대소문자)
- 이미지 태그 확인 (존재 여부)
- 레지스트리 주소 확인
- 인증 정보 확인 (imagePullSecrets)
- 네트워크 확인 (레지스트리 접근 가능)
- 이미지 정책 확인 (imagePullPolicy)
Pending 체크리스트
- 노드 리소스 확인 (
kubectl top nodes) - PVC 바인딩 확인 (
kubectl get pvc) - nodeSelector 확인
- Taint/Toleration 확인
- Affinity 규칙 확인
- 노드 상태 확인 (
kubectl get nodes)
트러블슈팅 패턴
패턴 1: 데이터베이스 연결 실패
증상:
kubectl logs myapp-abc
Error: connect ECONNREFUSED 10.0.0.1:5432
원인:
- 데이터베이스 Service가 없음
- 환경 변수 오류
- 네트워크 정책 해결:
# Service 확인
kubectl get svc
# 환경 변수 확인
kubectl exec myapp-abc -- env | grep DATABASE
# 올바른 설정
env:
- name: DATABASE_URL
value: "postgresql://postgres:5432/mydb"
패턴 2: ConfigMap/Secret 누락
증상:
kubectl describe pod myapp-abc
Warning Failed Error: configmap "app-config" not found
해결:
# ConfigMap 생성
kubectl create configmap app-config \
--from-literal=API_URL=https://api.example.com
# Secret 생성
kubectl create secret generic app-secret \
--from-literal=API_KEY=your-api-key
패턴 3: 권한 문제
증상:
kubectl logs myapp-abc
Error: EACCES: permission denied, open '/app/data/file.txt'
해결:
# SecurityContext 설정
securityContext:
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
패턴 4: 헬스체크 타임아웃
증상:
kubectl describe pod myapp-abc
Liveness probe failed: Get "http://10.0.0.1:8080/health": context deadline exceeded
해결:
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 60 # 시작 대기 시간 증가
periodSeconds: 10
timeoutSeconds: 5 # 타임아웃 증가
failureThreshold: 3
마무리
Kubernetes Pod 트러블슈팅은 체계적인 진단 순서를 따르면 빠르게 해결할 수 있습니다. 핵심 원칙:
- Pod 상태 확인 (
kubectl get pods) - 로그 확인 (
kubectl logs) - 상세 정보 확인 (
kubectl describe pod) - 이벤트 확인 (
kubectl get events) - 리소스 확인 (
kubectl top) 자주 발생하는 에러:
- CrashLoopBackOff: 애플리케이션 로그 확인
- ImagePullBackOff: 이미지 이름, 인증 확인
- Pending: 리소스, PVC, 노드 확인 예방 방법:
- 헬스체크 설정 (liveness/readiness probe)
- 리소스 제한 설정 (requests/limits)
- 로깅 및 모니터링 (Prometheus, Grafana)
- 테스트 환경에서 충분히 검증 Docker 최적화는 Docker 멀티스테이지 빌드와 함께 보시면 도움이 됩니다.
심화 부록: 구현·운영 관점
이 부록은 앞선 본문에서 다룬 주제(「Kubernetes Pod 트러블슈팅 완벽 가이드 | CrashLoopBackOff·ImagePullBackOff 해결」)를 구현·런타임·운영 관점에서 다시 압축합니다. 도메인별 세부 구현은 글마다 다르지만, 입력 검증 → 핵심 연산 → 부작용(I/O·네트워크·동시성) → 관측의 흐름으로 장애를 나누면 원인 추적이 빨라집니다.
내부 동작과 핵심 메커니즘
flowchart TD A[입력·요청·이벤트] --> B[파싱·검증·디코딩] B --> C[핵심 연산·상태 전이] C --> D[부작용: I/O·네트워크·동시성] D --> E[결과·관측·저장]
sequenceDiagram participant C as 클라이언트/호출자 participant B as 경계(런타임·게이트웨이·프로세스) participant D as 의존성(API·DB·큐·파일) C->>B: 요청/이벤트 B->>D: 조회·쓰기·RPC D-->>B: 지연·부분 실패·재시도 가능 B-->>C: 응답 또는 오류(코드·상관 ID)
- 불변 조건(Invariant): 버퍼 경계, 프로토콜 상태, 트랜잭션 격리, FD 상한 등 단계별로 문장으로 적어 두면 디버깅 비용이 줄어듭니다.
- 결정성: 순수 층과 시간·네트워크·스케줄에 의존하는 층을 분리해야 테스트와 장애 분석이 쉬워집니다.
- 경계 비용: 직렬화, 인코딩, syscall 횟수, 락 경합, 할당·GC, 캐시 미스를 의심 목록에 둡니다.
- 백프레셔: 생산자가 소비자보다 빠를 때 버퍼·큐·스트림에서 속도를 줄이는 신호를 어디에 둘지 정의합니다.
프로덕션 운영 패턴
| 영역 | 운영 관점 질문 |
|---|---|
| 관측성 | 요청 단위 상관 ID, 에러율·지연 p95/p99, 의존성 타임아웃·재시도가 대시보드에 보이는가 |
| 안전성 | 입력 검증·권한·비밀·감사 로그가 코드 경로마다 일관적인가 |
| 신뢰성 | 재시도는 멱등 연산에만 적용되는가, 서킷 브레이커·백오프·DLQ가 있는가 |
| 성능 | 캐시·배치 크기·커넥션 풀·인덱스·백프레셔가 데이터 규모에 맞는가 |
| 배포 | 롤백 룬북, 카나리/블루그린, 마이그레이션·피처 플래그가 문서화되어 있는가 |
| 용량 | 피크 트래픽·디스크·FD·스레드 풀 상한을 주기적으로 검증하는가 |
스테이징은 데이터 양·네트워크 RTT·동시성을 프로덕션에 가깝게 맞출수록 재현율이 올라갑니다.
확장 예시: 엔드투엔드 미니 시나리오
앞선 본문 주제(「Kubernetes Pod 트러블슈팅 완벽 가이드 | CrashLoopBackOff·ImagePullBackOff 해결」)를 배포·운영 흐름에 맞춰 옮긴 체크리스트입니다. 도메인에 맞게 단계 이름만 바꿔 적용할 수 있습니다.
- 입력 계약 고정: 스키마·버전·최대 페이로드·타임아웃·에러 코드를 경계에 둔다.
- 핵심 경로 계측: 요청 ID, 단계별 지연, 외부 호출 결과 코드를 로그·메트릭·트레이스에서 한 흐름으로 본다.
- 실패 주입: 의존성 타임아웃·5xx·부분 데이터·락 대기를 스테이징에서 재현한다.
- 호환·롤백: 설정/마이그레이션/클라이언트 버전을 되돌릴 수 있는지 확인한다.
- 부하 후 검증: 피크 대비 p95/p99, 에러율, 리소스 상한, 알림 임계값을 점검한다.
handle(request):
ctx = newCorrelationId()
validated = validateSchema(request)
authorize(validated, ctx)
result = domainCore(validated)
persistOrEmit(result, idempotentKey)
recordMetrics(ctx, latency, outcome)
return result
문제 해결(Troubleshooting)
| 증상 | 가능 원인 | 조치 |
|---|---|---|
| 간헐적 실패 | 레이스, 타임아웃, 외부 의존성, DNS | 최소 재현 스크립트, 분산 트레이스·로그 상관관계, 재시도·서킷 설정 점검 |
| 성능 저하 | N+1, 동기 I/O, 락 경합, 과도한 직렬화, 캐시 미스 | 프로파일러·APM으로 핫스팟 확인 후 한 가지씩 제거 |
| 메모리 증가 | 캐시 무제한, 구독/리스너 누수, 대용량 버퍼, 커넥션 미반납 | 상한·TTL·힙/FD 스냅샷 비교 |
| 빌드·배포만 실패 | 환경 변수, 권한, 플랫폼 차이, lockfile | CI 로그와 로컬 diff, 런타임·이미지 버전 핀 |
| 설정 불일치 | 프로필·시크릿·기본값, 리전 | 스키마 검증된 설정 단일 소스와 배포 매트릭스 표준화 |
| 데이터 불일치 | 비멱등 재시도, 부분 쓰기, 캐시 무효화 누락 | 멱등 키·아웃박스·트랜잭션 경계 재검토 |
권장 순서: (1) 최소 재현 (2) 최근 변경 범위 축소 (3) 환경·의존성 차이 (4) 관측으로 가설 검증 (5) 수정 후 회귀·부하 테스트.
배포 전에는 git add → git commit → git push 후 npm run deploy 순서를 권장합니다.
자주 묻는 질문 (FAQ)
Q. 이 내용을 실무에서 언제 쓰나요?
A. Kubernetes Pod 트러블슈팅 완벽 가이드. CrashLoopBackOff, ImagePullBackOff, Pending 상태 해결. kubectl logs, describe, events로 원인 파악. 실… 실무에서는 위 본문의 예제와 선택 가이드를 참고해 적용하면 됩니다.
Q. 선행으로 읽으면 좋은 글은?
A. 각 글 하단의 이전 글 또는 관련 글 링크를 따라가면 순서대로 배울 수 있습니다. C++ 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.
Q. 더 깊이 공부하려면?
A. cppreference와 해당 라이브러리 공식 문서를 참고하세요. 글 말미의 참고 자료 링크도 활용하면 좋습니다.
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- CMake 에러 완벽 해결 가이드 | 빌드 실패·의존성·링커 에러 트러블슈팅
- C++ std::filesystem 완벽 가이드 | 경로·디렉토리·파일·권한 한 번에 정리
- C++ 프로덕션 배포 완벽 가이드 | Docker·systemd·K8s·모니터링·로깅 [#50-5]
이 글에서 다루는 키워드 (관련 검색어)
Kubernetes, k8s, Pod, 트러블슈팅, DevOps, kubectl, CrashLoopBackOff 등으로 검색하시면 이 글이 도움이 됩니다.