Skip to content
Published on

IngressClass 詳解 — 1つのクラスタで複数の Ingress Controller を運用する

Authors

はじめに: コントローラが1つでは足りないとき

小さなクラスタでは Ingress Controller が1つで十分です。しかし運用規模が大きくなると、1つのクラスタにコントローラが2つ以上必要な状況が自然に生じます。たとえば、インターネットに公開される外部トラフィックと、社内ネットワークにのみ公開される内部トラフィックを分離したいとき、あるいはチームごとに独立した入口を置きたいとき、または ingress-nginx から別のコントローラへ段階的に移している最中などです。

このとき鍵となるメカニズムが IngressClass です。IngressClass は「この Ingress リソースをどのコントローラが処理するか」を決めます。本記事では IngressClass リソースの構造、デフォルトクラス、マルチコントローラのシナリオ、コントローラのスコープ設定、競合の防止、運用・コストの考慮を実務視点で深く扱います。

IngressClass リソースとデフォルトクラス

IngressClass はクラスタスコープのリソースで、どのコントローラ実装がこのクラスを担当するかを spec.controller で明示します。

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: nginx-external
spec:
  controller: k8s.io/ingress-nginx
---
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: nginx-internal
spec:
  controller: k8s.io/ingress-nginx

ここで2つの IngressClass が同じ controller 文字列(k8s.io/ingress-nginx)を使っている点に注目してください。controller 値は「どの種類のコントローラ実装か」を示すだけで、クラス自体は名前(nginx-externalnginx-internal)で区別されます。つまり同じ種類のコントローラを複数インスタンス立て、それぞれ異なるクラスを担当させられます。

Ingress リソースは spec.ingressClassName で自分がどのクラスに属するかを宣言します。

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

デフォルトクラス

IngressClass に次のアノテーションを付けると デフォルトクラス になります。

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: nginx-external
  annotations:
    ingressclass.kubernetes.io/is-default-class: "true"
spec:
  controller: k8s.io/ingress-nginx

ingressClassName を指定しない Ingress はデフォルトクラスへ自動的に割り当てられます。注意すべきは デフォルトクラスはクラスタに1つだけであるべき という点です。2つ以上を default に指定するとどれが適用されるか曖昧になるため、マルチコントローラ環境ではできるだけすべての Ingress に ingressClassName を明示するのが安全です。

アノテーション vs spec.ingressClassName

かつてはクラスを次のアノテーションで指定していました。

metadata:
  annotations:
    kubernetes.io/ingress.class: nginx

しかしこの方式は deprecated であり、現在の推奨方式は spec.ingressClassName フィールドです。両者の違いは次のとおりです。

区分kubernetes.io/ingress.class(アノテーション)spec.ingressClassName(フィールド)
状態deprecated推奨
検証自由文字列、検証なしIngressClass の存在を検証可能
デフォルトクラス非サポートis-default-class でサポート
マルチコントローラ動作するが非標準標準メカニズム

新規環境ではアノテーションを使わず spec.ingressClassName を使ってください。ただし一部のコントローラは後方互換のため依然アノテーションを認識するので、移行中は両方式が混在することがあります。その場合どちらが優先されるかはコントローラ実装によって異なるため、一方に統一すると混乱が減ります。

コントローラのスコープ — どの Ingress を watch するか

同じ種類のコントローラを複数インスタンス立てるときは、各インスタンスが 自分の担当するクラスの Ingress だけ を処理するようにスコープを狭める必要があります。ingress-nginx はそのためのいくつかの引数を提供します。

--ingress-class / class の設定

各コントローラインスタンスに担当クラスを指定します。ingress-nginx の Helm values の例は次のとおりです。

controller:
  ingressClassResource:
    name: nginx-external
    enabled: true
    default: false
    controllerValue: "k8s.io/ingress-nginx"
  ingressClass: nginx-external
  electionID: ingress-nginx-external-leader

内部用コントローラは別の values でデプロイします。

controller:
  ingressClassResource:
    name: nginx-internal
    enabled: true
    default: false
    controllerValue: "k8s.io/ingress-nginx"
  ingressClass: nginx-internal
  electionID: ingress-nginx-internal-leader

ここで electionID をインスタンスごとに異なる値にするのが重要です。同じリーダー選出キーを共有すると2つのインスタンスが互いに干渉することがあります。

watch-namespace

コントローラが特定の名前空間の Ingress だけを watch するよう制限することもできます。

controller:
  scope:
    enabled: true
    namespace: team-a

こうするとチーム A のコントローラは team-a 名前空間の Ingress だけを処理します。クラスベースの分離と名前空間ベースの分離を組み合わせると、より細かい隔離が可能です。

マルチコントローラのシナリオ

実務でよく登場する3つのシナリオをまとめます。

1. 内部/外部の分離

最も一般的なパターンです。外部コントローラはインターネットに公開される LoadBalancer を、内部コントローラは社内ネットワーク専用の LoadBalancer(または internal LB)を使います。

                Internet                       Internal Network
                   │                                  │
                   ▼                                  ▼
        ┌──────────────────┐              ┌──────────────────┐
        │  External LB      │              │  Internal LB      │
        └────────┬─────────┘              └────────┬─────────┘
                 │                                  │
                 ▼                                  ▼
   ┌──────────────────────┐          ┌──────────────────────┐
   │ ingress-nginx         │          │ ingress-nginx         │
   │ class: nginx-external │          │ class: nginx-internal │
   └──────────┬───────────┘          └──────────┬───────────┘
              │                                  │
              ▼                                  ▼
       public services                   admin/internal services

外部 Ingress は ingressClassName: nginx-external、内部 Ingress は ingressClassName: nginx-internal で分離します。こうすると管理画面のような機微なサービスを誤ってインターネットに公開する事故を減らせます。

