본문 바로가기

복사금지 블로그 짜증나서 만든 개발문서

도커 서버 디스크가 꽉 찼다면, 컨테이너 로그가 무제한으로 쌓이고 있을 수 있습니다

반응형

환경  Docker Engine · json-file 로깅 드라이버 · 2026-10-02 확인

몇 달 잘 돌던 서버가 No space left on device 로 멈추는 흔한 원인 하나를 따라가 봅니다. json-file 로그가 무제한으로 커지는 이유, daemon.json 을 고쳐도 기존 컨테이너엔 안 먹는 이유, 컨테이너를 새로 만들어 공간을 되찾는 순서까지.

디스크는 찼는데 du 로 훑어도 눈에 띄는 게 없다

처음엔 늘 업로드 폴더나 DB 데이터를 의심한다. 그런데 df -h 로 보면 루트가 100% 인데, 자주 보던 폴더들을 du 로 재 봐도 크기가 예전과 비슷하다. 그러다 /var/lib/docker 를 재 보면 수십 기가가 나온다.

이때 docker system prune 으로 안 쓰는 이미지와 빌드 캐시를 먼저 치우곤 한다. 몇 기가가 돌아오긴 하지만 며칠 지나면 다시 찬다. 줄어든 건 이미지였고, 계속 자라는 건 다른 곳이기 때문이다.

컨테이너 로그는 /var/lib/docker/containers/<컨테이너 ID>/ 아래에 <ID>-json.log 라는 이름으로 쌓인다. 이 파일들 크기만 따로 재 보면 범인이 바로 나온다. 하나만 유난히 큰 경우가 대부분이다. 디버그 로그를 켜 둔 앱이나, 헬스체크마다 접근 로그를 찍는 웹 서버 쪽이다.

df -h /
sudo du -sh /var/lib/docker

# 컨테이너별 로그 파일 크기, 큰 순서로
sudo sh -c 'du -h /var/lib/docker/containers/*/*-json.log' | sort -h | tail -5

# 어떤 컨테이너 것인지 ID 로 이름 찾기
docker ps -a --no-trunc --format '{{.ID}}  {{.Names}}' | grep <ID 앞부분>

로그 파일만 따로 재서 어느 컨테이너인지 찾는다

이미지를 치워도 금방 다시 찬다면, 컨테이너 로그 파일을 따로 재 본다.

기본 드라이버는 로그를 돌려 쓰지 않는다

도커는 컨테이너의 표준 출력과 표준 에러를 받아 JSON 형식 파일로 남긴다. 따로 정하지 않으면 이 일을 json-file 드라이버가 맡는다. 문제는 이 드라이버의 기본값이다. 파일 하나의 최대 크기인 max-size 가 -1, 즉 무제한이고, 남길 파일 개수 max-file 은 1 이다.

풀어 말하면 파일 하나에 끝없이 이어 쓴다는 얘기다. 크기가 차도 새 파일로 넘기지 않고 오래된 걸 지우지도 않는다. 로그를 조금만 찍는 컨테이너라면 몇 년이 가도 티가 안 나지만, 요청마다 몇 줄씩 찍는 서비스는 몇 달이면 디스크 하나를 채운다.

도커 문서도 이 점을 인정한다. 대부분의 경우엔 local 드라이버를 쓰라고 권하고, json-file 이 아직 기본인 건 예전 버전과의 호환과 쿠버네티스 쪽 사정 때문이라고 적어 둔다. local 드라이버는 기본으로 파일당 20MB, 5개까지 돌려 쓰고 지난 파일은 압축한다. 컨테이너 하나가 로그로 차지하는 양이 100MB 언저리에서 멈춘다.

지금 컨테이너가 어느 드라이버로, 어떤 옵션으로 돌고 있는지는 docker inspect 로 확인한다. Type 이 json-file 인데 Config 가 비어 있다면 무제한으로 쌓이고 있다는 뜻이다.

docker inspect -f '{{.Name}}  {{.HostConfig.LogConfig.Type}}  {{json .HostConfig.LogConfig.Config}}' $(docker ps -q)
# /web  json-file  {}                              ← 제한 없음
# /api  json-file  {"max-file":"3","max-size":"10m"} ← 돌려 쓰는 중

돌고 있는 컨테이너마다 로깅 설정을 찍어 본다

json-file 의 max-size 기본값은 -1 이다. 아무것도 안 정하면 로그 파일 하나가 끝없이 커진다.

daemon.json 을 고쳤는데 그대로인 이유

원인을 알면 대개 /etc/docker/daemon.json 에 log-opts 를 넣고 도커를 재시작한다. 여기까지는 맞다. 그런데 하루가 지나도 그 큰 파일이 계속 자란다. 설정을 잘못 쓴 게 아닌가 다시 열어 보게 되는 지점이다.

