Docker에서 Loki로 로그 수집하기: “사이드카 패턴”도 가능하지만, Docker에서는 더 단순한 방법이 있다

Category
Infra
Tags
Docker
Loki
Grafana
Alloy
Logging
Observability
Docker Compose
Published
March 13, 2026
Last updated
Last updated March 13, 2026
서비스를 운영하다 보면 로그를 어디에, 어떤 방식으로 모을지가 생각보다 빨리 문제로 떠오른다.
로컬에서는 docker logs만으로도 버틸 수 있지만, 서버에 올라간 뒤부터는 얘기가 달라진다. 특정 시점의 에러를 다시 보고 싶고, 여러 컨테이너 로그를 한곳에서 검색하고 싶고, 나중에는 알림과 대시보드까지 붙이고 싶어진다.
이때 자연스럽게 드는 질문이 있다.
Docker에서도 사이드카 패턴으로 로그를 뽑아서 Loki로 보낼 수 있을까?
결론부터 말하면 가능하다.
하지만 Docker 환경에서는 “무조건 사이드카”가 정답은 아니다.
앱이 이미 stdout/stderr로 로그를 잘 남기고 있다면, 지금 기준으로는 Loki Docker logging driver가 가장 단순하다. 반대로 파일 로그를 수집해야 하거나, 전처리·필터링·라우팅이 필요하다면 Grafana Alloy 컨테이너를 붙이는 방식이 더 잘 맞는다. 그리고 Promtail은 이미 deprecated 상태이며, Grafana는 앞으로의 수집 에이전트 방향을 Alloy로 가져가고 있다.

왜 “사이드카”라는 말이 Docker에서는 조금 애매할까

사이드카 패턴은 보통 애플리케이션 컨테이너 옆에 보조 컨테이너를 하나 더 붙여서 공통 기능을 분리할 때 많이 쓴다. 로그 수집도 그 대상이 될 수 있다.
그런데 Docker 단독 실행이나 Docker Compose 환경에서는, 컨테이너마다 사이드카를 하나씩 붙이는 것보다 호스트 단위로 수집기 하나를 두는 방식이 더 실용적인 경우가 많다.
로그는 결국 Docker 데몬이 이미 알고 있고, stdout 기반 로그라면 굳이 앱마다 수집 컨테이너를 복제할 이유가 크지 않기 때문이다.
그래서 Docker에서는 보통 선택지가 이렇게 갈린다.
  1. stdout/stderr 로그를 바로 Loki로 보낸다
    1. → loki-docker-driver
  1. 파일 로그를 수집하거나 전처리가 필요하다
    1. → Alloy 컨테이너가 파일 또는 Docker API를 읽어서 Loki로 전송
즉, 사이드카가 불가능한 게 아니라, Docker에서는 “호스트/Compose 단위 수집기 1개”가 더 현실적인 경우가 많다는 이야기다.

먼저 결론: 나는 어떤 방식을 고르면 될까

가장 실무적으로 정리하면 이렇다.
  • 애플리케이션이 stdout/stderr로 로그를 남긴다
    • → Loki Docker logging driver 추천
  • 애플리케이션이 파일 로그를 남긴다
    • → Alloy 컨테이너 + 공유 볼륨 tailing 추천
  • stdout 로그인데도 수집 규칙을 중앙에서 제어하고 싶다
    • → Alloy가 Docker daemon을 읽는 방식 고려
그리고 새로 시작한다면 Promtail 대신 Alloy를 보는 게 맞다. Promtail은 2025년 2월부터 LTS에 들어갔고, 2026년 3월 2일 EOL 예정이라고 공식 문서에 명시돼 있다.

방법 1. stdout/stderr 기반이라면 Loki Docker logging driver가 가장 단순하다

이 방식이 제일 깔끔한 이유는 명확하다. 애플리케이션이 이미 콘솔로 로그를 찍고 있다면, Docker가 그 로그를 알고 있고, 그걸 곧바로 Loki로 보내면 된다.
Grafana 공식 문서도 Docker plugin 형태의 Loki driver를 제공한다. 이 플러그인은 Docker 호스트마다 설치해야 하며, amd64와 arm64 태그를 나눠 제공한다. 또한 이 드라이버는 Loki로 로그를 보내면서도 json-log를 함께 사용해 docker logs가 계속 동작하도록 설계돼 있다.

여기서 말하는 “도커 플러그인”이란?

이건 컨테이너 안에 들어가는 라이브러리가 아니다.
Docker 공식 문서 기준으로, logging plugin은 Docker의 로깅 기능을 외부 확장 모듈로 붙이는 방식이다. 즉, 앱 컨테이너에 뭘 심는 게 아니라 Docker 데몬의 로그 처리 경로를 확장하는 쪽에 가깝다. Loki driver도 정확히 이 방식이다.

