Theme:

컨테이너가 시작되자마자 종료되거나, 갑자기 재시작되거나, 응답이 없습니다. 로그도 안 보이고 에러 메시지도 없을 때, 어디서부터 확인해야 할까요?

디버깅의 기본 흐름

PLAINTEXT
컨테이너 문제 발생

  ├── 1. 상태 확인: docker ps -a

  ├── 2. 로그 확인: docker logs

  ├── 3. 상세 정보: docker inspect

  ├── 4. 리소스 확인: docker stats

  ├── 5. 내부 진입: docker exec

  └── 6. 이벤트 확인: docker events

1단계: 컨테이너 상태 확인

BASH
# 실행 중인 컨테이너
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에 의한 종료
137128+9(SIGKILL) — OOM Killer 또는 docker kill
139128+11(SIGSEGV) — 세그멘테이션 폴트
143128+15(SIGTERM) — docker stop의 정상 종료
BASH
# 종료 코드 확인
docker inspect --format '{{.State.ExitCode}}' mycontainer
# 137

# OOM Killed 여부 확인
docker inspect --format '{{.State.OOMKilled}}' mycontainer
# true

2단계: 로그 확인

BASH
# 전체 로그
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

로그가 안 보일 때

BASH
# 로그 드라이버 확인
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가 아닌 파일에 로그를 남기는 경우:

BASH
# 컨테이너 내부의 로그 파일 직접 확인
docker exec mycontainer cat /var/log/app/error.log

# 또는 로그 파일 복사
docker cp mycontainer:/var/log/app/error.log ./error.log

3단계: 상세 정보 확인 (docker inspect)

BASH
# 전체 정보 (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단계: 리소스 모니터링

BASH
# 실시간 리소스 사용량
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

메모리 문제 진단

BASH
# 메모리 제한과 현재 사용량
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단계: 컨테이너 내부 진입

실행 중인 컨테이너

BASH
# 셸로 접속
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

종료된 컨테이너 디버깅

BASH
# 종료된 컨테이너에서 파일 복사
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로 접속할 수 없습니다. 이때 디버그 컨테이너를 사용합니다.

BASH
# 같은 네트워크에 디버그 컨테이너 연결
docker run -it --rm \
    --network container:mycontainer \
    nicolaka/netshoot \
    bash

# 내부에서 네트워크 디버깅
curl http://localhost:8080/health
tcpdump -i eth0
nslookup target-service

6단계: 이벤트 확인

BASH
# 실시간 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"

이벤트 예시:

PLAINTEXT
2026-03-19T10:30:00 container die abc123 (exitCode=137, image=myapp)
2026-03-19T10:30:00 container oom abc123 (image=myapp)

자주 발생하는 문제와 해결

컨테이너가 시작 즉시 종료

BASH
# 원인 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 (메모리 초과)

BASH
# 확인
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

네트워크 연결 실패

BASH
# 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

퍼미션 거부

BASH
# 로그 확인
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

디스크 공간 부족

BASH
# Docker 디스크 사용량 확인
docker system df
docker system df -v

# 사용하지 않는 리소스 정리
docker system prune        # 중지된 컨테이너, 미사용 네트워크, dangling 이미지
docker system prune -a     # 사용하지 않는 모든 이미지 포함
docker volume prune        # 미사용 볼륨
docker builder prune       # 빌드 캐시

디버깅 명령어 치트시트

BASH
# 상태 확인
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으로 이미지로 만들어 분석합니다.
댓글 로딩 중...