2026년 2월 22일

Docker 로그는 어디에 저장되는걸까?

docker logs는 어디서 오는 걸까?

컨테이너를 실행하다가 에러가 나면 우리는 습관처럼 아래 명령어를 입력한다.
docker logs <container-name>
 
그러면 방금 전 애플리케이션에서 찍힌 로그들이 주르륵 출력된다.
notion image
 
그런데 문득 이런 생각이 들었다.
“이 로그는 대체 어디에 저장되어 있는거고, 도커는 어떻게 이걸 보여주는 걸까?”
 
컨테이너는 “격리된 환경” 이라고 배웠는데, 그 안에서 찍힌 로그를 호스트에서 어떻게 읽을 수 있는 걸까?
오늘 글에서는 docker logs의 정체를 파헤쳐보려 한다.
 

로그는 어디에 저장되는가?

Docker에는 실행 중인 컨테이너에서 정보를 얻는 데 도움이 되는 여러 로깅 메커니즘이 포함되어 있다.
이러한 메커니즘을 로깅 드라이버라고 한다.
 
기본적으로 도커는 별도의 설정을 하지 않으면 json-file 로깅 드라이버를 사용한다.
저장 경로는 호스트 OS의 /var/lib/docker/containers/<container-id>/ 디렉토리 내에 존재하고, 파일 명은 <container-id>-json.log 라는 이름의 JSON 형식 파일로 저장된다.
 
ls /var/lib/docker/containers
notion image
  • 6개의 컨테이너 로그가 존재한다.
 
cat <container-id>
notion image
  • 로그가 JSON 형태로 저장되는 것을 확인할 수 있다.
 

어떤 원리로 저장되는가?

도커 로그가 저장되는 원리는 마치 중간에서 가로채는 도청 장치와 비슷하다.
애플리케이션이 화면에 글자를 내보내는 그 순간을 도커 엔진이 포착해서 파일로 기록한다.
 

1. 표준 스트림 (Standard Streams) 포착

모든 프로세스는 기본적으로 두 가지 출력 통로를 가진다.
  • stdout (Standard Output): 정상적인 실행 정보 (예: print, console.log)
  • stderr (Standard Error): 에러나 경고 메시지
 
도커 컨테이너 내부에서 실행되는 애플리케이션이 이 통로로 데이터를 내보내면, 도커 데몬(Docker Daemon)이 이 스트림을 끝단에서 기다리고 있다가 전부 읽어들인다.
 

2. 로깅 드라이버(Logging Driver)의 개입

도커 데몬은 가로챈 텍스트 데이터를 그대로 두지 않고, 설정된 로깅 드라이버에게 넘긴다.
기본값인 json-file 드라이버는 데이터를 다음과 같이 가공한다.
  • 메타데이터 부착: 로그가 발생한 정확한 시간(time), 어떤 통로(stdout/stderr)였는지 정보를 붙인다.
  • JSON 직렬화: 데이터를 아래와 같은 정형화된 포맷으로 변환한다.
    • {"log":"Hello World!\n","stream":"stdout","time":"2026-02-22T12:45:00Z"}
 

3. 호스트 파일 시스템에 기록

가공된 JSON 데이터는 이제 호스트 OS의 특정 위치에 물리적인 파일로 저장된다.
  • 기본값: /var/lib/docker/containers/<컨테이너ID>/<컨테이너ID>-json.log
 
애플리케이션이 로그를 stdout으로 내보내지 않고 내부 파일(예: /app/logs/error.log)에 직접 쓰게 된다면, 도커 엔진은 중간에서 가로챌 데이터가 없으므로 로그를 수집할 수 없다.
 

로그 파일이 무한정 커지면 어떻게 될까?

당연한 이야기이겠지만 로그 파일이 무한정 커지면 호스트 OS의 디스크 용량을 전부 차지하게 된다.
docker logs 명령어를 실행할 때 수 GB짜리 파일을 읽어야 하므로 일반적인 에디터로 열리지도 않아 장애 대응 시 로그 분석이 불가능해진다.
 
만약 지금 당장 디스크가 꽉 찼다면, 컨테이너를 지우지 않고 로그 내용만 비우는 ‘응급 처치’ 방법도 있다.
# 특정 컨테이너의 로그 파일 경로를 찾아 내용을 비웁니다 (0바이트로 만듦) truncate -s 0 $(docker inspect --format='{{.LogPath}}' <컨테이너_이름>)
 

