Skip to content
Published on

Ingress マルチテナンシーとコスト最適化 — プラットフォームチームの選択

Authors

はじめに

複数のチームが一つのKubernetesクラスタを共有するマルチテナント環境において、Ingressは厄介なテーマです。外部入口が共有リソースであるため、「あるチームが誤設定すると別のチームが被害を受ける」リスクが常に存在します。同時にコスト面では、「テナントごとにロードバランサを別々に持つと請求書が掛け算で増える」という現実的な圧力があります。

プラットフォームチームの課題は、分離(あるチームの問題が他チームへ波及しないように)とコスト効率(リソースを共有して請求書を減らすように)を同時に満たすことです。この2つはしばしば相反します。強い分離はリソース分離を要求し、リソース分離はコストを増やします。

本記事では、共有コントローラとテナント別コントローラのトレードオフから始めて、ネームスペース分離、ロードバランサのコスト構造、リソースクォータ、ノイジーネイバー対策、セキュリティ境界、チャージバック/ショーバックモデル、大規模Ingress運用、そしてプラットフォームチームのセルフサービスの観点まで、マルチテナントIngress運用とコスト最適化を総合的に扱います。

共有コントローラ vs テナント別コントローラ

最も根本的な決定は「Ingress Controllerを全チームで共有するか、チームごとに別々に持つか」です。

   共有コントローラモデル            テナント別コントローラモデル
   ───────────────────             ─────────────────────────
        外部LB(1個)                  team-a LB   team-b LB
           │                            │           │
   ┌───────────────┐             ┌──────────┐ ┌──────────┐
   │ 共有Ingress     │             │ a ctrl    │ │ b ctrl    │
   │ Controller      │             └────┬─────┘ └────┬─────┘
   └───────┬───────┘                   │            │
     ┌─────┼─────┐                   team-a       team-b
   team-a team-b team-c             (強い分離)    (強い分離)
   (コスト効率, 弱い分離)

2つのモデルのトレードオフを整理すると次のとおりです。

項目共有コントローラテナント別コントローラ
ロードバランサコスト低(1個共有)高(テナント数分)
分離の強度弱(設定/障害を共有)強(独立プロセス)
運用の複雑さ低(中央管理)高(多数を管理)
ノイジーネイバーリスク
ブラスト半径大(全員に影響)小(テナント限定)
バージョン独立性低(一括アップグレード)高(個別アップグレード)

一般に多数の小規模チームには共有コントローラが、強い規制要求や非常に大きいトラフィックを持つ少数テナントにはテナント別コントローラが有利です。多くの組織は両者を混ぜ、一般チームは共有コントローラを使いつつ特殊要求チームだけ専用コントローラを持つハイブリッドを採用します。

ネームスペース分離

共有クラスタの基本的な分離単位はネームスペースです。コントローラに特定のネームスペースだけを監視させるか、チームごとにIngressClassを分離する方式があります。

コントローラが特定のネームスペースだけを監視するように制限するには、watch-namespaceを指定します。

# Helm values 例 (ingress-nginx)
controller:
  scope:
    enabled: true
    namespace: "team-a"
  ingressClassResource:
    name: team-a-nginx
    controllerValue: "k8s.io/team-a-nginx"

こうすると、team-aコントローラはteam-aネームスペースのIngressだけを処理するため、他チームの誤設定がこのコントローラに影響しません。チーム別IngressClassと組み合わせると、ルーティングの所有権が明確になります。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web
  namespace: team-a
spec:
  ingressClassName: team-a-nginx
  rules:
    - host: team-a.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web
                port:
                  number: 80

共有コントローラを使いつつ分離を強化するなら、ContourのHTTPProxy委譲モデルが優雅です。ルートドメイン定義はプラットフォームチームが所有し、配下のパスルーティングだけを各チームのネームスペースへ委譲します。

# プラットフォームチーム: ルート定義 + 委譲
apiVersion: projectcontour.io/v1
kind: HTTPProxy
metadata:
  name: root
  namespace: platform
spec:
  virtualhost:
    fqdn: example.com
  includes:
    - name: team-a-routes
      namespace: team-a
      conditions:
        - prefix: /team-a