설치 예시

docker plugin install grafana/loki-docker-driver:3.6.0-arm64 \ --alias loki \ --grant-all-permissions
x86 서버라면 -amd64, ARM 서버라면 -arm64 태그를 맞춰 설치하면 된다. 플러그인은 로그를 수집할 Docker 호스트마다 설치해야 한다.

Docker Compose 예시

services: api: image: your-api:latest ports: - "8000:8000" environment: APP_ENV: prod logging: driver: loki options: loki-url: "https://USER:[email protected]/loki/api/v1/push" loki-batch-size: "400" loki-retries: "5" max-size: "10m" max-file: "3"
Loki로 푸시할 때는 /loki/api/v1/push 엔드포인트를 사용한다. 숫자와 불리언 옵션도 daemon.json에서는 문자열로 넣어야 한다는 점은 은근 자주 놓치는 포인트다.

이 방식이 잘 맞는 경우

  • 앱이 이미 콘솔 로그를 잘 남긴다
  • 설정을 최대한 단순하게 가져가고 싶다
  • 컨테이너별 수집기 운영이 번거롭다
  • docker logs도 계속 쓰고 싶다

주의할 점

공식 문서에는 Loki가 불안정하거나 도달 불가능할 때 Docker daemon이 로그 처리 때문에 대기할 수 있는 이슈도 적혀 있다. 무한 재시도는 로그 유실을 줄이는 대신 데몬 대기를 길게 만들 수 있고, 반대로 비차단 모드는 서비스 흐름을 막지 않지만 버퍼 초과 시 로그 유실 가능성이 생긴다.
즉, 이 방식은 단순하지만 Loki 장애 시 동작 방식까지 같이 이해하고 써야 한다.

방법 2. 진짜 “사이드카 느낌”이 필요하면 Alloy + 파일 로그 구성이 맞다

어떤 애플리케이션은 stdout보다 파일 로그를 선호한다.
혹은 로그를 파일로 남겨야 하는 이유가 있을 수도 있다. 이럴 때는 앱 컨테이너와 로그 수집 컨테이너가 공유 볼륨을 바라보는 구조가 가장 자연스럽다.
Grafana Alloy의 loki.source.file은 파일을 tail 해서 loki.write로 넘길 수 있고, 읽은 위치(offset)는 --storage.path 아래 positions 파일로 관리된다. 또한 glob 패턴 기반 파일 수집도 공식적으로 지원한다.

Compose 예시

services: api: image: your-api:latest volumes: - app_logs:/var/log/myapp environment: LOG_PATH: /var/log/myapp/app.log alloy: image: grafana/alloy:latest command: - run - --server.http.listen-addr=0.0.0.0:12345 - --storage.path=/var/lib/alloy/data - /etc/alloy/config.alloy volumes: - ./alloy/config.alloy:/etc/alloy/config.alloy:ro - app_logs:/var/log/myapp:ro - alloy_data:/var/lib/alloy/data depends_on: - api ports: - "12345:12345" volumes: app_logs: alloy_data:

config.alloy 예시

logging { level = "info" format = "logfmt" } loki.source.file "api_logs" { targets = [ { __path__ = "/var/log/myapp/*.log", job = "api", service = "your-api", env = "prod", }, ] forward_to = [loki.write.remote.receiver] file_match { enabled = true sync_period = "10s" } } loki.write "remote" { endpoint { url = "https://loki.example.com/loki/api/v1/push" basic_auth { username = "USER" password = "PASSWORD" } } }

이 방식이 잘 맞는 경우

  • 앱이 파일 로그를 남긴다
  • 컨테이너별로 수집 책임을 느슨하게 분리하고 싶다
  • 나중에 파싱, 라벨링, 필터링을 더 붙일 가능성이 있다
  • “앱 + 보조 수집기” 구조를 명확히 유지하고 싶다
이 방식은 사이드카라는 표현에 가장 가까운 형태다. 다만 Docker Compose에서 실제로는 완전한 Pod 개념이 없으니, Kubernetes식 사이드카를 그대로 가져왔다기보다 공유 볼륨 기반 보조 수집 컨테이너 패턴이라고 보는 편이 더 정확하다.

방법 3. stdout 로그인데도 중앙 수집기를 쓰고 싶다면 Alloy + Docker daemon 방식도 가능하다

