서비스 장애가 났는데 docker logs myapp에는 아무것도 나오지 않았습니다. 애플리케이션이 stdout이 아니라 /var/log/app.log 파일에 직접 로그를 쓰고 있었기 때문입니다.
다른 서버에서는 반대로 로그가 너무 많이 쌓여 /var/lib/docker 파티션이 100%가 됐습니다. Docker 로그는 어디에서 수집되고 얼마나 보관되는지 정하지 않으면, 장애를 못 찾거나 로그 때문에 다시 장애가 납니다.
도커 로그 관리와 디버깅
서비스 장애가 발생했을 때 가장 먼저 확인하는 것이 로그입니다. Docker 로그는 컨테이너 프로세스의 stdout/stderr를 자동으로 수집하는 방식으로 동작합니다. 로그를 올바르게 확인하는 방법과, 로테이션 설정 없이 운영할 때 발생하는 디스크 100% 장애를 예방하는 방법을 학습합니다.
컨테이너 로그가 어디에 어떻게 저장되는지 이해하고, 디스크 과적 없이 로그를 안전하게 관리하는 방법을 익힙니다.
- 1Docker 로그 드라이버 종류 — json-file / syslog / journald / fluentd / awslogs
- 2docker logs 명령어 — -f (follow) / --tail / --since / --until 옵션
- 3json-file 드라이버의 max-size / max-file 로테이션 설정
- 4컨테이너 stdout/stderr와 12-Factor App 로그 원칙
- 5로그가 디스크를 가득 채우는 장애 시나리오와 사전 예방
로그 파일 직접 조회(/var/lib/docker/containers/)는 root 권한이 필요합니다. sudo 권한이 없는 환경이라면 docker logs 명령어 위주로 실습합니다.
docker psdocker info | grep 'Logging Driver'ls /var/lib/docker/containers/ 2>/dev/null | head -3docker pull nginx:alpineDocker 로그 수집 원리: stdout과 stderr
서비스가 갑자기 내려갔습니다. docker logs myapp을 실행했더니 아무것도 나오지 않습니다. 컨테이너는 분명히 실행 중인데 로그가 없습니다. 원인은 해당 앱이 /var/log/app.log 파일에 로그를 기록하고 있기 때문입니다. Docker는 stdout과 stderr로 나오는 출력만 캡처합니다. 파일에 직접 기록하는 로그는 docker logs로 볼 수 없습니다. 컨테이너 환경에서 로그를 올바르게 설계하지 않으면 장애 발생 시 원인을 추적할 수 없게 됩니다. 이 ConceptBlock에서는 Docker가 로그를 수집하는 원리와 로그 드라이버 종류를 다룹니다.