로그 로테이션 (Log Roation)

도커는 로그 파일의 최대 크기저장할 파일 개수를 제한하는 기능을 제공한다. 이를 설정하면 가장 오래된 로그부터 자동으로 지우며 새 로그를 쌓는다.
 
방법 A: 특정 컨테이너에 적용 (docker run)
컨테이너를 실행할 때 옵션을 추가한다. 아래 예시는 로그 파일을 최대 10MB로 제한하고, 3개까지만 보관하도록 설정한다.
docker run -d \ --log-driver json-file \ --log-opt max-size=10m \ --log-opt max-file=3 \ nginx
 
방법 B: 도커 데몬 전체에 적용
모든 컨테이너에 일일이 설정하기 번거롭다면, 도커 설정 파일에 기본값을 지정하는 방식도 존재한다.
  1. 파일 수정: sudo nano /etc/docker/daemon.json
  1. 내용 추가:
    1. { "log-driver": "json-file", "log-opts": { "max-size": "20m", "max-file": "5" } }
  1. 도커 재시작: sudo systemctl restart docker
 

컨테이너를 내리면 로그가 삭제될까?

  1. 컨테이너를 중지(Stop)했을 때
      • 삭제 되지 않는다.
      • docker stop은 프로세스만 종료시킨 상태이므로, 컨테이너 객체는 호스트의 디스크에 그대로 남아있다.
      • 따라서 docker logs 명령어로 이전 로그를 여전히 확인할 수 있다.
  1. 컨테이너를 삭제(Remove)했을 때
      • 로그는 삭제된다.
      • docker rm 명령어를 사용하거나, docker run -rm 옵션으로 실행된 컨테이너가 종료되어 자동으로 삭제되면 로그 파일도 함께 삭제된다.
      • 도커 로그는 컨테이너의 라이프사이클과 운명을 같이한다.
 

로그를 유실하지 않으려면?

컨테이너가 삭제되더라도 로그를 보존하고 싶다면 주로 다음과 같은 방법을 사용한다.
 
방법 A: 호스트 볼륨 연결 (Volume Mount)
애플리케이션이 로그를 파일로 남긴다면, 호스트의 특정 디렉토리와 컨테이너를 연결한다.
이렇게 하면 컨테이너가 삭제 되어도 /home/user/logs 에 로그가 남는다.
docker run -v /home/user/logs:/app/logs my-app
 
방법 B: 로그 수집기 활용 (Logging Driver)
로그를 파일로 저장하지 않고 외부 서비스(CloudWatch, ELK, Fluentd 등)로 즉시 쏴주는 방식이다.
 
운영 환경에서 docker logs 에만 의존하다가 컨테이너를 재배포(삭제 후 재생성)하면 과거 기록이 모두 사라지는 대참사가 발생할 수 있다. 그래서 중요한 서비스라면 반드시 외부 저장소 설정을 병행해야 한다. (Cloud-native pattern)
 

json-file 방식의 치명적인 한계

docker logs로 확인하는 json-file 방식은 소규모 프로젝트에는 좋지만, 서비스 규모가 커지면 다음과 같은 벽에 부딪힌다.
  • 로그 휘발성: 컨테이너를 삭제(rm)하면 로그도 영구 삭제된다. 장애 원인을 파악해야 하는데 증거가 사라지는 셈이다.
  • 검색의 어려움: 서버가 100대라면, 에러를 찾기 위해 서버에 일일이 접속해서 docker logs를 쳐야 한다.
  • 디스크 압박: 로그 파일이 호스트 디스크를 가득 채워 서비스 전체를 마비시킬 위험이 높다.
  • 비정형 데이터: 로그는 단순 텍스트이다. “최근 1시간 동안 500 에러가 몇 번 발생했지?”라는 통계를 내기가 매우 어렵다.
 

외부 로그 수집기(ELK Stack)를 쓰는 이유

