💨

脱Nginx? Traefik v3で作る次世代のリバースプロキシ環境

2025/01/16に公開

はじめに

最近、開発環境のリバースプロキシとしてTraefikを採用する機会がありました。
実際に使ってみると、その使いやすさと機能性に驚かされます。
特にバージョン3以降は、Nginxと比較しても大きなアドバンテージがある場面が増えてきました。

ただ、日本語での情報が少なく、特にv3に関する実践的な知見の共有が不足していると感じています。
本記事では、実際の開発経験をもとに、Traefikの特徴や具体的な活用方法をご紹介します。

こんな課題を抱えていませんか?

  • Nginxの設定ファイルが複雑で管理が大変
  • コンテナのスケールアウト時の設定変更が面倒
  • リバースプロキシの状態を可視化したい

Traefikの主要な特徴

1. パフォーマンスの大幅な改善(v3の目玉機能)

v3では内部アーキテクチャの刷新により、大幅なパフォーマンス向上を実現しています。
公式のベンチマーク結果では

  • レイテンシが約20%改善(v2比)
    Nginxにも劣らない状態に
  • メモリ使用量の最適化
  • HTTPSルーティングの処理速度向上
  • 特に高負荷時のスループット改善

これらの改善は、Beyn Technologyによるベンチマークテストでも実証されていますのでご確認ください。
https://medium.com/beyn-technology/is-nginx-dead-is-traefik-v3-20-faster-than-traefik-v2-f28ffb7eed3e

特に、大規模なマイクロサービス環境での性能向上が顕著とのことです。

2. NginxとTraefikざっくり比較

機能 Nginx Traefik
設定方法 静的な設定ファイル 動的設定(⭐️自動検出)
コンテナ連携 手動設定が必要 Docker/K8s自動検出
設定の複雑さ やや複雑 ⭐️シンプル
モニタリング プラグインが必要 ダッシュボード標準搭載
動的設定反映 再起動が必要 Zero Downtime
コミュニティ 非常に大きい 成長中

Nginxと比較して、設定項目がシンプル、かつ動的に反映されるところが強みだと感じます。

3. 実用的なダッシュボード

Traefikダッシュボード画像

使っててテンション上がるダッシュボードですね。笑
このダッシュボードでは以下の事ができます。

  • リアルタイムのトラフィック可視化
  • ルーティングルールの確認と管理
  • ミドルウェア設定の状況確認
  • サービスヘルスチェックのモニタリング
  • HTTP/TCPエンドポイントの一覧表示

たとえばルーティングだとこんな感じ。
ルーティング画面

実装例

基本的な設定例

services:
  reverse-proxy:
    image: traefik:3.1.2
    command:
      - "--api.insecure=true"  # 開発環境用
      - "--providers.docker=true"
      - "--providers.docker.exposedByDefault=false"
      - "--entryPoints.web.address=:80"
      - "--api.dashboard=true"
      - "--accesslog=true"
    volumes:
      - /var/run/docker.sock:/var/docker.sock
    ports:
      - "80:80"
      - "8081:8081"  # ダッシュボード用

CORSミドルウェアの設定

http:
  middlewares:
    cors:
      headers:
        accessControlAllowMethods:
          - GET
          - POST
          - PUT
          - DELETE
        accessControlAllowOriginList:
          - "http://localhost:3000"
        accessControlAllowCredentials: true

完全な設定例

一例として、私が以下4コンテナで構築した場合の実装を記載します。

  • APIコンテナ
  • MongoDB
  • Mongo-Express(MongoDBのGUI)
  • Traefik

docker-compose.yml

services:
  # バックエンドAPI
  api:
    build:
      context: ./backend  # 同階層にbackendフォルダがあります
      dockerfile: Dockerfile.dev
    image: api:latest
    expose:
      - 8088
    volumes:
      - ./backend:/usr/src/app
    networks:
      - mynet
    depends_on:
      mongo:
        condition: service_healthy
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.backend-api.entrypoints=web"
      - "traefik.http.routers.backend-api.rule=Host(`localhost`)"  # "localhost"でアクセスできるよう設定
      - "traefik.http.routers.backend-api.middlewares=cors@file"
      - "traefik.http.services.backend-api.loadbalancer.server.port=8088"

  # MongoDB
  mongo:
    image: mongo:6.0.8
    command: mongod --port 27018 --auth
    environment:
      - MONGO_INITDB_DATABASE=${MONGO_INITDB_DATABASE:-myapp}
      - MONGO_INITDB_ROOT_USERNAME=${MONGO_INITDB_ROOT_USERNAME:-admin}
      - MONGO_INITDB_ROOT_PASSWORD=${MONGO_INITDB_ROOT_PASSWORD}
    volumes:
      - mongodb_data:/data/db
      - ./mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js:ro
    networks:
      - mynet
    healthcheck:
      test: echo 'db.runCommand("ping").ok' | mongosh mongodb://${MONGO_INITDB_ROOT_USERNAME}:${MONGO_INITDB_ROOT_PASSWORD}@localhost:27018/admin --quiet
      interval: 10s
      timeout: 10s
      retries: 5
      start_period: 40s

  # MongoDB管理UI
  mongo-express:
    image: mongo-express:1.0.0-alpha.4
    environment:
      ME_CONFIG_MONGODB_SERVER: mongo
      ME_CONFIG_MONGODB_PORT: 27018
      ME_CONFIG_MONGODB_AUTH_DATABASE: admin
      ME_CONFIG_MONGODB_ADMINUSERNAME: ${MONGO_INITDB_ROOT_USERNAME:-admin}
      ME_CONFIG_MONGODB_ADMINPASSWORD: ${MONGO_INITDB_ROOT_PASSWORD}
    depends_on:
      mongo:
        condition: service_healthy
    networks:
      - mynet
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.mongo-express.entrypoints=web"
      - "traefik.http.routers.mongo-express.rule=Host(`db-admin.localhost`)"
      # Basic認証を有効化する場合は以下をコメント解除
      # - "traefik.http.routers.mongo-express.middlewares=auth"

  # Traefik
  reverse-proxy:
    image: traefik:3.1.2
    command:
      - "--log.level=DEBUG"
      - "--api.insecure=true"  # 開発環境用
      - "--providers.docker=true"
      - "--providers.docker.exposedByDefault=false"
      - "--entryPoints.web.address=:80"
      - "--api.dashboard=true"
      - "--accesslog=true"
      - "--accesslog.filePath=/logs/access.log"
      - "--accesslog.format=json"
      - "--providers.file.filename=/etc/traefik.yml"
      - "--providers.file.watch=true"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./traefik-logs:/logs
      - ./traefik.yml:/etc/traefik.yml
    ports:
      - "80:80"
      - "8081:8081"  # ダッシュボード用
    networks:
      - mynet