컨테이너 로그의 동작 원리
Docker는 컨테이너의 PID 1 프로세스(메인 프로세스)가 표준 출력(stdout)과 표준 오류(stderr)로 내보내는 내용을 자동으로 캡처합니다.
컨테이너 내부
├── PID 1: nginx 프로세스
│ ├── stdout → "192.168.1.1 - GET /index.html 200"
│ └── stderr → "error: connection reset by peer"
│
Docker 데몬
└── 두 스트림 캡처 → 로그 파일로 저장
└── /var/lib/docker/containers/[ID]/[ID]-json.log
docker logs 명령을 실행하면 이 파일을 읽어서 출력합니다.
stdout vs 파일 로그
[stdout 로그 — docker logs로 확인 가능]
app.py에서:
print("서버 시작됨") # stdout
sys.stderr.write("에러발생") # stderr
→ docker logs my-container 로 확인 가능 ✓
[파일 로그 — docker logs로 확인 불가]
app.py에서:
with open("/var/log/app.log", "a") as f:
f.write("서버 시작됨")
→ docker logs my-container : (아무 출력 없음) ✗
→ docker exec my-container cat /var/log/app.log 로 확인 ✓
로그 드라이버 종류
Docker는 로그를 어떻게 저장하고 전달할지 결정하는 로그 드라이버를 제공합니다.
| 드라이버 | 설명 | 특징 |
|---|---|---|
json-file | JSON 형식으로 로컬 파일 저장 | 기본값, docker logs 지원 |
local | 최적화된 바이너리 형식으로 저장 | json-file보다 효율적 |
syslog | 시스템 syslog로 전달 | 중앙 집중식 로깅 |
journald | systemd journal로 전달 | docker logs 지원 |
fluentd | Fluentd 로그 수집기로 전달 | 대규모 로그 파이프라인 |
none | 로그 비활성화 | 성능 최우선 시 |
# 실습 디렉토리 준비
mkdir -p /tmp/docker/part4/exam_14 && cd /tmp/docker/part4/exam_14
# 기본 로그 드라이버 확인
docker info --format '{{.LoggingDriver}}'
# json-file (기본값)
# 특정 드라이버로 컨테이너 실행
docker run --log-driver local nginx
로그 파일 위치
# 로그 파일이 실제로 저장된 위치 확인
docker inspect my-container --format '{{.LogPath}}'
# /var/lib/docker/containers/abc123.../abc123...-json.log
# 직접 파일 내용 확인 (root 권한 필요)
sudo cat /var/lib/docker/containers/abc123.../abc123...-json.log
# {"log":"서버 시작됨\n","stream":"stdout","time":"2024-01-01T00:00:00Z"}
로그 로테이션과 디스크 장애 예방
서비스가 안정적으로 잘 돌아가고 있는데 어느 날 새벽 모든 컨테이너가 한꺼번에 내려갑니다. 원인은 디스크 풀(full)입니다. docker logs도 느려지고 컨테이너를 재시작해도 디스크가 이미 꽉 찬 상태입니다. 조사해보면 특정 서비스의 로그 파일이 수십 GB에 달합니다. Docker의 기본 로그 드라이버(json-file)는 파일 크기 제한이 없어서 로테이션을 설정하지 않으면 이런 상황이 발생합니다. 트래픽이 많지 않은 서비스도 몇 달이면 디스크를 채울 수 있습니다. 로그 로테이션은 선택이 아니라 운영 필수 설정입니다. 이 ConceptBlock에서는 Docker 로그 파일이 쌓이는 구조와 로테이션 설정 방법을 다룹니다.