ロードバランサコスト

マルチテナントコストにおける最大の変数はクラウドロードバランサです。LoadBalancerタイプのサービス一つごとにクラウドLBがプロビジョニングされ、時間あたり料金とスループット料金が課されます。

   テナント別LB (コスト高)           共有LB (コスト低)
   ──────────────────             ─────────────────
   team-a -> LB1 (月額)             全チーム -> LB1 (月額1個分)
   team-b -> LB2 (月額)                   │
   team-c -> LB3 (月額)             共有コントローラがホスト/パスで分岐
   = LB料金 x 3                     = LB料金 x 1

コストを下げる肝はLoadBalancerサービスの個数を最小化することです。共有コントローラ一つが単一LBの背後でホスト/パスベースで全テナントをルーティングすれば、LBの固定料金をテナントたちが分担します。テナント別コントローラがどうしても必要でも、可能なら共有LBの背後に複数のコントローラを配置する構成を検討する価値があります。

リソースクォータ

共有コントローラ環境では、あるテナントがリソースを独占して他テナントを枯渇させないようにクォータをかける必要があります。ネームスペース単位のResourceQuotaとLimitRangeを使います。

apiVersion: v1
kind: ResourceQuota
metadata:
  name: team-a-quota
  namespace: team-a
spec:
  hard:
    requests.cpu: "4"
    requests.memory: 8Gi
    limits.cpu: "8"
    limits.memory: 16Gi
    count/ingresses.networking.k8s.io: "20"

Ingressの個数自体にもクォータをかけ、あるチームが数千個のIngressを作ってコントローラの設定生成負荷を爆発させるのを防ぎます。コントローラのPodには別途適切なrequests/limitsを設定し、トラフィック急増時でも安定して動作するようにします。

ノイジーネイバー対策

共有コントローラの最大のリスクは、あるテナントの暴走がコントローラ全体を麻痺させることです。これを防ぐ仕組みです。

  • レートリミット: テナント別のリクエスト速度制限で、あるチームがコントローラのスループットを独占しないようにします。
  • コネクション制限: 同時接続数の上限。
  • タイムアウト: 遅いバックエンドがワーカを占有しないよう合理的なタイムアウト。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web
  namespace: team-a
  annotations:
    nginx.ingress.kubernetes.io/limit-rps: "100"
    nginx.ingress.kubernetes.io/limit-connections: "50"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "30"
spec:
  ingressClassName: shared-nginx
  rules:
    - host: team-a.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web
                port:
                  number: 80

ただしこのようなアノテーションベースの任意設定注入はセキュリティ問題がありうるため、可能ならプラットフォームチームが検証したポリシーだけを許可するゲート(ポリシーエンジン)を置くのがよいです。

セキュリティ境界

マルチテナントではルーティングがすなわちセキュリティ境界です。あるチームが他チームのホストやパスを横取りするとトラフィック窃取が発生します。次を強制すべきです。

  • ホスト名所有権の検証: ポリシーエンジン(例: 検証ウェブフック)で、チームが自分に割り当てられたドメインだけをIngressに使えるよう強制します。
  • IngressClass強制: チームは自分のIngressClassだけを使えるよう制限します。
  • TLS Secret分離: 証明書Secretがネームスペースをまたがないようにします。
  • アノテーション許可リスト: 危険なsnippet系アノテーションを遮断します。

ポリシーエンジンで「team-aネームスペースのIngressはホストが必ずteam-aで始まらねばならない」といったルールを強制すれば、ホスト衝突と窃取を根本から遮断できます。

チャージバック/ショーバックモデル

プラットフォームチームが共有インフラを運用するとき、コストをテナントへどう配分するかが重要です。2つのアプローチがあります。

  • チャージバック: 実際のコストをテナントへ請求。明確だが精密な計測が必要です。
  • ショーバック: 請求はしないが各チームの使用量/コストを可視化。行動改善を促します。

共有LBと共有コントローラの固定コストはテナントのトラフィック比率で按分し、テナント別の専用コントローラや追加LBは当該チームへ直接帰属させる方式が一般的です。