networks:
  mynet:
    driver: "bridge"

volumes:
  mongodb_data:

traefik.yml

http:
  middlewares:
    cors:
      headers:
        accessControlAllowMethods:
          - GET
          - POST
          - PUT
          - DELETE
          - OPTIONS
        accessControlAllowOriginList:
          - "http://localhost:3000"
        accessControlAllowCredentials: true
        accessControlMaxAge: 100
        addVaryHeader: true
        accessControlAllowHeaders:
          - "Content-Type"
          - "Authorization"

  routers:
    backend-api:
      rule: "Host(`localhost`)"
      service: backend-api
      entryPoints:
        - web
      middlewares:
        - cors
    dashboard:
      rule: "Host(`traefik.localhost`)"
      service: api@internal
      entryPoints:
        - web

  services:
    backend-api:
      loadBalancer:
        servers:
          - url: "http://api:8088"
        passHostHeader: true

log:
  level: INFO

accessLog:
  filePath: "/var/log/traefik/access.log"
  format: json

上記の設定例では開発環境向けの基本的な構成を示していますが、実運用では管理用UIへのアクセス制御も重要です。
Traefikでは、Basic認証やDigest認証、OAuth2などの認証機能を簡単に追加できます。
詳細は以下記事をご確認ください!
Traefikマーシャラーになろう

実践から見えたメリット・デメリット

メリット

  • 設定ファイルのシンプルさ
  • コンテナの自動検出機能
  • 直感的なダッシュボード
  • 動的な設定更新
  • マイクロサービスとの高い親和性

デメリット

1. Label Driven設定への学習コスト

Traefikの大きな特徴の一つが「Label Driven」な設定アプローチです。これは、各コンテナにラベルを付与することで、ルーティングやミドルウェアの設定を行う方式です。

labels:
  - "traefik.enable=true"
  - "traefik.http.routers.my-app.rule=Host(`app.localhost`)"
  - "traefik.http.services.my-app.loadbalancer.server.port=8080"

一見シンプルに見えるこの方式ですが、以下の点で学習コストが発生します

  • ラベルの階層構造の理解
  • 適切なラベル名の把握
  • 動的設定時のデバッグ方法

とはいえ、もう少し実例が広まれば容易に学習できるレベルだとは思います。

2. 設定更新時のエラー検知の難しさ

Zero Downtime設定更新は強力な機能ですが、設定エラー時の問題特定が困難です。
使いやすいだけに、ここはぜひとも改善してほしいポイントだと感じました。

具体的な課題:

  • 設定エラーがログに明示的に出力されない
  • 古い設定が継続して動作するため、エラーに気づきにくい
  • デバッグ時に問題の切り分けが難しい

対応策:

  • Prometheusなどの監視ツールと連携し、設定変更のメトリクスを監視
  • テスト環境での事前検証の徹底
  • --log.level=DEBUGの活用(開発環境)
  • ダッシュボードでの設定状態の定期確認

以下記事の”構成に誤りがあってもエラー等は表示されない” 章でもご紹介されています。
https://zenn.dev/ureo/articles/traefik-tips

3. その他の制約

  • traefik.ymlでの環境変数利用不可
    (読者の皆様、良い方法があればぜひ教えてください!)
  • 日本語の情報が限定的
  • Nginxと比べてプラグインエコシステムが発展途上

その他実践的なTips

  1. 設定ファイルの分割管理

    • 環境別の設定分離
    • ミドルウェア設定の個別ファイル化
  2. モニタリングの強化

    • Prometheusとの連携
    • JSONフォーマットでのログ出力
  3. セキュリティ設定

    • ダッシュボード認証
    • TLS終端の適切な設定

まとめ

Traefikは、特にコンテナベースの開発において、その真価を発揮します。v3での大幅なパフォーマンス向上も相まって、モダンなWeb開発のための強力なツールとなっています。

前述のようにデメリットはありますが、開発効率の向上や運用の容易さを考えると、挑戦する価値は十分にあると考えています 🙌

参考資料

Discussion