Skip to content
Published on

Ingress Controller 完全理解 — Ingress リソースからコントローラの動作原理まで

Authors

はじめに: なぜ Ingress は最初から紛らわしいのか

Kubernetes を初めて運用するときに最も多く聞かれる質問の一つが「Service で LoadBalancer を作ればいいのに、なぜ Ingress が必要なのか」というものです。小さなクラスタでサービスが1つや2つなら、LoadBalancer タイプの Service だけでも十分です。しかしサービスが数十個に増えると話は変わります。サービスごとにクラウドのロードバランサを1つずつ立てると、コストが線形に増加し、各ロードバランサごとに別々のパブリック IP と TLS 証明書を管理しなければなりません。

Ingress はこの問題を解決します。単一の入口(通常は1つのロードバランサ)の背後で、ホスト名と URL パスを基準にトラフィックを複数のバックエンドサービスへルーティングします。L7(HTTP/HTTPS)レベルで動作するため、パスベースのルーティング、ホストベースの仮想ホスティング、TLS 終端を一箇所で処理できます。

ところが実務で最大の混乱は「Ingress が2つのものを同時に指す」点にあります。一つは Ingress リソース(API オブジェクト)で、もう一つは Ingress Controller(実際に動作するプロキシ)です。この2つを区別できないと、「Ingress を作ったのになぜ何も起きないのか」というような罠にはまります。本記事ではこの区別から出発し、動作原理、リソース仕様、TLS、コントローラ比較、そして2026年現在の Gateway API への移行の流れまで順を追って扱います。

Ingress API vs Ingress Controller — 最も重要な区別

まず2つの概念を明確に分けましょう。

  • Ingress リソース(API オブジェクト): 「example.com の /api パスは api-service へ送れ」というルーティングルールを宣言的に記述した YAML 仕様です。これ自体は etcd に保存されたデータにすぎず、どのトラフィックも処理しません。
  • Ingress Controller: クラスタで実際に動作するプログラム(通常はリバースプロキシ)です。Ingress リソースを watch し、変更を検知すると自身の設定をそれに合わせて生成し、入ってくるトラフィックをルールどおりにルーティングします。

核心は Ingress リソースを作ってもコントローラがクラスタにインストールされていなければ何も起きない という点です。Deployment を作るとコントロールプレーンが自動的に Pod を立ち上げますが、Ingress は別途インストールしたサードパーティのコントローラがあって初めて意味を持ちます。これが他のリソースと決定的に異なる部分です。

例えるなら Ingress リソースは「注文票」で、Ingress Controller は「厨房」です。注文票だけ積み上げても厨房がなければ料理は出てきません。

代表的なコントローラには ingress-nginx、Traefik、HAProxy Ingress、Contour(Envoy ベース)、Kong、APISIX などがあります。クラウドプロバイダも独自のコントローラ(AWS Load Balancer Controller、GKE Ingress など)を提供しています。

動作の流れ: API watch から reload まで

Ingress Controller の内部動作は、ほとんど次のような制御ループ(control loop)に従います。

                        Kubernetes API Server
                  (1) watch Ingress / Service / Endpoints / Secret
        ┌───────────────────────────────────────────┐
        │            Ingress Controller               │
        │                                             │
        │   (2) 変更検知 → 内部モデル更新              │
        │   (3) ルーティング設定ファイル/オブジェクト生成 │
        │       (例: nginx.conf テンプレートのレンダリング) │
        │   (4) プロキシ reload または動的 reconfigure  │
        │                                             │
        │   ┌─────────────┐      ┌──────────────┐     │
        │   │ Controller   │ ───▶ │  Data Plane   │     │
        │   │  (watcher)   │ cfg  │ (nginx/envoy) │     │
        │   └─────────────┘      └──────────────┘     │
        └───────────────────────────────────────────┘
                       (5) 外部トラフィックのルーティング
   Client ─▶ LoadBalancer ─▶ Controller Pod ─▶ Backend Service ─▶ Pod