ELK Stack을 사용하면 로그의 흐름이 [생성 → 수집 → 저장 → 시각화] 의 파이프라인을 타게 된다.
단계
도구
실무에서의 역할
수집 (Ship)
Logstash / Fluentd
각 컨테이너의 stdout을 실시간으로 낚아채서 중앙 서버로 보냅니다.
저장/검색 (Store)
Elasticsearch
방대한 로그 데이터를 인덱싱(색인)하여 0.1초 만에 검색 가능하게 만듭니다.
시각화 (Visualize)
Kibana
로그 데이터를 차트나 그래프로 그려줍니다. (예: 에러 발생률 그래프)
이에 대한 이점은 다음과 같다.
  1. 중앙 집중형 관리: 어떤 컨테이너에서 로그가 발생하든 한 곳에서 모두 볼 수 있다. 서버 IP를 몰라도 서비스 이름만으로 로그 검색이 가능해진다.
  1. 실시간 알람: “에러 로그가 1분에 10번 이상 발생하면 담당자에게 슬랙(Slack) 메시지를 보내라”는 식의 자동화가 가능하다. 장애 대응 속도가 압도적으로 빨라진다.
  1. 로그 보존 및 법적 근거: 컨테이너가 죽거나 사라져도 로그는 별도의 스토리지에 안전하게 보관된다. 이는 보안 사고 발생 시 추적을 위한 법적 근거로도 활용된다.
  1. 데이터 분석: 단순히 “에러 찾기”를 넘어, “사용자가 주로 어떤 페이지를 보는지”, “응답 시간이 느려지는 구간은 어디인지” 같은 비즈니스 인사이트를 추출할 수 있다.
 

번외: PLG Stack

  • ELK Stack보다 더 가벼운 PLG Stack도 존재한다고 한다.
 
구성 요소
역할
비유
Promtail
로그 수집기 (컨테이너 로그를 낚아채서 Loki로 전송)
우체부
Loki
로그 저장소 (데이터를 압축 저장하고 쿼리 처리)
우체국 물류 창고
Grafana
시각화 도구 (로그를 검색하고 대시보드 출력)
수신용 모니터
ELK와 Loki의 가장 큰 차이점은 인덱싱 방식이다.
  • ELK (전체 인덱싱): 로그의 모든 단어를 인덱싱한다. 검색은 빠르지만, 저장 용량을 엄청나게 차지하고 CPU와 메모리를 많이 쓴다.
  • Loki (레이블 인덱싱): 로그 본문은 그냥 압축해서 쌓아두기만 하고, ‘어떤 컨테이너인가?’, ‘어떤 앱인가?’ 같은 레이블(Label) 정보만 인덱싱한다.
 
Loki의 장점은 다음과 같다.
  • 가벼움: 로그 본문을 다 뒤지지 않고 레이블만 관리하므로, Elasticsearch에 비해 디스크 사용량이 1/10 수준으로 낮고 운영 비용이 저렴하다.
  • Grafana와의 완벽합 궁합: 이미 서버 모니터링을 위해 Grafana를 쓰고 있다면, 별도의 툴(Kibana)을 배울 필요 없이 한 화면에서 메트릭과 로그를 동시에 볼 수 있다.
    • 예시: "서버 CPU가 튀었네? (메트릭) -> 그 시점에 어떤 로그가 찍혔지? (Loki)"
  • 쿠버네티스 친화적: Loki는 쿠버네티스의 ‘Pod 레이블’을 그대로 가져와서 로그 인덱스로 활용한다. 클라우드 네이티브 환경에 최적화되어 있다.
 
Loki의 단점은 다음과 같다.
  • 전문화된 검색의 한계: 로그 본문의 특정 단어로 복잡한 통계를 내야 한다면(예: “모든 로그 중 ‘error’ 단어의 빈도 분석”), 모든 본문을 인덱싱하는 Elasticsearch보다 느릴 수밖에 없다.
  • 단순 검색 위주: 주로 “장애 발생 시 특정 시간대의 로그를 확인”하는 용도에 최적화되어 있다.
 

요약

  • docker logs컨테이너의 표준 출력을 도커 데몬이 낚아채서 호스트 디스크의 JSON 파일에 기록해둔 것을 보여주는 도구이다.
  • 기본 로깅 드라이브는 json-file 이다.
  • 로그는 컨테이너의 라이프사이클과 함께 움직인다.
  • 로그 파일이 무한히 커지는 것을 방지하려면 반드시 로그 로테이션(max-size, max-file) 설정이 필요하다.
  • 운영 환경에서는 json-file에만 의존하지 않고, 외부 로그 수집 시스템을 사용하는 것이 일반적이다.
 

참고 자료