Kubernetes Ingress ControllerとしてTraefik v3をデプロイ:動的ルーティング、セキュリティミドルウェア、Let’s Encrypt SSLの設定

DevOps tutorial - IT technology blog
DevOps tutorial - IT technology blog

クラスターの規模が拡大し、稼働するサービスがほんの数個を超えたあたりから、標準のKubernetes Ingressリソースは急速に限界を迎えます。煩雑なアノテーションの管理に追われ、不安定な正規表現リライトと格闘し、基本的なTLS証明書の発行やレート制限を導入するためだけに別々のコントローラー群をインストールする羽目になります。

Traefik v3はこうした課題をスマートに解決します。ネイティブのカスタムリソース定義(CRD)をベースに設計されており、サードパーティ製プラグインに頼ることなく、動的ルーティング、Let’s Encrypt証明書の自動発行、ミドルウェアのチェーン処理を標準機能だけで処理できます。

クイックスタート:HelmによるTraefik v3のインストール

Traefik v3は約5分で稼働させることができます。Helmを使用すれば、専用のネームスペースへのコントローラーのデプロイや、HTTP、HTTPS、管理ダッシュボード用のポートルーティング設定など、一連のプロセスを極めてシンプルに行えます。

Traefikの公式リポジトリを追加し、ローカルインデックスを更新します:

helm repo add traefik https://traefik.github.io/charts
helm repo update

基本的なvalues-quickstart.yamlファイルを作成します。この構成では、耐障害性を考慮して2つのPodを立ち上げ、80番と443番のポートを公開し、内部Webダッシュボードを有効化します:

# values-quickstart.yaml
deployment:
  enabled: true
  replicas: 2

ingressRoute:
  dashboard:
    enabled: true
    matchRule: Host(`traefik.local`)
    entryPoints: ["web"]

ports:
  web:
    port: 8000
    exposedPort: 80
  websecure:
    port: 8443
    exposedPort: 443

service:
  type: LoadBalancer

新規作成するtraefikネームスペースにチャートをデプロイします:

helm install traefik traefik/traefik \
  --namespace traefik \
  --create-namespace \
  -f values-quickstart.yaml

クラウドプロバイダー(AWS NLBやGCP Cloud Load Balancingなど)によって外部IPがプロビジョニングされるまで、30〜60秒ほど待ちます:

kubectl get svc -n traefik traefik -w

本番環境のDNSレコードをクラスターに向ける前に、ポートフォワードを使ってローカルから管理インターフェースへのアクセスをテストします:

kubectl port-forward -n traefik $(kubectl get pods -n traefik -l app.kubernetes.io/name=traefik -o jsonpath='{.items[0].metadata.name}') 9000:9000

ブラウザでhttp://localhost:9000/dashboard/にアクセスします。アクティブなすべてのEntryPoints、ルーター、バックエンドサービスのステータスがインタラクティブな画面で確認できます。

コアアーキテクチャ:CRDによる動的ルーティング

従来のKubernetes Ingressオブジェクトは、ベンダー固有のアノテーションに大きく依存していました。こうしたアノテーションはマニフェストを脆弱にし、CI/CDパイプラインでの検証を困難にします。Traefikは、3つの専用カスタムリソース定義(CRD)であるIngressRoute、Middleware、TraefikServiceを採用することで、この問題を根本から解消しています。

IngressRouteと標準Kubernetes Ingressの比較

Traefikは後方互換性のために標準のIngressオブジェクトも引き続き解釈できます。しかし、ネイティブのIngressRouteマニフェストを使用することで、はるかに直感的でクリーンなルーティング構文が利用可能です。解読が難しいNGINXのリライト正規表現を書く代わりに、Host(`api.example.com`) && PathPrefix(`/v1`)のような可読性の高い論理式で記述できます。

動的ルーティングを使用したサンプルアプリケーションのデプロイ

defaultネームスペースに2つのレプリカを持つ軽量なHTTP echoサービスを起動します:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-app
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      app: demo-app
  template:
    metadata:
      labels:
        app: demo-app
    spec:
      containers:
      - name: web
        image: hashicorp/http-echo
        args: ["-text=Traefik v3 Ingressへようこそ!"]
        ports:
        - containerPort: 5678
---
apiVersion: v1
kind: Service
metadata:
  name: demo-service
  namespace: default
spec:
  ports:
  - port: 80
    targetPort: 5678
  selector:
    app: demo-app

IngressRouteカスタムリソースを使用して、このデプロイメントを外部へ公開します:

apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
  name: demo-ingress-route
  namespace: default
spec:
  entryPoints:
    - web
  routes:
    - match: Host(`demo.example.com`) && PathPrefix(`/api`)
      kind: Rule
      services:
        - name: demo-service
          port: 80

これらのマニフェストをkubectl apply -f app.yamlで適用します。TraefikはKubernetes APIサーバーのリソースイベントを常に監視しています。処理中のTCP接続を切断したりコアバイナリをリロードしたりすることなく、50ミリ秒未満で内部ルーティングテーブルを瞬時に更新します。

応用設定:セキュリティミドルウェアとSSLの自動化

本番環境の運用には、強固なHTTPセキュリティヘッダー、動的なレート制限、そして手動運用のいらないTLSライフサイクル管理が不可欠です。

1. ACMEによるSSL自動化(Let’s Encrypt)の設定

Traefikには組み込みのACMEクライアントが備わっています。証明書の発行から更新までのライフサイクル全体を単体で完結し、生成された鍵情報をディスクに保存します。そのため、単一のIngress構成であれば、cert-managerのCRDを追加でインストール・運用する必要がありません。