段階ごとに見ると次のとおりです。

  1. Watch: コントローラは API サーバに watch 接続を張り、Ingress、Service、Endpoints(または EndpointSlice)、TLS Secret などの変更をリアルタイムに購読します。
  2. モデル更新: 変更イベントが届くと、コントローラ内部にルーティングモデルを再構成します。
  3. 設定生成: そのモデルをデータプレーンが理解できる形に変換します。ingress-nginx なら nginx.conf をテンプレートでレンダリングし、Envoy ベースのコントローラなら xDS 設定を作ります。
  4. 適用(reload/reconfigure): nginx のように設定ファイルベースの場合は reload を実行し、Envoy のように動的設定をサポートする場合は無停止で reconfigure します。ingress-nginx も動的な backend 変更(エンドポイントの追加/削除)は Lua モジュールで reload なしに処理します。
  5. トラフィックルーティング: データプレーンが実際の HTTP リクエストをルールどおりにバックエンドサービスへ転送します。

ここで重要なのは コントローラがトラフィックをバックエンドサービスの ClusterIP ではなく直接 Pod エンドポイントへ送る場合が多い という点です。ingress-nginx はデフォルトで Endpoints/EndpointSlice を watch し、個々の Pod IP へ負荷分散します。そのため kube-proxy の iptables ラウンドロビンを経由せず、コントローラ自身のロードバランシング(例: ewma、round_robin)を適用できます。

Ingress リソース仕様の詳細

ここから Ingress リソースの仕様をフィールドごとに見ていきます。次は networking.k8s.io/v1 を基準とした典型例です。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: shop-ingress
  namespace: shop
spec:
  ingressClassName: nginx
  defaultBackend:
    service:
      name: fallback-service
      port:
        number: 80
  rules:
    - host: shop.example.com
      http:
        paths:
          - path: /api
            pathType: Prefix
            backend:
              service:
                name: api-service
                port:
                  number: 8080
          - path: /static
            pathType: Prefix
            backend:
              service:
                name: static-service
                port:
                  number: 80
    - host: admin.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: admin-service
                port:
                  number: 3000

rules と host

rules はルーティングルールの配列です。各ルールは任意で host を持てます。host が指定されると、リクエストの Host ヘッダが一致するときだけそのルールが適用されます。host を省略するとすべてのホストに対して適用されます。ワイルドカードホスト(*.example.com)もサポートされますが、単一ラベルのみマッチするという制約があります。つまり *.example.coma.example.com にはマッチしますが a.b.example.com にはマッチしません。

paths と pathType

host の下には複数の paths を置けます。pathType はパスのマッチ方式を決定し、3つの値があります。

  • Prefix: パスをスラッシュ単位の要素に分け、接頭辞でマッチします。/api/api/api/v1 にマッチしますが /apixyz にはマッチしません。
  • Exact: パスが正確に一致する必要があります。大文字小文字を区別します。
  • ImplementationSpecific: マッチ方式をコントローラ実装に委ねます。ingress-nginx はこの場合、正規表現など独自のルールを適用できます。

pathType は v1 API で 必須フィールド です。省略すると Ingress の作成が拒否されます。

backend service と defaultBackend

backend はトラフィックの送信先を指定し、サービス名とポート(番号または名前)を明記します。defaultBackend はどのルールにもマッチしないリクエストを受けるフォールバックです。指定しないと、マッチしないリクエストはコントローラのデフォルト404ページへ落ちます。

resource backend

サービスの代わりにリソース(例: オブジェクトストレージを指すカスタムリソース)をバックエンドに指定することもできます。backend.servicebackend.resource は相互排他です。

IngressClass と複数コントローラ

1つのクラスタにコントローラを2つ以上インストールできます(例: 内部用と外部用)。このとき「この Ingress リソースを誰が処理するか」を決めるメカニズムが IngressClass です。

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

Ingress リソースでは spec.ingressClassName: nginx でどのクラスを使うか指定します。コントローラは自分が担当するクラスの Ingress だけを処理し、それ以外は無視します。is-default-class アノテーションが true の IngressClass があれば、ingressClassName を省略した Ingress はそのデフォルトクラスへ自動的に割り当てられます。

かつてはクラスを kubernetes.io/ingress.class アノテーションで指定していましたが、この方式は deprecated です。新規環境では spec.ingressClassName を使ってください。複数コントローラ運用の詳細は別記事で扱います。

TLS の設定

Ingress で HTTPS を終端するには、TLS Secret と spec.tls を使います。

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

secretName が指す Secret は kubernetes.io/tls タイプである必要があり、tls.crttls.key を含んでいなければなりません。Secret は Ingress と同じ名前空間に存在する必要があります。