로그 로테이션 없이 운영 시 발생하는 문제
Docker의 기본 설정에서는 로그 파일 크기 제한이 없습니다. 트래픽이 많은 서비스를 오랫동안 운영하면 로그 파일이 수십 GB로 커져 디스크를 가득 채울 수 있습니다.
# 로테이션 없는 컨테이너의 로그 파일 크기 확인
sudo du -sh /var/lib/docker/containers/*/
# 52G /var/lib/docker/containers/abc123.../ ← 장애!
# 전체 도커 데이터 디스크 사용량
docker system df
# TYPE TOTAL ACTIVE SIZE RECLAIMABLE
# Containers 5 3 52.3GB 12.1GB
디스크가 100%가 되면 새 로그를 기록하지 못하는 것은 물론, 컨테이너가 새 프로세스를 생성하지 못해 서비스 전체가 다운될 수 있습니다.
로그 로테이션 설정 방법
방법 1: 컨테이너 실행 시 개별 설정
docker run \
--log-driver json-file \
--log-opt max-size=10m \
--log-opt max-file=3 \
nginx
max-size=10m: 로그 파일이 10MB에 도달하면 새 파일 생성
max-file=3: 최대 3개 파일 유지 → 최대 30MB
방법 2: Docker 데몬 전역 설정 (권장)
모든 컨테이너에 기본 로테이션이 적용됩니다.
// /etc/docker/daemon.json
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
# 설정 적용
sudo systemctl restart docker
방법 3: Docker Compose에서 설정
services:
web:
image: nginx
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
api:
image: my-api
logging:
driver: "json-file"
options:
max-size: "50m" # API는 로그가 많으므로 크게
max-file: "5"
로테이션 동작 시각화
시간 흐름 →
파일1: [0MB → 10MB] → 순환
파일2: [0MB → 10MB] → 순환
파일3: [0MB → 10MB] → 순환
파일1: [0MB → ...] (파일1 재사용, 이전 내용 삭제)
최대 유지되는 로그: 파일1 + 파일2 + 파일3 = 30MB
docker logs 명령의 다양한 옵션을 실제로 사용해봅니다.
1단계: 로그를 생성하는 테스트 컨테이너 실행
실습 전 디렉토리와 예제 파일을 먼저 준비합니다.
# 실습 디렉토리 준비
mkdir -p /tmp/docker/part2/exam_4 && cd /tmp/docker/part2/exam_4
# 로그 테스트용 docker-compose.yml 생성
cat > docker-compose.yml << 'EOF'
version: '3.8'
services:
app:
image: nginx:alpine
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
EOF
이제 실습을 진행합니다.
# 1초마다 타임스탬프와 함께 로그를 출력하는 컨테이너
docker run -d --name log-demo alpine sh -c \
"while true; do echo \"\$(date '+%Y-%m-%d %H:%M:%S') 서비스 정상 동작 중\"; sleep 1; done"
2단계: 기본 로그 확인
# 전체 로그 출력
docker logs log-demo
# 실시간 로그 스트리밍 (Ctrl+C로 종료)
docker logs -f log-demo
3단계: 최근 N줄만 보기
# 최근 5줄만 출력
docker logs --tail 5 log-demo
# 최근 5줄부터 실시간 스트리밍
docker logs -f --tail 5 log-demo
4단계: 시간 기반 필터링
# 지난 30초 동안의 로그
docker logs --since 30s log-demo
# 특정 시각 이후 로그
docker logs --since "2024-01-01T12:00:00" log-demo
# 특정 시각 이전 로그 (until)
docker logs --until "2024-01-01T12:05:00" log-demo
# 시간 범위 지정
docker logs --since 2m --until 1m log-demo
5단계: 타임스탬프 포함 출력
# Docker가 캡처한 시각의 타임스탬프 포함
docker logs -t log-demo
# 2024-01-01T12:00:01.123456789Z 2024-01-01 12:00:01 서비스 정상 동작 중
# 실시간 + 타임스탬프 + 최근 10줄
docker logs -f -t --tail 10 log-demo
6단계: stdout/stderr 분리 확인
# stdout과 stderr 모두 출력하는 컨테이너
docker run -d --name mixed-log alpine sh -c \
"while true; do
echo 'stdout: 정상 처리'
echo 'stderr: 경고 발생' >&2
sleep 2
done"
# 기본은 stdout + stderr 합쳐서 출력
docker logs mixed-log
로그 실습 컨테이너 강제 삭제
안전한 실행 조건: docker logs 옵션 확인을 끝낸 뒤 실습 컨테이너를 정리하는 경우에만 실행합니다.
실행 전 반드시 확인
- docker logs로 필요한 로그 확인을 마쳤다
- 삭제 대상이 log-demo와 mixed-log임을 확인했다
- 보존해야 할 로그가 없음을 확인했다
docker rm -f log-demo mixed-log위 항목을 모두 확인한 후 복사할 수 있습니다
- docker logs log-demo 출력에서 1초마다 생성된 로그가 보이는가?
- docker logs -f --tail 5 log-demo가 최근 5줄부터 실시간으로 이어지는가?
- docker logs mixed-log 출력에서 stdout과 stderr가 함께 보이는가?
- 정리 후 docker ps -a에서 log-demo와 mixed-log가 남아있지 않은가?
로그 로테이션을 설정하고 실제로 파일이 순환되는 것을 확인합니다.
1단계: 로테이션 없는 컨테이너의 로그 파일 확인
# 로테이션 없이 실행
docker run -d --name no-rotate nginx:alpine
# 로그 파일 경로 확인
LOG_PATH=$(docker inspect no-rotate --format '{{.LogPath}}')
echo "로그 파일: $LOG_PATH"
# 현재 크기 확인
sudo ls -lh "$LOG_PATH"
# 처음엔 매우 작지만 트래픽이 쌓이면 무한정 커짐
2단계: 로테이션 설정한 컨테이너 실행
docker run -d --name with-rotate \
--log-driver json-file \
--log-opt max-size=1m \
--log-opt max-file=3 \
alpine sh -c "while true; do
# 빠르게 로그를 생성해 로테이션 유발
for i in \$(seq 1 100); do
echo \"로그 데이터: \$(date) - \$(cat /dev/urandom | head -c 100 | base64)\"
done
done"
3단계: 로그 파일 로테이션 관찰
# 로그 파일 디렉토리 감시
LOG_DIR=$(dirname $(docker inspect with-rotate --format '{{.LogPath}}'))
sudo watch -n 1 "ls -lh $LOG_DIR/"
# 처음: with-rotate-json.log (단일 파일)
# 1MB 초과 후: with-rotate-json.log + with-rotate-json.log.1
# 다시 초과: ...log + ...log.1 + ...log.2
# 3개 유지: 가장 오래된 파일 자동 삭제
4단계: docker logs는 계속 동작함 확인
# 로테이션 후에도 docker logs는 최신 로그를 정상 출력
docker logs --tail 5 with-rotate
5단계: 정리
로그 로테이션 실습 컨테이너 강제 삭제
안전한 실행 조건: 로그 로테이션 관찰을 끝낸 뒤 실습 컨테이너를 정리할 때만 실행합니다.
실행 전 반드시 확인
- 로그 파일 로테이션 결과를 확인했다
- docker logs --tail 5 with-rotate 확인을 마쳤다
- 삭제 대상이 실습 컨테이너뿐임을 확인했다
docker rm -f no-rotate with-rotate위 항목을 모두 확인한 후 복사할 수 있습니다
애플리케이션이 파일에 직접 로그를 기록할 때 접근하는 방법을 익힙니다.
1단계: 파일에 로그를 기록하는 컨테이너 시뮬레이션
# /var/log/app.log 파일에 직접 기록하는 컨테이너
docker run -d --name file-logger alpine sh -c \
"while true; do
echo \"\$(date '+%Y-%m-%d %H:%M:%S') [INFO] 주문 처리 완료 ID=\$(shuf -i 1000-9999 -n 1)\" >> /var/log/app.log
sleep 1
done"
2단계: docker logs로 확인 시도 (실패)
docker logs file-logger
# (아무 출력 없음) — 파일에 기록했으므로 docker logs에 안 잡힘
3단계: exec로 컨테이너 내부에서 확인
# 최근 10줄 확인
docker exec file-logger tail -10 /var/log/app.log
# 실시간 파일 로그 스트리밍
docker exec file-logger tail -f /var/log/app.log
4단계: 로그 파일 호스트로 복사
# 분석을 위해 호스트로 복사
docker cp file-logger:/var/log/app.log ./app-logs-backup.log
cat ./app-logs-backup.log | head -20
5단계: 근본 해결 — stdout으로 심볼릭 링크
일부 컨테이너 이미지는 로그 파일을 stdout으로 리디렉션해 이 문제를 해결합니다.
# nginx 공식 이미지가 사용하는 방식 확인
docker run --rm nginx:alpine cat /etc/nginx/nginx.conf | grep access_log
# access_log /dev/stdout main; ← stdout으로 리디렉션!
# error_log /dev/stderr notice; ← stderr로 리디렉션!
6단계: 정리
rm -f ./app-logs-backup.log
파일 로그 실습 컨테이너 강제 삭제
안전한 실행 조건: 필요한 로그를 호스트로 복사했거나 로그 보존이 필요 없는 실습 상황에서만 실행합니다.
실행 전 반드시 확인
- docker cp로 필요한 로그 백업을 완료했다
- 삭제 대상이 file-logger인지 확인했다
- 로컬 백업 파일 삭제 여부를 별도로 판단했다
docker rm -f file-logger위 항목을 모두 확인한 후 복사할 수 있습니다
증상
컨테이너가 실행 중인데 docker logs를 실행해도 아무 내용이 나오지 않습니다.
docker logs my-java-app
# (출력 없음)
docker ps
# my-java-app Up 2 hours ← 분명히 실행 중인데...
원인 진단
원인 1: 애플리케이션이 파일에 로그를 기록
# 컨테이너 내부에 로그 파일이 있는지 확인
docker exec my-java-app find /var/log -name "*.log" 2>/dev/null
# /var/log/application/app.log ← 여기에 기록 중
# 파일 로그 확인
docker exec my-java-app tail -50 /var/log/application/app.log
원인 2: 로그 드라이버가 none으로 설정됨
docker inspect my-java-app --format '{{.HostConfig.LogConfig.Type}}'
# none ← 로그 드라이버 비활성화 상태
원인 3: 로그 버퍼링 (Python/Java에서 흔함)
# Python은 기본적으로 stdout을 버퍼링
# 버퍼가 가득 차기 전까지 docker logs에 나타나지 않음
# 해결: 환경변수로 버퍼링 비활성화
docker run -e PYTHONUNBUFFERED=1 my-python-app
# 또는
docker run my-python-app python -u app.py # -u: unbuffered
해결 방법
로그를 stdout으로 출력하도록 애플리케이션 수정
# Python: 파일 대신 stdout으로 출력
import sys
print("로그 메시지", file=sys.stdout, flush=True)
// Java/Spring Boot: application.properties
logging.file.name= # 파일 로깅 비활성화
# 콘솔 출력은 자동으로 stdout으로 전달됨
# Nginx: /dev/stdout으로 심볼릭 링크
ln -sf /dev/stdout /var/log/nginx/access.log
ln -sf /dev/stderr /var/log/nginx/error.log
증상
df -h
# Filesystem Size Used Avail Use% Mounted on
# /dev/sda1 50G 50G 0 100% / ← 재앙!
docker ps
# ERROR: Cannot connect to Docker daemon — 도커도 동작 중단!
즉각 대응 (디스크 공간 확보)
1. 로그 파일 크기 확인
sudo du -sh /var/lib/docker/containers/*/
# 45G /var/lib/docker/containers/abc123.../ ← 원인 발견!
# 해당 컨테이너 이름 확인
sudo docker inspect abc123... --format '{{.Name}}'
2. 긴급 로그 파일 비우기 (컨테이너 중단 없이)
# ⚠️ rm이 아니라 truncate로! rm하면 프로세스가 파일 핸들 유지해 공간 안 줄어듦
sudo truncate -s 0 /var/lib/docker/containers/abc123.../abc123...-json.log
# 공간 확인
df -h
# 공간 확보됨!
3. 사용하지 않는 Docker 리소스 정리
로그 장애 대응 중 Docker 리소스 정리
안전한 실행 조건: 디스크 장애 대응 중이며 삭제 대상 리소스와 볼륨을 확인한 경우에만 실행합니다.
실행 전 반드시 확인
- sudo du 또는 docker system df로 디스크 원인을 확인했다
- docker volume ls로 보존할 볼륨이 없는지 확인했다
- 먼저 truncate와 로그 로테이션 설정을 검토했다
docker system prune -f --volumes위 항목을 모두 확인한 후 복사할 수 있습니다
재발 방지 설정
// /etc/docker/daemon.json — 즉시 적용
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
sudo systemctl restart docker
# 이후 모든 새 컨테이너에 로테이션 자동 적용
기존 실행 중인 컨테이너에 적용하려면: 컨테이너를 재생성해야 합니다. 로그 드라이버 설정은 컨테이너 생성 시에만 적용됩니다.
소규모 서비스는 docker logs로 충분하지만, 여러 서버에 분산된 마이크로서비스 환경에서는 중앙 집중식 로그 관리 시스템이 필수입니다.
실무 로그 파이프라인 아키텍처
[컨테이너들]
├── web-service (stdout/stderr)
├── api-service (stdout/stderr)
└── order-service (stdout/stderr)
↓
[로그 수집기]
├── Fluentd/Promtail (컨테이너 로그 수집)
↓
[로그 저장소]
├── Elasticsearch + Kibana (ELK Stack)
└── Loki + Grafana (경량화 대안)
Docker Compose에서 Loki 로그 드라이버 사용
version: "3.9"
services:
app:
image: my-app
logging:
driver: loki # Loki로 직접 전송
options:
loki-url: "http://loki:3100/loki/api/v1/push"
loki-batch-size: "400"
loki-retries: "5"
labels: "service=app,env=production"
loki:
image: grafana/loki:latest
ports:
- "3100:3100"
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
현업에서의 로그 레벨 전략
DEBUG → 개발 환경에서만 (stdout)
INFO → 주요 이벤트, 운영에서 항상 수집
WARN → 주의 필요, 알림 연동 고려
ERROR → 즉시 알림 발송 (Slack/PagerDuty)
FATAL → 서비스 중단 수준, 온콜 호출
이 플랫폼의 e-commerce 프로젝트에서는 각 언어별 로깅 라이브러리(Winston, Structlog, Logrus, SLF4j)가 모두 JSON 형식으로 stdout에 출력하도록 설정되어 있어, Promtail이 자동으로 수집해 Loki로 전달하는 구조입니다. 실무 DevOps 엔지니어는 모든 서비스의 로그를 단일 대시보드(Grafana)에서 통합 조회하고, 에러 패턴을 알림으로 설정합니다.
구조적 로그(Structured Logging)와 멀티라인 처리

