実際に直面した問題
VPS上で約8つのコンテナのreverse proxyとしてTraefikをデプロイしていた。最初の2週間は問題なく動いていた。ある朝、ユーザーからサイトが異常に遅いと報告があった――コンテナはrunning状態で、Traefik側に明確なエラーもない。サーバーにSSHしてdocker statsを実行し、コンテナのログを1つずつ確認した。ボトルネックを見つけるまで約30分かかった。あるserviceがrequestの処理に時間をかけすぎていたのが原因だった。
問題はツールが足りないことではなかった。問題はTraefikがブラックボックスとして動作することだ。起動していることはわかるが、何件のrequestが通過しているか、5xxエラーが何件発生しているか、平均latencyが何ミリ秒なのかがわからない。その数値がなければ、デバッグは手探りになってしまう。
Traefikに不足しているvisibilityとは
デフォルトではTraefikはmetricsを外部に公開しない。コンテナが正常であっても、見えるのはテキスト形式のログだけ――時系列グラフを描くための数値がない。具体的に不足しているもの:
- Request Rate:各entrypointやrouterを通過するrequestは毎秒何件か?
- Error Rate:4xx/5xxレスポンスの割合は?どのserviceが最も多くエラーを返しているか?
- Latencyパーセンタイル:各backendサービスのP50、P95、P99 latencyはどのくらいか?
- Active connections:ある時点でいくつのTCP connectionが開いているか?
TraefikはPrometheus、Datadog、InfluxDBへのmetricsエクスポートをサポートしている――ただしデフォルトではすべて無効だ。以下の2つの方法は、手軽なものから本格的なものへと、実際のニーズに応じて選べる。
解決方法
方法1:access logを有効化して手動で分析する
即時デバッグに最も手早い方法だ。traefik.ymlに以下を追加する:
accessLog:
filePath: "/var/log/traefik/access.log"
format: json
fields:
headers:
defaultMode: drop
5xxエラーをカウントする:
grep '"DownstreamStatus":5' /var/log/traefik/access.log | wc -l
問題を素早く特定したいときに便利だ。ただし、ダッシュボードもアラートもなく、時系列のトレンドも把握できない――長期的なproduction環境には適さない。
方法2:Prometheus metrics + Grafana
TraefikにはPrometheus形式の/metricsエンドポイントが組み込まれている――有効化して、Prometheusにscrapeさせ、Grafanaダッシュボードを作成すれば完了だ。セットアップに約30分かかるが、一度構築すれば永続的に使える。これが自分がproductionで実際に使っている方法だ。
Docker Composeを使った完全なセットアップ
ステップ1:TraefikにPrometheus metricsを公開する設定を行う
traefik.ymlにmetricsセクションを追加する。注意:addRoutersLabelsとaddServicesLabelsは最も見落としやすい2つのオプションだ――これらがないと、metricsはentrypoint単位でのaggregateのみになり、service単位のbreakdownができなくなる:
# traefik.yml
api:
dashboard: true
insecure: true
entryPoints:
web:
address: ":80"
websecure:
address: ":443"
metrics:
address: ":8082"
metrics:
prometheus:
entryPoint: metrics
addEntryPointsLabels: true
addRoutersLabels: true
addServicesLabels: true
buckets:
- 0.1
- 0.3
- 1.2
- 5.0
providers:
docker:
exposedByDefault: false
ステップ2:全スタックのDocker Compose設定
Traefik + Prometheus + Grafana用のdocker-compose.ymlファイル:
version: '3.8'
networks:
proxy:
external: true
monitoring:
internal: true
volumes:
prometheus_data:
grafana_data:
services:
traefik:
image: traefik:v3.0
container_name: traefik
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "8080:8080" # dashboard
- "8082:8082" # metrics
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik.yml:/traefik.yml:ro
networks:
- proxy
- monitoring
prometheus:
image: prom/prometheus:v2.51.0
container_name: prometheus
restart: unless-stopped
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.retention.time=15d'
networks:
- monitoring
grafana:
image: grafana/grafana:10.4.0
container_name: grafana
restart: unless-stopped
environment:
- GF_SECURITY_ADMIN_PASSWORD=your_strong_password
- GF_USERS_ALLOW_SIGN_UP=false
volumes:
- grafana_data:/var/lib/grafana
networks:
- monitoring
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.grafana.rule=Host(`grafana.yourdomain.com`)"
- "traefik.http.routers.grafana.entrypoints=websecure"
ステップ3:PrometheusにTraefikをscrapeさせる設定
prometheus.ymlファイル。TraefikとPrometheusが同じmonitoringネットワーク上にあるため、IPアドレスの代わりにサービス名を使用できる――アドレスをハードコードする必要がない:
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- "traefik_alerts.yml"
scrape_configs:
- job_name: 'traefik'
static_configs:
- targets: ['traefik:8082']
metrics_path: /metrics
TraefikがmetricsをExposeしているか確認する:
curl http://localhost:8082/metrics | grep traefik_entrypoint
出力例:
traefik_entrypoint_requests_total{code="200",entrypoint="websecure",method="GET",protocol="http"} 1523
traefik_entrypoint_requests_total{code="404",entrypoint="websecure",method="GET",protocol="http"} 42
traefik_entrypoint_request_duration_seconds_bucket{entrypoint="websecure",le="0.1"} 1401
ステップ4:Grafanaダッシュボード用のPromQLクエリ
GrafanaにPrometheusデータソースを追加し(http://prometheus:9090)、以下のパネルでダッシュボードを作成する:
Request Rate — entrypoint別の合計request/秒:
sum(rate(traefik_entrypoint_requests_total[2m])) by (entrypoint)
Error Rate — service別の5xxエラーrequest割合:
100 * sum(rate(traefik_service_requests_total{code=~"5.."}[2m])) by (service)
/
sum(rate(traefik_service_requests_total[2m])) by (service)
P95 Latency — service別の95パーセンタイルlatency:
histogram_quantile(0.95,
sum(rate(traefik_service_request_duration_seconds_bucket[5m])) by (le, service)
)
entrypoint別のActive connections:
traefik_entrypoint_open_connections{entrypoint="websecure"}
ゼロから作りたくない場合は?Grafana dashboard ID 17346をインポートしよう――公式Traefikダッシュボードで、すぐに使えてあとからカスタマイズできる。
ステップ5:自動アラートのためのalert rules設定
prometheus.ymlと同じディレクトリにtraefik_alerts.ymlファイルを作成する:
groups:
- name: traefik_alerts
rules:
- alert: TraefikHighErrorRate
expr: |
100 * sum(rate(traefik_service_requests_total{code=~"5.."}[5m])) by (service)
/
sum(rate(traefik_service_requests_total[5m])) by (service)
> 5
for: 2m
labels:
severity: warning
annotations:
summary: "サービス {{ $labels.service }} のerror rateが5%を超えました"
description: "現在のerror rate: {{ $value | printf \"%.1f\" }}%"
- alert: TraefikHighLatency
expr: |
histogram_quantile(0.95,
sum(rate(traefik_service_request_duration_seconds_bucket[5m])) by (le, service)
) > 2
for: 5m
labels:
severity: warning
annotations:
summary: "{{ $labels.service }} のP95 latencyが2秒を超えました"
実際の成果
ダッシュボードが稼働すると、状況は大きく変わった。service別のrequest rate、backendコンテナがクラッシュしたときのerror rate、P50/P95/P99によるlatencyのbreakdown――すべてが1画面でリアルタイムに表示される。もうサーバーにSSHする必要はない。外出中でもスマートフォンでダッシュボードを開いて、ユーザーから報告が来る前に問題を発見できるようになった。
一点注意が必要なのは:traefik_service_*のmetricsは、そのserviceに少なくとも1件の実際のrequestが通過して初めて表示される。セットアップ直後にGrafanaパネルが「No data」と表示されるのは正常だ――まずテスト用のrequestをいくつか送ってみよう:
curl -s https://yourdomain.com/healthz
# 15〜30秒待ってから、Grafanaで再確認する
リソースについて:Prometheus + GrafanaはRAMを約300MB消費する――2GB VPSなら問題ない。--storage.tsdb.retention.time=15dで15日分のデータを保持でき、トレンドの確認や週末に誰も気づかないうちに発生したインシデントの追跡に十分な期間だ。

