Docker 디버깅 — 컨테이너가 죽었을 때 원인을 찾는 방법
컨테이너가 시작되자마자 종료되거나, 갑자기 재시작되거나, 응답이 없습니다. 로그도 안 보이고 에러 메시지도 없을 때, 어디서부터 확인해야 할까요?
디버깅의 기본 흐름
컨테이너 문제 발생
│
├── 1. 상태 확인: docker ps -a
│
├── 2. 로그 확인: docker logs
│
├── 3. 상세 정보: docker inspect
│
├── 4. 리소스 확인: docker stats
│
├── 5. 내부 진입: docker exec
│
└── 6. 이벤트 확인: docker events
1단계: 컨테이너 상태 확인
# 실행 중인 컨테이너
docker ps
# 종료된 컨테이너 포함
docker ps -a
# 출력 예시
# CONTAINER ID IMAGE STATUS PORTS NAMES
# abc123 myapp Up 2 hours 8080 api
# def456 myworker Exited (137) 5 minutes ago worker
# ghi789 mydb Exited (1) 10 minutes ago db
종료 코드(Exit Code) 분석
| 코드 | 의미 |
|---|---|
| 0 | 정상 종료 |
| 1 | 일반적인 애플리케이션 에러 |
| 126 | 명령어 실행 불가 (퍼미션 문제) |
| 127 | 명령어를 찾을 수 없음 |
| 128+N | 시그널 N에 의한 종료 |
| 137 | 128+9(SIGKILL) — OOM Killer 또는 docker kill |
| 139 | 128+11(SIGSEGV) — 세그멘테이션 폴트 |
| 143 | 128+15(SIGTERM) — docker stop의 정상 종료 |
# 종료 코드 확인
docker inspect --format '{{.State.ExitCode}}' mycontainer
# 137
# OOM Killed 여부 확인
docker inspect --format '{{.State.OOMKilled}}' mycontainer
# true
2단계: 로그 확인
# 전체 로그
docker logs mycontainer
# 실시간 로그 follow
docker logs -f mycontainer
# 최근 100줄
docker logs --tail 100 mycontainer
# 타임스탬프 포함
docker logs -t mycontainer
# 특정 시간 이후
docker logs --since "2026-03-19T10:00:00" mycontainer
docker logs --since 30m mycontainer
# 특정 기간
docker logs --since 1h --until 30m mycontainer
로그가 안 보일 때
# 로그 드라이버 확인
docker inspect --format '{{.HostConfig.LogConfig.Type}}' mycontainer
# json-file, syslog, journald, none 등
# json-file이 아니면 docker logs로 볼 수 없음
# 로그 파일 직접 확인
cat /var/lib/docker/containers/<container-id>/<container-id>-json.log
애플리케이션이 stdout/stderr가 아닌 파일에 로그를 남기는 경우:
# 컨테이너 내부의 로그 파일 직접 확인
docker exec mycontainer cat /var/log/app/error.log
# 또는 로그 파일 복사
docker cp mycontainer:/var/log/app/error.log ./error.log
3단계: 상세 정보 확인 (docker inspect)
# 전체 정보 (JSON)
docker inspect mycontainer | jq
# 자주 사용하는 필드들
# 상태 정보
docker inspect --format '{{json .State}}' mycontainer | jq
# {
# "Status": "exited",
# "Running": false,
# "Pid": 0,
# "ExitCode": 137,
# "Error": "",
# "StartedAt": "2026-03-19T10:00:00Z",
# "FinishedAt": "2026-03-19T10:30:00Z",
# "OOMKilled": true
# }
# IP 주소
docker inspect --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mycontainer
# 마운트된 볼륨
docker inspect --format '{{json .Mounts}}' mycontainer | jq
# 환경변수
docker inspect --format '{{json .Config.Env}}' mycontainer | jq
# 포트 매핑
docker inspect --format '{{json .NetworkSettings.Ports}}' mycontainer | jq
# 헬스체크 상태
docker inspect --format '{{json .State.Health}}' mycontainer | jq
4단계: 리소스 모니터링
# 실시간 리소스 사용량
docker stats
# 특정 컨테이너만
docker stats mycontainer
# CONTAINER CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O
# mycontainer 45.2% 384MiB / 512MiB 75.0% 12.5MB / 8MB 50MB / 20MB
# 한 번만 출력 (스크립팅용)
docker stats --no-stream mycontainer
메모리 문제 진단
# 메모리 제한과 현재 사용량
docker inspect --format '{{.HostConfig.Memory}}' mycontainer
# 536870912 (512MB)
docker stats --no-stream --format "{{.MemUsage}}" mycontainer
# 480MiB / 512MiB ← 거의 한계에 도달
# cgroup에서 직접 확인 (호스트에서)
cat /sys/fs/cgroup/memory/docker/<container-id>/memory.usage_in_bytes
5단계: 컨테이너 내부 진입
실행 중인 컨테이너
# 셸로 접속
docker exec -it mycontainer /bin/sh
# 또는 bash가 있으면
docker exec -it mycontainer /bin/bash
# 특정 사용자로 실행
docker exec -it --user root mycontainer /bin/sh
# 일회성 명령 실행
docker exec mycontainer cat /etc/hosts
docker exec mycontainer env
docker exec mycontainer ps aux
docker exec mycontainer netstat -tlnp
종료된 컨테이너 디버깅
# 종료된 컨테이너에서 파일 복사
docker cp mycontainer:/var/log/app.log ./app.log
docker cp mycontainer:/app/config.json ./config.json
# 같은 이미지로 새 컨테이너를 만들어 디버깅
docker run -it --entrypoint /bin/sh myapp:latest
# 종료된 컨테이너를 커밋하여 이미지로 만들기
docker commit mycontainer debug-image
docker run -it --entrypoint /bin/sh debug-image
디버그 컨테이너 (sidecar)
최소 이미지(distroless 등)에는 셸이 없어서 exec로 접속할 수 없습니다. 이때 디버그 컨테이너를 사용합니다.
# 같은 네트워크에 디버그 컨테이너 연결
docker run -it --rm \
--network container:mycontainer \
nicolaka/netshoot \
bash
# 내부에서 네트워크 디버깅
curl http://localhost:8080/health
tcpdump -i eth0
nslookup target-service
6단계: 이벤트 확인
# 실시간 Docker 이벤트 모니터링
docker events
# 특정 컨테이너의 이벤트
docker events --filter container=mycontainer
# 특정 이벤트 타입 필터
docker events --filter event=die
docker events --filter event=oom
# 시간 범위
docker events --since "2026-03-19T10:00:00" --until "2026-03-19T11:00:00"
이벤트 예시:
2026-03-19T10:30:00 container die abc123 (exitCode=137, image=myapp)
2026-03-19T10:30:00 container oom abc123 (image=myapp)
자주 발생하는 문제와 해결
컨테이너가 시작 즉시 종료
# 원인 1: CMD/ENTRYPOINT가 포그라운드가 아님
# 나쁜 예
CMD nginx # 백그라운드로 실행되어 즉시 종료
# 좋은 예
CMD ["nginx", "-g", "daemon off;"]
# 원인 2: 설정 파일 오류
docker logs mycontainer
# nginx: [emerg] unknown directive "servr" in /etc/nginx/nginx.conf:1
# 원인 3: 의존 서비스 미연결
docker logs mycontainer
# Error: connect ECONNREFUSED 172.17.0.2:5432
OOMKilled (메모리 초과)
# 확인
docker inspect --format '{{.State.OOMKilled}}' mycontainer
# true
# 해결 1: 메모리 제한 늘리기
docker run --memory 1g myapp
# 해결 2: 메모리 사용 패턴 분석
docker stats mycontainer # 시간에 따른 메모리 증가 패턴 확인
# 해결 3: JVM 등 런타임 메모리 설정 조정
docker run --memory 512m -e JAVA_OPTS="-Xmx384m -Xms256m" myjava-app
네트워크 연결 실패
# DNS 해석 확인
docker exec mycontainer nslookup target-service
# 네트워크 연결 확인
docker exec mycontainer wget -qO- http://target-service:8080/health
# 같은 네트워크에 있는지 확인
docker network inspect mynetwork | jq '.[0].Containers'
# 포트가 열려있는지 확인
docker exec mycontainer nc -zv target-service 8080
퍼미션 거부
# 로그 확인
docker logs mycontainer
# Permission denied: '/app/data/file.txt'
# 컨테이너 내부 파일 소유자 확인
docker exec mycontainer ls -la /app/data/
# 해결: 소유권 수정 또는 올바른 사용자로 실행
docker run --user $(id -u):$(id -g) -v $(pwd)/data:/app/data myapp
디스크 공간 부족
# Docker 디스크 사용량 확인
docker system df
docker system df -v
# 사용하지 않는 리소스 정리
docker system prune # 중지된 컨테이너, 미사용 네트워크, dangling 이미지
docker system prune -a # 사용하지 않는 모든 이미지 포함
docker volume prune # 미사용 볼륨
docker builder prune # 빌드 캐시
디버깅 명령어 치트시트
# 상태 확인
docker ps -a
docker inspect <container>
docker stats
# 로그
docker logs -f --tail 100 <container>
docker logs --since 10m <container>
# 내부 접근
docker exec -it <container> /bin/sh
docker cp <container>:/path/to/file ./local-file
# 네트워크
docker network inspect <network>
docker exec <container> nslookup <target>
# 이벤트
docker events --filter container=<container>
# 정리
docker system df
docker system prune -a
정리
- 디버깅은 상태 → 로그 → inspect → stats → exec → events 순서로 접근합니다.
- 종료 코드를 확인하면 문제의 종류를 빠르게 파악할 수 있습니다. 137은 OOM, 143은 정상 종료 시그널입니다.
- docker inspect의 State 필드에서 OOMKilled, ExitCode, Error 등 핵심 정보를 얻습니다.
- distroless 같은 최소 이미지에서는 nicolaka/netshoot 같은 디버그 컨테이너를 sidecar로 붙여서 진단합니다.
- 종료된 컨테이너에서는
docker cp로 파일을 꺼내거나,docker commit으로 이미지로 만들어 분석합니다.
댓글 로딩 중...