← 전체 글로 돌아가기

Docker

Docker 로그의 마지막 줄만 보면 놓치는 것들

컨테이너가 재시작 루프에 빠졌을 때 최신 로그만 보면 원인을 찾기 어렵다. 로그를 어떻게 읽어야 하는지 정리했다.

컨테이너가 이상하게 동작한다고 연락이 왔을 때, docker logs container-name을 치면 가장 최근 로그가 나온다. 문제는 이 마지막 줄이 원인이 아니라 결과인 경우가 많다는 것이다.

재시작 루프 예시

$ docker logs myapp
[2026-06-23 08:01:22] Server starting on :3000
[2026-06-23 08:01:23] Connected to database
[2026-06-23 08:01:45] Server starting on :3000
[2026-06-23 08:01:46] Connected to database
[2026-06-23 08:01:50] Server starting on :3000

마지막 줄만 보면 "서버가 정상적으로 시작하는 중"처럼 보인다. 그런데 실제로는 계속 죽고 다시 시작하는 중이다. docker ps로 재시작 횟수를 먼저 본다.

docker ps
# RESTARTS 컬럼이 계속 늘고 있다면 루프에 빠진 것

충분한 양의 로그를 본다

기본 docker logs는 전체 로그를 출력한다. 컨테이너가 오래 됐다면 너무 많다. --tail로 줄이되, 너무 짧게 자르면 중요한 맥락을 놓친다.

# 최근 200줄
docker logs --tail=200 container-name

# 타임스탬프 포함
docker logs --tail=200 -t container-name

# 실시간 추적
docker logs -f container-name

재시작 루프라면 한 사이클의 로그가 몇 줄인지 파악하고, 그 배수로 --tail을 잡아야 에러 발생 시점을 볼 수 있다.

죽기 직전의 로그가 중요하다

컨테이너가 비정상 종료(exit code != 0)했을 때의 로그를 보려면 현재 실행 중인 컨테이너가 아니라 죽은 컨테이너의 로그가 필요하다.

# 죽은 컨테이너 포함 목록 보기
docker ps -a

# 직전에 죽은 컨테이너 ID로 로그 확인
docker logs <죽은-컨테이너-ID>

Docker Swarm이나 Compose의 재시작 정책이 설정돼 있으면 죽은 컨테이너가 바로 교체된다. 이때 새 컨테이너의 로그에는 이전 컨테이너가 왜 죽었는지가 없다.

docker inspect로 종료 정보 확인

docker inspect container-name | jq '.[0].State'
{
  "Status": "exited",
  "ExitCode": 137,
  "Error": "",
  "FinishedAt": "2026-06-23T08:01:48Z"
}

ExitCode 137은 OOM(메모리 부족)으로 강제 종료됐을 가능성이 높다. ExitCode 1은 애플리케이션 자체 오류다. 종료 코드를 보면 로그를 어느 방향으로 읽어야 할지 실마리가 생긴다.

정리

로그는 가장 나중 줄이 아니라 이상 신호가 처음 나온 시점을 찾아야 한다. 재시작 루프라면 직전 사이클의 종료 직전 로그, 단순 오류라면 ERROR 레벨 로그가 처음 등장하는 지점. 로그를 거꾸로 읽어서 원인을 찾는 습관이 디버깅 시간을 줄여준다.