데몬 설정의 로깅 값은 바뀐 뒤에 새로 만드는 컨테이너에만 적용된다. 이미 만들어진 컨테이너는 생성될 때 받은 로깅 설정을 계속 들고 있다. 도커를 재시작해도, 컨테이너를 restart 해도 마찬가지다. restart 는 같은 컨테이너를 다시 켜는 것일 뿐 새로 만드는 게 아니다.

또 하나 흔한 실수가 따옴표다. daemon.json 의 log-opts 값은 전부 문자열이어야 한다. max-file 을 "3" 이 아니라 3 으로 쓰면 도커 데몬이 설정을 거부하고, 재시작 자체가 실패할 수 있다. 서버에서 이 파일을 고칠 땐 재시작 직후 상태부터 확인한다.

{
  "log-driver": "local",
  "log-opts": {
    "max-size": "20m",
    "max-file": "5"
  }
}

/etc/docker/daemon.json — 숫자도 따옴표로 감싼다. json-file 을 유지하려면 log-driver 만 json-file 로

컨테이너를 새로 만들어야 설정이 붙는다

결국 할 일은 컨테이너를 지우고 다시 만드는 것이다. compose 로 띄운 서비스라면 docker compose up -d --force-recreate 가 그 일을 한다. 컨테이너를 지우면 그 컨테이너의 로그 파일도 같이 사라지니, 쌓인 공간도 이때 돌아온다.

남겨야 할 로그가 있다면 지우기 전에 docker logs 로 필요한 구간만 떠 둔다. 수십 기가를 통째로 복사하면 그 복사본이 또 디스크를 채운다. --since 로 최근 며칠만 받아 두는 편이 현실적이다.

급하다고 로그 파일을 직접 비우거나 지우고 싶어진다. 도커 문서는 이 파일을 도커 데몬만 다루도록 만들었고, 바깥 도구로 건드리면 로깅이 꼬일 수 있다고 적어 둔다. 공간이 정말 1% 도 없어서 컨테이너를 다시 만들 여유조차 없을 때가 아니라면 재생성으로 푼다.

compose 파일에 서비스별 logging 항목을 적어 두면 데몬 설정과 상관없이 그 서비스는 정한 대로 돈다. 서버를 옮기거나 새로 깔 때 daemon.json 을 빠뜨려도 같은 사고가 안 난다.

# 필요한 구간만 떠 두고
docker logs --since 72h web > ~/web-$(date +%F).log 2>&1

# 컨테이너를 새로 만든다 (기존 로그 파일도 함께 사라진다)
docker compose up -d --force-recreate web

# 새 설정이 붙었는지 확인
docker inspect -f '{{.HostConfig.LogConfig.Type}} {{json .HostConfig.LogConfig.Config}}' $(docker compose ps -q web)

restart 가 아니라 재생성. 끝나면 inspect 로 다시 본다

restart 는 같은 컨테이너를 다시 켤 뿐이다. 로깅 설정을 바꾸려면 컨테이너를 새로 만들어야 한다.

같은 일을 다시 겪지 않으려면

새 서버를 세팅하는 스크립트에 daemon.json 을 넣는 단계를 박아 둔다. 도커를 깔고 컨테이너를 띄운 다음에 넣으면 앞에서 본 것처럼 기존 컨테이너엔 안 먹는다. 순서가 중요하다.

compose 파일에는 서비스마다 logging 을 적는다. 반복이 싫으면 YAML 앵커로 한 번 정의해 두고 가져다 쓰면 된다. 데몬 설정이 빠진 서버에서 띄워도 이 서비스들은 제한 안에서 돈다.

디스크 사용량 경보는 루트 파티션 80% 쯤에 걸어 둔다. 로그는 하루아침에 차지 않는다. 몇 주에 걸쳐 천천히 오르니 경보만 있으면 재생성할 시간이 충분하다.

x-logging: &default-logging
  driver: local
  options:
    max-size: "20m"
    max-file: "5"

services:
  web:
    image: nginx:stable
    logging: *default-logging
  api:
    build: .
    logging: *default-logging

서비스마다 로깅 제한을 박아 두면 데몬 설정에 기대지 않는다

조치 후 확인할 것

  • docker inspect 로 모든 컨테이너의 LogConfig 를 찍어 json-file 에 Config 가 빈 것이 없는지 본다
  • daemon.json 의 log-opts 값이 전부 따옴표로 감싼 문자열인지 확인한다
  • 설정을 바꾼 뒤에는 restart 가 아니라 docker compose up -d --force-recreate 로 컨테이너를 새로 만든다
  • compose 파일 서비스마다 logging 항목이 들어가 있는지 본다
  • 루트 파티션 사용량 경보가 80% 근처에 걸려 있는지 확인한다

이 사고는 원인을 알고 나면 허무할 만큼 단순하다. 기본값이 무제한이고, 설정은 새 컨테이너에만 붙는다. 그런데 몇 달에 걸쳐 천천히 차오르니 아무도 연결을 못 짓는다. 디스크가 찼는데 이미지를 치워도 금방 다시 찬다면 /var/lib/docker/containers 를 먼저 재 보자.

반응형