HTTP-01チャレンジの自動化とHTTPSへの強制リダイレクトを有効にするため、この構成をvalues-production.yamlとして保存します。なお、単一のReadWriteOnceボリュームは複数ノード間で共有できないため、レプリカ数は1に設定している点にご注意ください:

# values-production.yaml
deployment:
  replicas: 1

persistence:
  enabled: true
  name: data
  accessMode: ReadWriteOnce
  size: 1Gi
  path: /data

additionalArguments:
  - "[email protected]"
  - "--certificatesresolvers.letsencrypt.acme.storage=/data/acme.json"
  - "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
  - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
  - "--entrypoints.web.http.redirections.entrypoint.to=websecure"
  - "--entrypoints.web.http.redirections.entrypoint.scheme=https"

これらの引数により、ポート80への受信トラフィックがHTTPSへリダイレクトされます。同時に、ポート80経由でチャレンジをスムーズに解決するletsencryptリゾルバーが構成されます。

実行中のリリースにこれらの設定変更を反映します:

helm upgrade traefik traefik/traefik -n traefik -f values-production.yaml

2. セキュリティミドルウェアの適用

ミドルウェアは、Traefikと背後のPodとの間のリクエスト経路に直接配置されます。リクエストがアプリケーションコードに到達する前に、ヘッダーの書き換え、認証トークンの挿入、不正トラフィックの遮断などを実行できます。

以下は本番環境向けのミドルウェアマニフェストです。必須とされるOWASPセキュリティヘッダーを適用し、受信トラフィックを毎秒100リクエスト(バースト許容値50)に制限します:

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: app-security-headers
  namespace: default
spec:
  headers:
    frameDeny: true
    sslRedirect: true
    browserXssFilter: true
    contentTypeNosniff: true
    stsIncludeSubdomains: true
    stsPreload: true
    stsSeconds: 31536000
    customResponseHeaders:
      X-Robots-Tag: "none,noindex,nofollow"
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: app-ratelimit
  namespace: default
spec:
  rateLimit:
    average: 100
    burst: 50

3. IngressRouteでTLSとミドルウェアを統合する

定義したセキュリティポリシーと自動TLSリゾルバーを、HTTPS対応のIngressRouteに紐付けます:

apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
  name: demo-secure-route
  namespace: default
spec:
  entryPoints:
    - websecure
  routes:
    - match: Host(`app.example.com`)
      kind: Rule
      services:
        - name: demo-service
          port: 80
      middlewares:
        - name: app-security-headers
        - name: app-ratelimit
  tls:
    certResolver: letsencrypt

45のマイクロサービスにわたって毎秒12,000リクエスト以上を処理するような高スループット環境でも、この構成は極めて安定して動作します。ミリ秒未満のルーティングオーバーヘッドにより、p99レイテンシーを5ms未満に抑えつつ、突発的なバーストトラフィックがバックエンドデータベースに過負荷を与えるのを防ぎます。

Day-2運用:本番運用のためのプラクティス

本番環境の継続的な負荷の下でTraefikを安定運用するには、ステート管理、グローバルルール、可観測性(オブザーバビリティ)に関する設計が不可欠です。

高可用性(HA)クラスターにおけるACMEストレージロックの対策

TraefikはACME証明書データを単一のローカルファイル(acme.json)に書き込みます。Kubernetesの標準的な永続ボリューム(EBS、GPD、Ceph RBDなど)はReadWriteOnceモードを使用するため、複数のPodから同時に書き込むことができません。共有ファイルリゾルバーを使用したままTraefikを複数レプリカにスケールアウトすると、ファイルロックの競合が発生し、証明書データが破損する原因となります。

マルチレプリカのIngress構成では、実績のある以下の2つの選択肢から検討してください:

  • cert-managerに証明書発行を委譲する: cert-managerを個別に運用します。発行されたTLS証明書はネイティブのKubernetes Secretオブジェクトに保存されるため、複数のTraefik Podがtls.secretName経由で同時にマウントして利用できます。
  • Traefik Enterpriseへアップグレードする: サードパーティ製オペレーターを導入せずネイティブなクラスタリングを好む場合は、Raftコンセンサスアルゴリズムを用いて分散ノード間で証明書を同期できるEnterprise版が最適です。

EntryPointへのグローバルミドルウェアの適用

50ものマイクロサービスのマニフェストそれぞれで同じセキュリティヘッダーを参照するように記述すると、人為的ミスの温床になります。代わりに、HelmのEntryPoint定義に共通ミドルウェアを直接アタッチするのがベストプラクティスです:

ports:
  websecure:
    middlewares:
      - default-app-security-headers@kubernetescrd

これにより、開発者が各サービスのマニフェストでミドルウェアの定義を忘れてしまった場合でも、TraefikがHSTSやレート制限をグローバルに強制適用します。

Prometheusによるゴールデンシグナルの監視

Traefikはポート9100でネイティブなPrometheusテレメトリを公開します。Helmのvalues設定でメトリクス収集を有効化します:

metrics:
  prometheus:
    entryPoint: metrics
    addEntryPointsLabels: true
    addServicesLabels: true

このエンドポイントをPrometheus OperatorのServiceMonitorと連携させます。traefik_entrypoint_request_duration_seconds_bucketを追跡することでユーザーに影響が出る前にレイテンシーの悪化を検知し、traefik_service_requests_total{code=~"5.."}にしきい値アラートを設定してエラーをリアルタイムに検知できるようにします。

Share: