Traefik

現代的なHTTPリバースプロキシ・ロードバランサー。自動サービス検出、Docker/Kubernetes統合、Let's Encrypt自動証明書取得。

リバースプロキシロードバランサー動的設定マイクロサービス自動HTTPSクラウドネイティブDockerKubernetes

サーバー

Traefik Proxy

概要

Traefik Proxyは、マイクロサービスアーキテクチャ向けに設計された現代的なHTTPリバースプロキシ・ロードバランサーです。既存のインフラストラクチャコンポーネント(Docker、Kubernetes等)と統合し、自動的かつ動的に設定を行います。サービスレジストリ・オーケストレーターAPIを監視し、リアルタイムでルートを生成することで、マイクロサービスのデプロイメントを大幅に簡素化します。Let's Encrypt自動統合、包括的な可観測性機能、チェーン可能なミドルウェアシステムにより、現代的なクラウドネイティブ環境で幅広く採用されています。

詳細

Traefik Proxyは、EntryPoints(ネットワークリスナー)、Routers(ルーティングロジック)、Middlewares(リクエスト/レスポンス処理)、Services(バックエンドサーバー)から構成される洗練されたアーキテクチャを持ちます。静的設定(起動時読み込み)と動的設定(実行時更新)の二重設定システムにより、再起動なしでの設定変更を実現します。Docker、Kubernetes、Consul、Etcd等の多様なプロバイダーと統合し、サービスディスカバリー機能を提供します。Weighted Round Robin、ヘルスチェック、フェイルオーバー、ミラーリング等の高度な負荷分散機能を備えています。

主な特徴

  • 動的設定管理: オーケストレーター連携による無停止設定変更
  • 自動サービスディスカバリー: Docker/Kubernetes等からの自動ルート生成
  • 自動HTTPS: Let's Encrypt統合によるワイルドカード証明書サポート
  • 包括的可観測性: メトリクス、ログ、分散トレーシング機能
  • ミドルウェアシステム: 認証、ヘッダー操作、リダイレクト等の柔軟な処理
  • シングルバイナリ: 単一実行ファイルでの簡単デプロイメント

メリット・デメリット

メリット

  • マイクロサービス・コンテナ環境に最適化された自動設定機能
  • 再起動不要で設定変更が可能な動的設定システム
  • Let's Encrypt自動統合による証明書管理の大幅簡素化
  • Prometheus、OpenTelemetry等との豊富な観測機能統合
  • チェーン可能なミドルウェアによる柔軟なリクエスト処理
  • Docker公式イメージとシングルバイナリでの簡単デプロイメント

デメリット

  • 静的・動的設定の組み合わせによる学習コストの高さ
  • プロバイダー依存による設定の複雑化リスク
  • 高度なルーティング設定での構成複雑性
  • 従来の静的プロキシと比較した場合のオーバーヘッド
  • 大規模環境でのデバッグ・トラブルシューティングの困難さ
  • プロバイダー障害時の設定更新停止リスク

参考ページ

書き方の例

基本設定とHTTPルーティング

# traefik.yml (静的設定)
api:
  dashboard: true
  insecure: true

entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"

providers:
  file:
    directory: "/etc/traefik/dynamic"
    watch: true
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false

certificatesResolvers:
  letsencrypt:
    acme:
      email: [email protected]
      storage: acme.json
      httpChallenge:
        entryPoint: web

log:
  level: INFO

accessLog: {}

metrics:
  prometheus:
    addEntryPointsLabels: true
    addServicesLabels: true
# dynamic.yml (動的設定)
http:
  routers:
    api-router:
      rule: "Host(`api.example.com`)"
      service: api-service
      entryPoints:
        - websecure
      tls:
        certResolver: letsencrypt
      middlewares:
        - auth-middleware
        - rate-limit

    frontend-router:
      rule: "Host(`www.example.com`)"
      service: frontend-service
      entryPoints:
        - websecure
      tls:
        certResolver: letsencrypt

  middlewares:
    auth-middleware:
      basicAuth:
        users:
          - "admin:$2y$10$..."
    
    rate-limit:
      rateLimit:
        burst: 100
        average: 50

  services:
    api-service:
      loadBalancer:
        servers:
          - url: "http://api-server1:8080"
          - url: "http://api-server2:8080"
        healthCheck:
          path: "/health"
          interval: "30s"
          timeout: "5s"

    frontend-service:
      loadBalancer:
        servers:
          - url: "http://frontend-server:3000"

Docker Compose統合とサービスディスカバリー

# docker-compose.yml
version: '3.8'