コスト項目配分方式
共有LB固定料金トラフィック比率で按分
共有コントローラのコンピュートリクエスト数/帯域比率で按分
専用コントローラ当該テナントへ全額帰属
追加LB当該テナントへ全額帰属
データ転送(egress)テナント別egress計測で帰属

Prometheusメトリクスからテナント(ネームスペース/ホスト)ラベルでリクエスト数と帯域を集計すれば、ショーバックレポートを自動化できます。

大規模Ingress運用

テナントとIngressが数百、数千個に増えると、コントローラ自体がボトルネックになります。大規模運用の肝心なポイントです。

  • 設定reload負荷: nginx系はIngressが変わるたびに設定を再生成/reloadします。Ingressが多いとreloadが頻繁で重くなります。Envoy系(xDS)は動的更新なのでこの負荷が少ないです。
  • コントローラのシャーディング: 多すぎるIngressを一つのコントローラが捌けなければ、ネームスペース/IngressClass基準でコントローラをシャーディングします。
  • メモリ/CPUスケール: Ingress数に比例してコントローラのリソース要求が増えるため、監視後に適切に増設します。
  • 状態の可視化: reload時間、設定生成遅延、キュー長といったコントローラ内部メトリクスを観察します。

数千個規模では、reloadベースより動的更新(Envoy系)コントローラの方が運用上有利な場合が多いです。

プラットフォームチームの観点: セルフサービス

プラットフォームチームの目標は「各チームがプラットフォームチームへ毎回依頼せずとも安全にIngressを作れる」ようにすることです。そのためのパターンです。

  • ゴールデンパスの提供: 検証済みIngressClass、デフォルトTLS発行(cert-manager)、デフォルトポリシーが揃ったテンプレートを提供します。
  • ポリシーによるガードレール: ポリシーエンジンでホスト所有権、アノテーション許可リスト、IngressClass強制といった安全ルールを自動検証します。
  • ショーバックで責任感を付与: 各チームが自分のコスト/使用量を見られるようにして自律的な最適化を促します。
  • 抽象化されたインターフェイス: チームはホストとバックエンドだけを宣言し、複雑なコントローラ設定はプラットフォームが隠します。Gateway APIの役割分離(GatewayClass/Gatewayはプラットフォーム、HTTPRouteはアプリチーム)がこのモデルによく合います。

Gateway APIは本来マルチテナントと役割分離を念頭に設計されており、プラットフォームチームのセルフサービスモデルに自然に当てはまります。Ingressがfrozenとなった流れで、マルチテナントプラットフォームならGateway API導入を積極的に検討する価値があります。

事例シナリオ

仮想の組織事例で整理してみます。50チームが一つのクラスタを共有します。

  • 一般チーム(45個): 単一の共有コントローラ + 単一の共有LB。ネームスペースクォータ、ホスト所有権ポリシー、レートリミットのガードレールを適用。コストはトラフィック比率でショーバック。
  • 高トラフィックチーム(3個): ノイジーネイバーを避けるため専用コントローラ。ただし共有LBの背後に配置して追加LBコストは回避。
  • 規制チーム(2個): 強い分離要求で専用コントローラ + 専用LB。コストは全額当該チームへ帰属。

このようにハイブリッドにすると、多数チームのコスト効率を保ちつつ特殊要求を満たし、コスト帰属も公正になります。

おわりに

マルチテナントIngress運用は、分離とコスト効率という相反する目標の間でのバランス取りです。核心原則を整理すると、第一に、基本は共有コントローラ + 共有LBでコストを下げ、特殊要求チームだけ専用に分離するハイブリッドです。第二に、ネームスペース分離、クォータ、レートリミット、ホスト所有権ポリシーで、ノイジーネイバーとセキュリティ境界の問題をガードレールで防ぎます。第三に、ショーバックで各チームにコスト可視性と責任感を与え、自律的な最適化を促します。

そして2026年の流れで、マルチテナントプラットフォームを新しく設計するなら、役割分離が内蔵されたGateway APIをセルフサービスの基盤とすることを強くおすすめします。Ingressがfrozenとなった今、マルチテナンシーはGateway APIが最も輝く領域の一つです。

参考資料