왜 구조적 로그인가?
# 비구조적 로그 (텍스트 파싱이 어려움)
2026-04-05 10:30:15 ERROR User login failed: invalid password for user john
# 구조적 로그 (JSON — 필드별 검색/집계 가능)
{"time":"2026-04-05T10:30:15Z","level":"ERROR","event":"login_failed",
"user":"john","reason":"invalid_password","ip":"192.168.1.100","latency_ms":5}
구조적 로그는 Elasticsearch, Loki 같은 로그 저장소에서 user=john AND event=login_failed 같은 쿼리로 빠르게 검색할 수 있습니다.
앱에서 구조적 로그 출력하기
# Python — structlog 라이브러리
import structlog
log = structlog.get_logger()
log.error("login_failed", user="john", reason="invalid_password", ip="1.2.3.4")
# {"event": "login_failed", "user": "john", "reason": "invalid_password", ...}
// Node.js — pino 라이브러리 (고성능)
const pino = require('pino');
const log = pino();
log.error({ user: 'john', reason: 'invalid_password' }, 'login failed');
// {"level":50,"time":1712300000000,"user":"john","reason":"invalid_password","msg":"login failed"}
// Java — logback JSON 레이아웃
// logback.xml에서 JsonLayout 설정
// 모든 로그가 자동으로 JSON으로 출력됨
logger.error("login failed", kv("user", "john"), kv("reason", "invalid_password"));
멀티라인 로그 문제 (스택 트레이스)
Docker의 json-file 드라이버는 줄 단위로 로그를 저장합니다. Java 스택 트레이스처럼 여러 줄인 경우 각 줄이 별개 이벤트로 분리됩니다.
# 실제 저장된 로그 파일 확인
sudo cat /var/lib/docker/containers/<id>/<id>-json.log | head -5
# {"log":"Exception in thread \"main\"\n","stream":"stderr","time":"..."}
# {"log":"\tat com.example.App.main(App.java:42)\n","stream":"stderr","time":"..."}
# {"log":"\tat com.example.App.run(App.java:28)\n","stream":"stderr","time":"..."}
# ← 스택 트레이스가 3개의 별개 이벤트로 분리됨!
멀티라인 처리 방법
# 방법 2: Promtail (Loki 수집기) 멀티라인 파서 설정
scrape_configs:
- job_name: docker
pipeline_stages:
- multiline:
firstline: '^\d{4}-\d{2}-\d{2}' # 날짜로 시작하는 줄이 첫 줄
max_wait_time: 3s
로그 포맷 권장 사항
| 상황 | 권장 방식 |
|---|---|
| 단순 개발/디버깅 | 텍스트 로그 (가독성 우선) |
| 운영 환경 (로그 집중화 예정) | JSON 구조적 로그 |
| 멀티라인 예외가 많은 Java | JSON + 스택 트레이스 직렬화 |
| 고성능 필요 | binary format (Loki native, OpenTelemetry) |
로그 중앙화 — 여러 컨테이너 로그를 한 곳에서 보기