services:
  traefik:
    image: traefik:v3.0
    container_name: traefik
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    command:
      - --api.dashboard=true
      - --api.insecure=true
      - --providers.docker=true
      - --providers.docker.exposedbydefault=false
      - --entrypoints.web.address=:80
      - --entrypoints.websecure.address=:443
      - --certificatesresolvers.letsencrypt.acme.httpchallenge=true
      - --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web
      - --certificatesresolvers.letsencrypt.acme.email=your-email@example.com
      - --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
      - --log.level=INFO
      - --accesslog=true
      - --metrics.prometheus=true
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - letsencrypt:/letsencrypt
    networks:
      - traefik-network

  web-app:
    image: nginx:alpine
    container_name: web-app
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.webapp.rule=Host(`app.example.com`)"
      - "traefik.http.routers.webapp.entrypoints=websecure"
      - "traefik.http.routers.webapp.tls.certresolver=letsencrypt"
      - "traefik.http.services.webapp.loadbalancer.server.port=80"
      - "traefik.http.middlewares.webapp-auth.basicauth.users=admin:$$2y$$10$$..."
      - "traefik.http.routers.webapp.middlewares=webapp-auth"
    networks:
      - traefik-network

  api-app:
    image: node:alpine
    container_name: api-app
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.api.rule=Host(`api.example.com`) && PathPrefix(`/api`)"
      - "traefik.http.routers.api.entrypoints=websecure"
      - "traefik.http.routers.api.tls.certresolver=letsencrypt"
      - "traefik.http.services.api.loadbalancer.server.port=3000"
      - "traefik.http.middlewares.api-ratelimit.ratelimit.average=100"
      - "traefik.http.middlewares.api-ratelimit.ratelimit.burst=200"
      - "traefik.http.routers.api.middlewares=api-ratelimit"
    networks:
      - traefik-network

volumes:
  letsencrypt:

networks:
  traefik-network:
    external: true

Kubernetes統合(IngressRoute CRD)

# traefik-rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: traefik
  namespace: traefik-system

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: traefik
rules:
  - apiGroups: [""]
    resources: ["services", "endpoints", "secrets"]
    verbs: ["get", "list", "watch"]
  - apiGroups: ["extensions", "networking.k8s.io"]
    resources: ["ingresses", "ingressclasses"]
    verbs: ["get", "list", "watch"]
  - apiGroups: ["traefik.io"]
    resources: ["*"]
    verbs: ["get", "list", "watch"]

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: traefik
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: traefik
subjects:
  - kind: ServiceAccount
    name: traefik
    namespace: traefik-system

---
# traefik-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: traefik
  namespace: traefik-system
  labels:
    app: traefik
spec:
  replicas: 2
  selector:
    matchLabels:
      app: traefik
  template:
    metadata:
      labels:
        app: traefik
    spec:
      serviceAccountName: traefik
      containers:
      - name: traefik
        image: traefik:v3.0
        args:
          - --api.dashboard=true
          - --entrypoints.web.address=:80
          - --entrypoints.websecure.address=:443
          - --providers.kubernetescrd
          - --certificatesresolvers.letsencrypt.acme.httpchallenge=true
          - --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web
          - [email protected]
          - --certificatesresolvers.letsencrypt.acme.storage=/data/acme.json
          - --log.level=INFO
          - --accesslog=true
          - --metrics.prometheus=true
        ports:
        - name: web
          containerPort: 80
        - name: websecure
          containerPort: 443
        - name: admin
          containerPort: 8080
        volumeMounts:
        - name: data
          mountPath: /data
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: traefik-data

---
# traefik-ingressroute.yaml
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
  name: api-ingressroute
  namespace: default
spec:
  entryPoints:
    - websecure
  routes:
  - match: Host(`api.example.com`)
    kind: Rule
    services:
    - name: api-service
      port: 80
    middlewares:
    - name: api-auth
    - name: api-ratelimit
  tls:
    certResolver: letsencrypt

---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: api-auth
  namespace: default
spec:
  basicAuth:
    secret: api-auth-secret

---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: api-ratelimit
  namespace: default
spec:
  rateLimit:
    average: 100
    burst: 200

---
apiVersion: v1
kind: Secret
metadata:
  name: api-auth-secret
  namespace: default
data:
  users: YWRtaW46JGFwcjEkSDZKeFhXb3lQYVhuE...  # htpasswd格式

負荷分散とヘルスチェック設定

# 重み付きラウンドロビン設定
http:
  services:
    weighted-service:
      weighted:
        services:
        - name: service-v1
          weight: 70
        - name: service-v2
          weight: 30

    service-v1:
      loadBalancer:
        servers:
        - url: "http://app-v1-1:8080"
        - url: "http://app-v1-2:8080"
        healthCheck:
          path: "/health"
          interval: "10s"
          timeout: "3s"
          scheme: "http"

    service-v2:
      loadBalancer:
        servers:
        - url: "http://app-v2-1:8080"
        - url: "http://app-v2-2:8080"
        healthCheck:
          path: "/health"
          interval: "10s"
          timeout: "3s"

    # スティッキーセッション設定
    sticky-service:
      loadBalancer:
        servers:
        - url: "http://backend1:8080"
        - url: "http://backend2:8080"
        sticky:
          cookie:
            name: "server-session"
            secure: true
            httpOnly: true

    # フェイルオーバー設定
    failover-service:
      failover:
        service: primary-service
        fallback: backup-service

    primary-service:
      loadBalancer:
        servers:
        - url: "http://primary-server:8080"
        healthCheck:
          path: "/health"
          interval: "5s"

    backup-service:
      loadBalancer:
        servers:
        - url: "http://backup-server:8080"

