タイトル長いですが、そのままの内容です
個人開発 & 運用しているウェブアプリが複数のDocker Composeで稼働しており、ログ調査の際にわざわざ各サーバーにSSHでログイン、docker compose logsコマンドを使って調査していました
ただこれだとめんどくさいだけでなく、エラー発生の際にアラートを出すことも難しいため、なにか良いツールが無いか調べたところGrafana + Loki + Promtailの構成にたどり着きました
各ツールについて簡単なまとめ
Grafanaとは・・・
様々なデータソースから取得した情報をダッシュボードに表示したり、グラフとして表示するためのツール
Lokiとは・・・
ログの収集、検索を行うためのシステム
Promtailとは・・・
ログを収集し、Lokiにデータを送るためのエージェント
なので、仕組みとしては
Promtailがコンテログを収集してLokiに集約、集約されたデータをGrafanaで表示したり、Grafanaで実行した検索クエリをLokiが実行して、結果をGrafanaで表示する というように、3つのツールが連携して動くようです
Grafana + Loki + Promtailの構成例
Grafana + Loki + Promtailの環境自体もDocker composeで作成します
今回はほとんどの内容をClaude(Sonnet 4.5)に作ってもらってます
services:
# ログ集約システム
loki:
image: grafana/loki:3.5
container_name: loki
ports:
- "3100:3100"
volumes:
- ./loki-config.yml:/etc/loki/config.yaml
- loki-data:/loki
command: -config.file=/etc/loki/config.yaml
user: "0" # rootユーザーで実行(Mac環境での権限問題回避)
networks:
- staging-network
restart: unless-stopped
# ログ収集エージェント
promtail:
image: grafana/promtail:3.5
container_name: promtail
volumes:
- ./promtail-config.yml:/etc/promtail/config.yml
# Mac用のログパス設定
- /var/log:/var/log:ro
# Docker Desktop for Macの場合
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
command: -config.file=/etc/promtail/config.yml
networks:
- staging-network
depends_on:
- loki
restart: unless-stopped
# ダッシュボード
grafana:
image: grafana/grafana:11.3.0
container_name: grafana
ports:
- "3101:3000"
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=admin
- GF_USERS_ALLOW_SIGN_UP=false
volumes:
- grafana-data:/var/lib/grafana
- ./grafana-datasources.yml:/etc/grafana/provisioning/datasources/datasources.yml
networks:
- staging-network
depends_on:
- loki
networks:
staging-network:
external: true
volumes:
loki-data:
grafana-data:Grafana + Loki + Protmailの構成例です
上記のdocker composeファイル1つの中にまとめられています
上記はローカル環境の例でMacBook上に構築されたアプリ実行環境(Webアプリ、MySQLなどのインフラが複数のdocker containerで動作している環境)のログを監視できます
apiVersion: 1
datasources:
- name: Loki
type: loki
access: proxy
url: http://loki:3100
isDefault: true
editable: true
jsonData:
maxLines: 1000上記は「grafana-datasources.yml」の構成例です
auth_enabled: false
server:
http_listen_port: 3100
grpc_listen_port: 9096
log_level: info
common:
instance_addr: 127.0.0.1
path_prefix: /loki
storage:
filesystem:
chunks_directory: /loki/chunks
rules_directory: /loki/rules
replication_factor: 1
ring:
kvstore:
store: inmemory
# Loki 3.x の新しいストレージ設定
pattern_ingester:
enabled: true
query_range:
results_cache:
cache:
embedded_cache:
enabled: true
max_size_mb: 100
schema_config:
configs:
- from: 2024-01-01
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: index_
period: 24h
# Loki 3.x でのストレージ設定
storage_config:
tsdb_shipper:
active_index_directory: /loki/tsdb-index
cache_location: /loki/tsdb-cache
filesystem:
directory: /loki/chunks
# コンパクション設定(Loki 3.x)
compactor:
working_directory: /loki/compactor
compaction_interval: 10m
retention_enabled: true
retention_delete_delay: 2h
retention_delete_worker_count: 150
delete_request_store: filesystem
# ログの保持期間とクエリ制限
limits_config:
retention_period: 744h # 31日間
max_query_length: 721h
max_query_series: 500
max_query_parallelism: 32
split_queries_by_interval: 15m
# Loki 3.x の新機能: パターン検出
allow_structured_metadata: true
volume_enabled: true
ruler:
alertmanager_url: http://localhost:9093
storage:
type: local
local:
directory: /loki/rules
rule_path: /loki/rules-temp
ring:
kvstore:
store: inmemory
# Loki 3.x: クエリスケジューラー設定
query_scheduler:
max_outstanding_requests_per_tenant: 2048
# フロントエンド設定
frontend:
encoding: protobuf
log_queries_longer_than: 5s
compress_responses: true上記は「loki-config.yml」の設定例です
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
# Dockerコンテナのログを収集
- job_name: docker
docker_sd_configs:
- host: unix:///var/run/docker.sock
refresh_interval: 5s
relabel_configs:
# コンテナ名をラベルに追加
- source_labels: ['__meta_docker_container_name']
regex: '/(.*)'
target_label: 'container'
# コンテナIDをラベルに追加
- source_labels: ['__meta_docker_container_id']
target_label: 'container_id'
# コンテナのログパスを設定
- source_labels: ['__meta_docker_container_id']
target_label: '__path__'
replacement: '/var/lib/docker/containers/\/*-json.log'
# イメージ名をラベルに追加
- source_labels: ['__meta_docker_container_image']
target_label: 'image'
# ホスト名をラベルに追加
- source_labels: ['__meta_docker_container_label_com_docker_compose_service']
target_label: 'service'
# JSON形式のログをパース
pipeline_stages:
- json:
expressions:
output: log
stream: stream
time: time
- timestamp:
source: time
format: RFC3339Nano
- output:
source: output
# システムログの収集(オプション)
- job_name: system
static_configs:
- targets:
- localhost
labels:
job: varlogs
__path__: /var/log/*.log上記は「promtail-config.yml」の構成例です
上記3つの設定ファイルをdocker composeと同じディレクトリに設置し、docker compose up -dコマンドを実行すればログ監視環境が完成します
Grafanaを利用してみる
docker compose で環境が起動したら、以下のURLでGrafana管理画面にアクセスできます
http://localhost:3101

ログインID / パスワードは以下になります
username:admin
password:admin
※ログイン後、パスワードの変更画面が表示されるので必要に応じて変更してください
ログ検索を行ってみる
無事にログインできたら、早速ログ検索を行ってみましょう
左のExplorerからログの検索が行えます

Label filtersの項目を以下のように設定します
検索用のクエリが自動で生成されます

あとは右上の青いボタンをクリックすればクエリが実行され、ログ検索結果が表示されます

自分も、まだここまでしか使い方を調べられていませんが、少なくとも各サーバーにログインしてdocker compose logsを叩くような運用と比較してかなりマシになった気がします
ログ検索以外にも、CPU使用率などのメトリクスを取得でいるようにすれば、その内容をGrafanaに表示して条件に応じてアラートを発生させる
ということもできるようです
ログについても、Errorレベルのログが出力されたタイミングでアラートを飛ばすことができるようなので、次はその設定にチャレンジてみたいと思います
以上
Grafana + Loki + Promtailを利用したdocker containerログ確認の紹介でした
Docker環境とAIの発達で環境構築もかなり楽に行えるようになったと感じました
ログ監視できる環境は良いのですが、余計なアラートが飛んだり、ログ検索データが多くなりすぎないようにログを適切に出力することも大切だなと感じました
(今作っているウェブサイトでは、そのあたりはできていないので・・・)