apiVersion: v1
kind: Secret
metadata:
  name: shop-tls
  namespace: shop
type: kubernetes.io/tls
data:
  tls.crt: <base64-encoded-cert>
  tls.key: <base64-encoded-key>

実務では証明書を手動で管理せず、cert-manager を使って Let's Encrypt などから自動発行・更新します。cert-manager は ACME チャレンジを処理し、発行された証明書を上記の形の Secret として保存します。すると Ingress Controller がその Secret を watch して TLS 終端に使用します。

コントローラ種類の概要比較

代表的な Ingress Controller を比較すると次のとおりです。

コントローラデータプレーン設定更新方式特徴備考
ingress-nginxNGINXnginx.conf reload + Lua 動的エンドポイント最も広く使われる、アノテーション豊富メンテナンスモード寄り、セキュリティパッチ中心
NGINX Ingress (F5)NGINX / NGINX Plus設定 reload商用サポート(Plus)、別プロジェクトingress-nginx とは別プロジェクト
TraefikTraefik動的ホット reconfigureCRD/アノテーション、自動サービスディスカバリダッシュボード提供
HAProxy IngressHAProxyruntime API + reload高性能 L4/L7、精緻なヘルスチェック細かいチューニング可能
ContourEnvoyxDS 動的HTTPProxy CRD、委任モデルCNCF プロジェクト
KongKong/NGINXDB-less 動的API ゲートウェイ機能、プラグイン認証/レート制限が豊富
APISIXAPISIXetcd ベース動的プラグインエコシステム、高性能Apache プロジェクト
EmissaryEnvoy動的Mapping CRD ベース旧 Ambassador

選択基準は通常次のとおりです。単純なルーティングと幅広いリファレンスが必要なら ingress-nginx、動的サービスディスカバリと運用の容易さが重要なら Traefik、API ゲートウェイ級の認証/プラグインが必要なら Kong/APISIX、Envoy ベースの精緻なポリシーが必要なら Contour を検討します。

2026年のコンテキスト: Ingress API frozen と Gateway API への継承

ここで必ず押さえるべき2026年現在の流れがあります。Ingress API は frozen の状態 です。つまり networking.k8s.io/v1 の Ingress は安定して動き続けますが、新しい機能はもう追加されません。ヘッダベースのルーティング、トラフィック分割(重み付け)、明示的なメソッドマッチングといった高度な機能はすべてコントローラ別のアノテーションで回避するしかなく、この「アノテーション地獄」が Ingress API の根本的な限界として指摘されてきました。

その後継として登場したのが Gateway API です。Gateway API は GatewayClass、Gateway、HTTPRoute など役割が分離されたリソースモデルを提供し、標準仕様の中でヘッダマッチング、トラフィックの重み付け分割、クロス名前空間ルーティングなどをサポートします。インフラ運用者(Gateway 管理)とアプリケーション開発者(Route 管理)の責任を分離するよう設計されているのが大きな違いです。

また、これまで事実上の標準のように使われてきた ingress-nginx は、メンテナンスモードに近い流れで、新機能よりセキュリティパッチ中心に運用されています。したがって新規プロジェクトなら、最初から Gateway API とそれを実装するコントローラ(例: Envoy Gateway、Traefik、Contour の Gateway サポートなど)を検討するのが合理的です。

とはいえ、今すぐすべての Ingress を捨てろという意味ではありません。Ingress は依然として広く動作し、単純なルーティングには十分です。ただし新しく設計するなら Gateway API を第一候補に置き、既存資産は段階的に移行する戦略が推奨されます。

最初のデプロイ実習

では ingress-nginx で最初の Ingress をデプロイしてみましょう。まずコントローラをインストールします(Helm 使用)。

helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install ingress-nginx ingress-nginx/ingress-nginx \
  --namespace ingress-nginx --create-namespace

インストール後、コントローラが LoadBalancer タイプの Service で外部 IP を受け取ったか確認します。

kubectl get svc -n ingress-nginx
kubectl get pods -n ingress-nginx

次にテスト用のアプリケーションとサービスをデプロイします。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello
spec:
  replicas: 2
  selector:
    matchLabels:
      app: hello
  template:
    metadata:
      labels:
        app: hello
    spec:
      containers:
        - name: hello
          image: hashicorp/http-echo
          args:
            - "-text=hello from ingress"
          ports:
            - containerPort: 5678