왜 중앙화가 필요한가
컨테이너가 5개일 때는 docker logs로 하나씩 확인할 수 있습니다. 하지만 수십 개의 컨테이너가 있다면 docker logs로는 불가능합니다.
중앙화 전: 컨테이너마다 docker logs 명령 실행
→ 로그가 각 서버에 분산
→ 서버 장애 시 로그 유실
중앙화 후: 모든 로그를 한 곳에 수집
→ 전체 서비스 로그를 한 화면에서 검색/분석
→ 장기 보관 및 알람 연동
주요 스택 선택
| 스택 | 수집기 | 저장소 | 시각화 | 특징 |
|---|---|---|---|---|
| ELK | Filebeat/Logstash | Elasticsearch | Kibana | 강력한 검색, 무거움 |
| PLG (Grafana) | Promtail | Loki | Grafana | 경량, Prometheus와 통합 |
| 클라우드 AWS | CloudWatch Agent | CloudWatch | Console | 관리형, 비용 발생 |
| 클라우드 GCP | Ops Agent | Cloud Logging | Console | 관리형, 비용 발생 |
Docker 로그 드라이버로 직접 전송
# docker-compose.yml
services:
app:
image: myapp
logging:
driver: "json-file" # 기본 (로컬 저장 + docker logs 지원)
options:
max-size: "10m"
max-file: "3"
app-prod:
image: myapp
logging:
driver: "awslogs" # AWS CloudWatch로 직접 전송
options:
awslogs-region: "ap-northeast-2"
awslogs-group: "/myapp/prod"
awslogs-stream: "api-1"
Loki + Promtail 빠른 설정 (Docker Compose)
# monitoring/compose.yml
services:
loki:
image: grafana/loki:latest
ports:
- "3100:3100"
volumes:
- loki-data:/loki
promtail:
image: grafana/promtail:latest
volumes:
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- /var/run/docker.sock:/var/run/docker.sock
- ./promtail-config.yml:/etc/promtail/config.yml
grafana:
image: grafana/grafana:latest
ports:
- "3001:3000"
environment:
- GF_AUTH_ANONYMOUS_ENABLED=true
volumes:
- grafana-data:/var/lib/grafana
volumes:
loki-data:
grafana-data:
# promtail-config.yml
scrape_configs:
- job_name: docker
docker_sd_configs:
- host: unix:///var/run/docker.sock
refresh_interval: 5s
relabel_configs:
- source_labels: [__meta_docker_container_name]
target_label: container
Grafana에서 Loki 데이터소스를 추가하면 {container="myapp"} 쿼리로 모든 컨테이너 로그를 한 화면에서 검색할 수 있습니다.
정리
Docker 로그 관리의 핵심을 요약합니다.
| 명령/설정 | 용도 |
|---|---|
docker logs -f [컨테이너] | 실시간 로그 스트리밍 |
docker logs --tail 100 | 최근 100줄 출력 |
docker logs --since 1h | 지난 1시간 로그 |
max-size: "10m" | 로그 파일 최대 크기 |
max-file: "3" | 최대 파일 개수 유지 |
모든 컨테이너에 로그 로테이션을 설정하는 것은 운영 환경 필수 사항입니다. /etc/docker/daemon.json에 전역 설정을 추가해 새로 생성되는 모든 컨테이너에 자동 적용하세요.
다음 모듈에서는 Docker 환경에서 실행 중인 컨테이너를 관찰하고, 메트릭 기반으로 이상 징후를 찾는 모니터링 흐름을 다룹니다.