2. チーム別の分離

マルチテナント環境でチームごとに独立したコントローラを置くパターンです。各チームは自分のクラス(例: team-a-nginx、team-b-nginx)だけを使い、コントローラもチームの名前空間だけを watch するようスコープを狭めます。こうすると、あるチームのトラフィック急増や設定ミスが他チームに及ぼす影響を減らせます。

3. 移行

ingress-nginx から別のコントローラ(例: Traefik、Envoy ベース)へ移している間は、2つのコントローラがしばらく共存します。既存サービスは既存クラスのまま残し、新しいサービスや一部のトラフィックだけを新しいクラスへ移してから段階的に切り替えます。2026年のコンテキストで Gateway API へ移す場合も、Ingress コントローラと Gateway コントローラがしばらく並行運用されます。

競合の防止

マルチコントローラの核心リスクは 2つのコントローラが同じ Ingress を同時に処理する、または同じホストを重複して公開する ことです。次の原則で防ぎます。

  1. すべての Ingress に ingressClassName を明示: デフォルトクラスに依存しないと「どのコントローラが掴むか」の曖昧さがなくなります。
  2. デフォルトクラスは最大1つ: default を2つ以上に指定しません。
  3. electionID の分離: 同じ種類のコントローラインスタンスごとにリーダー選出キーを異なる値にします。
  4. ホストの重複禁止: 2つのクラスが同じホストを公開しないようにします。特に移行中はカットオーバーまでホストを分離します。
  5. スコープを狭める: 必要なら watch-namespace でコントローラの管轄範囲を明確に制限します。

コストと運用の考慮

マルチコントローラは隔離と安全性をもたらしますが、コストと運用の複雑さを伴います。

  • ロードバランサのコスト: コントローラごとに LoadBalancer タイプの Service を置くとクラウドのロードバランサが追加されます。内部コントローラは internal LB にする、あるいは単一の LB の背後でホストで分岐する方式でコストを抑えられます。
  • リソースのオーバーヘッド: 各コントローラは自前の Pod、メモリ、CPU を消費します。コントローラの数が増えるほど管理対象も増えます。
  • 運用の一貫性: コントローラごとにバージョン、アノテーション構文、チューニングパラメータがバラバラだと運用が難しくなります。可能なら同じ種類のコントローラを複数クラスに分けるほうが運用の一貫性に有利です。
  • 可観測性: コントローラごとにメトリクス/ログを分離収集し、どのクラスがどのトラフィックを処理しているかをダッシュボードで明確にします。

また2026年現在 ingress-nginx がメンテナンスモード寄りの流れであることを踏まえると、新しいマルチ入口を設計する際は Gateway API の複数 Gateway モデルも併せて検討するのが合理的です。Gateway API は複数の Gateway を GatewayClass で区別するため、マルチ入口を標準的に表現できます。

実習 YAML

内部/外部の2クラスを作り、それぞれに Ingress を付ける全体例です。

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: nginx-external
spec:
  controller: k8s.io/ingress-nginx
---
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: nginx-internal
spec:
  controller: k8s.io/ingress-nginx
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: public-api
  namespace: shop
spec:
  ingressClassName: nginx-external
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: api-service
                port:
                  number: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: admin-panel
  namespace: shop
spec:
  ingressClassName: nginx-internal
  rules:
    - host: admin.internal.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: admin-service
                port:
                  number: 80

作成後の確認コマンドは次のとおりです。

kubectl get ingressclass
kubectl get ingress -A -o custom-columns=NAME:.metadata.name,CLASS:.spec.ingressClassName,HOSTS:.spec.rules[*].host

落とし穴とトラブルシューティング

1. Ingress がどのコントローラにも掴まれない。 ingressClassName が実在する IngressClass 名と異なるか、デフォルトクラスがなくクラス未指定の Ingress が放置されている場合です。IngressClass の一覧と Ingress のクラス指定を突き合わせます。

kubectl get ingressclass
kubectl describe ingress admin-panel

2. 2つのコントローラが同じ Ingress を処理する。 同じ種類のコントローラがクラススコープを正しく狭めていないと起こり得ます。各コントローラの ingressClass 設定と electionID が分離されているか確認します。

3. デフォルトクラスが2つあり動作が予測不能。 is-default-class: "true" が2つの IngressClass に付いていないか確認し、1つだけ残します。

kubectl get ingressclass -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.metadata.annotations.ingressclass\.kubernetes\.io/is-default-class}{"\n"}{end}'

4. 内部サービスがインターネットに公開された。 内部用 Ingress に外部クラスを誤って指定した場合です。機微なサービスの ingressClassName を再確認し、ポリシー(例: OPA/Kyverno)で強制することを検討します。

5. 移行中にトラフィックが混ざる。 既存クラスと新しいクラスが同じホストを同時に公開するとトラフィックが分散します。カットオーバーまではホストや重みで分離し、一度に切り替えます。

おわりに

マルチコントローラ運用の出発点は IngressClass を正確に理解することです。spec.controller でコントローラの種類を、IngressClass 名でインスタンスを区別し、Ingress の spec.ingressClassName でどのクラスが処理するかを明示します。デフォルトクラスは1つだけにし、同じ種類のコントローラは ingressClasselectionID でスコープを分離し、必要なら watch-namespace で管轄を狭めます。

内部/外部の分離、チーム別の隔離、移行という代表的なシナリオで、マルチコントローラは強力なツールです。ただしロードバランサのコストと運用の複雑さという代償が伴うため、競合防止の原則を守り、可観測性を備えることが重要です。2026年のコンテキストでは ingress-nginx のメンテナンスの流れを踏まえ、新しいマルチ入口の設計には Gateway API の複数 Gateway モデルも併せて検討することをお勧めします。

参考資料