stdout 로그를 유지하면서도 logging driver 대신 별도 수집 컨테이너를 두고 싶을 수 있다.
이 경우 Alloy의 discovery.dockerdiscovery.relabelloki.source.docker를 조합하면 Docker daemon에서 특정 컨테이너 로그만 골라 읽을 수 있다. loki.source.docker는 Docker 컨테이너 로그를 읽고, discovery.docker는 컨테이너 메타데이터를 타깃으로 노출하며, discovery.relabel은 그 라벨을 정리하거나 필터링할 때 쓰인다.

Alloy 설정 예시

discovery.docker "containers" { host = "unix:///var/run/docker.sock" } discovery.relabel "api_only" { targets = discovery.docker.containers.targets rule { source_labels = ["__meta_docker_container_label_logging_job"] action = "keep" regex = "api" } rule { source_labels = ["__meta_docker_container_name"] target_label = "container" } rule { target_label = "job" replacement = "api" } } loki.source.docker "api" { host = "unix:///var/run/docker.sock" targets = discovery.relabel.api_only.output labels = { env = "prod" } forward_to = [loki.write.remote.receiver] } loki.write "remote" { endpoint { url = "https://loki.example.com/loki/api/v1/push" basic_auth { username = "USER" password = "PASSWORD" } } }
앱 쪽에는 이런 식으로 라벨만 달아둘 수 있다.
services: api: image: your-api:latest labels: logging_job: api

그런데 이 방식은 왜 1순위가 아닐까

구성 자체는 예쁘다.
하지만 결국 docker.sock 접근이 필요하고, 보안 민감도가 올라간다. 또한 stdout 로그라면 logging driver가 훨씬 단순하다.
즉, 이 방식은 “할 수는 있지만, 꼭 그래야 할 이유가 있을 때만” 선택하는 편이 낫다.

self-hosted Loki를 서버에 둘 때 꼭 알아둘 점

Loki를 직접 서버에 올릴 수도 있다. 다만 운영 관점에서는 몇 가지를 분리해서 봐야 한다.
첫째, Loki 자체에는 인증 계층이 기본 포함되어 있지 않다.
공식 문서도 Loki 앞단에 인증용 reverse proxy를 두라고 안내한다. NGINX, HAProxy, OAuth2 proxy 같은 구성이 여기에 해당한다. 즉, 외부 네트워크에 직접 노출할 생각이라면 반드시 프록시 계층에서 인증과 접근 제어를 처리해야 한다.
둘째, Docker Compose 기반 Loki는 평가·테스트·개발 환경에는 괜찮지만, 운영 기본값으로 보긴 어렵다.
Grafana 문서는 Docker/Compose 설치를 평가나 개발 용도로 설명하고, 운영용으로는 Helm이나 Tanka를 권장한다.

그래서 내 선택은?

1. 가장 추천하는 기본값

앱이 stdout/stderr로 로그를 남긴다
→ Loki Docker logging driver
이유는 단순하다. 설정이 가장 짧고, 운영 복잡도가 낮고, Docker 로그 흐름을 크게 거스르지 않는다.

2. 사이드카가 정말 필요한 경우

앱이 파일 로그를 남기거나, 로그 전처리·라우팅이 필요하다
→ Alloy 컨테이너
이때부터는 수집기가 단순 운반자가 아니라, 로그 파이프라인의 일부가 된다.

3. 중앙에서 유연하게 관리하고 싶을 때

stdout 로그를 유지하면서 특정 컨테이너만 선별 수집하고 싶다
→ Alloy + Docker daemon
가능하지만, 단순성만 놓고 보면 logging driver보다 무겁다.

마무리

Docker에서 사이드카 패턴으로 로그를 뽑는 건 분명 가능하다.
하지만 로그 수집 설계에서 더 중요한 질문은 “사이드카가 가능한가?”가 아니라, “내 로그는 어디에서 나오고, 누가 가장 단순하게 책임질 수 있는가?”다.
  • stdout 로그라면 Docker logging driver가 가장 실용적이고
  • 파일 로그라면 Alloy 수집 컨테이너가 자연스럽고
  • 정교한 수집 제어가 필요하면 Alloy가 더 유연하다
그리고 지금 시점에서는 Promtail로 새 구성을 시작하기보다 Alloy 기준으로 설계하는 쪽이 맞다. Promtail은 이미 deprecated 상태이고, Grafana의 향후 기능 개발도 Alloy 중심으로 진행되고 있기 때문이다.
결국 이 문제는 “사이드카를 쓸까 말까”의 문제가 아니라, 내 운영 환경에서 가장 덜 복잡하고, 가장 오래 유지 가능한 로그 수집 경로를 고르는 문제에 가깝다.