高度なミドルウェア設定

# 認証・認可ミドルウェア
http:
  middlewares:
    # OAuth2認証
    oauth-auth:
      forwardAuth:
        address: "http://oauth-provider:4181"
        authResponseHeaders:
          - "X-Forwarded-User"
          - "X-Auth-User"
          - "X-Secret"

    # JWT認証
    jwt-auth:
      plugin:
        jwt:
          secret: "your-jwt-secret"
          alg: "HS256"

    # レート制限
    rate-limit-strict:
      rateLimit:
        extractorFunc: "request.host"
        rateLimitKey: "client.ip"
        average: 6
        period: "1m"
        burst: 12

    # CORS設定
    cors-middleware:
      headers:
        accessControlAllowMethods:
          - GET
          - OPTIONS
          - PUT
          - POST
          - DELETE
        accessControlAllowOriginList:
          - "https://example.com"
          - "https://api.example.com"
        accessControlMaxAge: 100
        addVaryHeader: true

    # セキュリティヘッダー
    security-headers:
      headers:
        contentTypeNosniff: true
        browserXssFilter: true
        forceSTSHeader: true
        stsIncludeSubdomains: true
        stsPreload: true
        stsSeconds: 31536000

    # リクエスト変換
    transform-request:
      addPrefix:
        prefix: "/api/v1"

    # レスポンス変換
    transform-response:
      headers:
        customRequestHeaders:
          X-Custom-Request-Header: "request-value"
        customResponseHeaders:
          X-Custom-Response-Header: "response-value"

  routers:
    protected-api:
      rule: "Host(`secure.example.com`)"
      service: api-service
      middlewares:
        - oauth-auth
        - rate-limit-strict
        - cors-middleware
        - security-headers
        - transform-request
      tls:
        certResolver: letsencrypt

TCPルーティングとSSL終端

# TCP設定
tcp:
  routers:
    mysql-router:
      rule: "HostSNI(`mysql.example.com`)"
      service: mysql-service
      tls:
        passthrough: false
        certResolver: letsencrypt

    postgres-router:
      rule: "HostSNI(`postgres.example.com`)"
      service: postgres-service
      tls:
        passthrough: true

    redis-router:
      rule: "HostSNI(`*`)"
      service: redis-service
      entryPoints:
        - redis

  services:
    mysql-service:
      loadBalancer:
        servers:
        - address: "mysql-master:3306"
        - address: "mysql-slave:3306"
        proxyProtocol:
          version: 1

    postgres-service:
      loadBalancer:
        servers:
        - address: "postgres-primary:5432"
        - address: "postgres-secondary:5432"

    redis-service:
      loadBalancer:
        servers:
        - address: "redis-node1:6379"
        - address: "redis-node2:6379"
        - address: "redis-node3:6379"

entryPoints:
  redis:
    address: ":6379"
  mysql:
    address: ":3306"
  postgres:
    address: ":5432"

監視とログ設定

# traefik.yml
log:
  level: INFO
  format: json

accessLog:
  filePath: "/var/log/traefik/access.log"
  format: json
  fields:
    defaultMode: keep
    headers:
      defaultMode: drop
      names:
        User-Agent: keep
        Authorization: drop
        Content-Type: keep

metrics:
  prometheus:
    addEntryPointsLabels: true
    addServicesLabels: true
    buckets:
      - 0.1
      - 0.3
      - 1.2
      - 5.0

tracing:
  jaeger:
    samplingServerURL: "http://jaeger:14268/api/sampling"
    localAgentHostPort: "jaeger:6831"

ping:
  entryPoint: "ping"

entryPoints:
  ping:
    address: ":8082"

api:
  dashboard: true
  debug: true

トラブルシューティング

# Traefik状態確認
docker logs traefik

# 設定確認
curl http://localhost:8080/api/rawdata

# ルーター状態確認
curl http://localhost:8080/api/http/routers

# サービス状態確認
curl http://localhost:8080/api/http/services

# ミドルウェア状態確認
curl http://localhost:8080/api/http/middlewares

# ヘルスチェック確認
curl http://localhost:8082/ping

# メトリクス確認
curl http://localhost:8080/metrics

# 設定リロード(ファイルプロバイダー)
docker exec traefik kill -SIGUSR1 1

# ログレベル変更
docker exec traefik traefik --log.level=DEBUG

# SSL証明書確認
openssl s_client -connect your-domain.com:443 -servername your-domain.com

# Docker Compose設定確認
docker-compose config