---
apiVersion: v1
kind: Service
metadata:
  name: hello-service
spec:
  selector:
    app: hello
  ports:
    - port: 80
      targetPort: 5678

そして Ingress を作ります。

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

ローカルでテストするときは、hosts ファイルにコントローラの外部 IP を hello.example.com としてマッピングするか、curl の Host ヘッダで模倣します。

curl -H "Host: hello.example.com" http://<EXTERNAL-IP>/

hello from ingress が出れば成功です。

運用とチューニングのポイント

運用段階でよく触る設定をまとめます。ほとんどは ingress-nginx 基準のアノテーションまたは ConfigMap です。

metadata:
  annotations:
    nginx.ingress.kubernetes.io/proxy-body-size: "50m"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/rewrite-target: /$2
  • proxy-body-size: アップロードサイズ制限。ファイルアップロードサービスで 413 エラーが出たら最初に確認します。
  • proxy-read-timeout: バックエンド応答の待機時間。長時間処理が 504 で切れるなら増やします。
  • ssl-redirect: HTTP を HTTPS へ強制リダイレクト。
  • rewrite-target: パスの書き換え。正規表現のキャプチャグループと併用します。

ConfigMap ではグローバル設定(ワーカープロセス数、keepalive、gzip など)を調整します。コントローラ自体もリソースの requests/limits、HPA、PodDisruptionBudget を設定して安定運用すべきです。コントローラはすべてのトラフィックが通る単一地点なので、十分なレプリカ数とノード分散(topologySpreadConstraints)が重要です。

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

実務でよく遭遇する問題をまとめます。

1. Ingress を作ったのに ADDRESS が空。 コントローラがインストールされていないか、ingressClassName が誤っていてどのコントローラもその Ingress を掴んでいない場合です。kubectl get ingress で ADDRESS 列を確認し、kubectl describe ingress のイベントを見ます。

kubectl get ingress
kubectl describe ingress hello-ingress

2. 503 Service Temporarily Unavailable。 バックエンドサービスのセレクタが Pod と合っておらず Endpoints が空の可能性が高いです。次を確認します。

kubectl get endpoints hello-service

空ならサービスセレクタと Pod ラベル、そして Pod の readiness probe の状態を点検します。

3. コントローラから 404 Not Found が返る。 Host ヘッダがルールの host と異なるか、pathType/パスマッチが意図と異なるときです。PrefixExact の違い、ワイルドカードホストの単一ラベル制約を再確認します。

4. rewrite-target が意図どおり動かない。 キャプチャグループの正規表現とパス定義がずれている場合です。ingress-nginx で rewrite-target を使うときはパスを正規表現キャプチャの形で定義し、use-regex アノテーションと一緒に検証します。

5. TLS が適用されない。 Secret タイプが kubernetes.io/tls でない、Secret が Ingress と別の名前空間にある、spec.tls.hostsrules.host が不一致、のいずれかです。cert-manager を使うなら Certificate、CertificateRequest、Order リソースの状態を追跡します。

6. コントローラの reload が頻繁で遅延が生じる。 Ingress/Service/Endpoints の変更が頻繁だと設定 reload が頻発することがあります。ingress-nginx は動的エンドポイント更新でほとんどの reload を回避しますが、アノテーションやホストルールの変更は reload を引き起こします。変更をまとめてデプロイし、コントローラログで reload 頻度を監視します。

kubectl logs -n ingress-nginx deploy/ingress-nginx-controller | grep -i reload

おわりに

Ingress をきちんと扱うには、まず Ingress リソース(宣言)と Ingress Controller(実行)の区別 を体得する必要があります。その上で API watch → 設定生成 → reload に至る制御ループを理解すれば、ルーティングがなぜ動くのか、あるいはなぜ動かないのかを構造的に推論できます。

リソース仕様(rules/paths/pathType/backend/defaultBackend)、IngressClass を通じたコントローラ選択、TLS Secret 連携まで習得すれば、基本的な運用は十分こなせます。ただし2026年現在は Ingress API が frozen で、Gateway API が後継標準として定着していく移行期です。既存の Ingress 資産は安定して維持しつつ、新規設計では Gateway API を積極的に検討することをお勧めします。次のステップとしては、Gateway API への移行と複数コントローラ運用を見ていくとよいでしょう。

参考資料