Tech Learning Daily

2026-08-30 (Sun) — 第 45 号
AI が毎朝届ける、ソフトウェア技術の基礎解説
Container 🌿 基礎 ⏱ 約 8 分

Ingress を apply したのに、なぜ何も起きないのか?

ドキュメント通りに hostpath を書いた Ingress マニフェストを kubectl apply したのに、ブラウザからは何も変わらない。kubectl get ingress の ADDRESS 欄はいつまでも空のまま——そんな経験はないだろうか。Service を作ったときはすぐ疎通したのに、なぜ Ingress だけは素直に動いてくれないのか。

🎯 3 行まとめ
  • Ingress は「こう振り分けろ」というルールを宣言する API オブジェクトに過ぎず、それ自体はトラフィックを処理しない。ルールを読んで実際に動く Ingress Controller が別途稼働していない限り、Ingress を作っただけでは何も起きない
  • Service は L4(ポート単位)でしか区別できないため、複数ドメインをまとめて捌くには足りない。Ingress は L7 で Host ヘッダとパスを見て、1 つの IP から複数の Service へ振り分ける
  • Kubernetes 公式は Ingress API を凍結し、ヘッダーマッチや重み付けルーティングなど今後の拡張は後継の Gateway API 側に入ると案内している

Service だけでは複数ドメインを 1 つの IP で捌けない

Service(ClusterIPNodePort)は kube-proxy が iptables のルールで宛先 IP・ポート単位に転送するだけの L4 の仕組みで、HTTP の Host ヘッダやパスを見ることができない。そのため type: LoadBalancer の Service で foo.example.combar.example.com を別々のアプリに振り分けようとすると、Service ごとに別のクラウドロードバランサーと外部 IP を用意するしかなくなる。

foo.example.com ─▶ IP-A ─▶ Service A
bar.example.com ─▶ IP-B ─▶ Service B

ドメインの数だけ外部 IP とロードバランサーのコストが積み上がる。しかも Service 側はそもそも Host ヘッダの中身に興味が無いので、この構成は「たまたまドメインごとに IP を分けている」だけで、1 つのエンドポイントで複数ドメインをさばく仕組みにはなっていない。

Ingress は「ルールを書くだけ」——それ自体は動かない

ここで登場する kind: Ingress は、Service や Deployment と同じく Kubernetes API サーバーに保存される 1 つの API オブジェクトでしかない。だが Deployment を作れば kubelet が Pod を起動し、Service を作れば kube-proxy が iptables ルールを書き換えるのに対し、Ingress にはそれに相当する標準搭載のコンポーネントが存在しない。

Kubernetes 公式ドキュメントはこの点をはっきり書いている。You must have an Ingress controller to satisfy an Ingress. Only creating an Ingress resource has no effect.——Ingress リソースを作成しただけでは、エラーも出ないまま本当に何も起きない。これが冒頭の「ADDRESS 欄が空のまま」の正体で、マニフェストの書き方の問題ではなくクラスタに Ingress Controller が存在しないことが原因であるケースが多い。

🍱 たとえるなら

ビルの入口に「経理部は 3 階、営業部は 5 階」と書かれた案内板を掲げるようなものだ。案内板(Ingress)を壁に貼っただけでは誰も動かない。その案内板を実際に読んで、来訪者を該当の階へ案内する受付係(Ingress Controller)が常駐して初めて、1 つの入口から複数の部署へ人を振り分けられるようになる。

Ingress Controller が実際に振り分ける

Ingress Controller(nginx ingress、Envoy ベースの Contour、Traefik など複数の実装がある)は、Ingress オブジェクトの変更を API サーバーへ問い合わせ続け、そこに書かれたルールを自分自身が動かすリバースプロキシの設定に変換する Pod 群だ。Controller 自身は普通の Service(多くは type: LoadBalancer)としてクラスタの外に公開されており、kubectl get ingress の ADDRESS 欄に入る IP は、実は Ingress そのものではなく Controller 側の Service が持つ外部 IP になる。

Client (HTTPS)
     │
     ▼
Ingress Controller Pod
 (Service: LoadBalancer)
     │ ルールを見て転送(HTTP)
     ▼
  Service ──▶ Pod

1 つのクラスタに複数の Controller を共存させることもでき、どの Ingress をどの Controller に処理させるかは、Controller ごとに登録された IngressClass(Controller の識別子を持つ API オブジェクト)を spec.ingressClassName で指定して結びつける。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-ingress
spec:
  ingressClassName: nginx   # このIngressを担当するController
  rules:
  - host: foo.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web
            port:
              number: 80

ingressClassName を省略すると、ingressclass.kubernetes.io/is-default-class: "true" が付いた IngressClass を持つ Controller が既定で処理する。複数の Controller を入れているのに ingressClassName を書き忘れると、意図しない Controller がルールを拾ってしまうことがある。

host/path のマッチングと TLS 終端の具体挙動

pathType には Prefix(接頭辞一致。/testpath なら /testpath/one にもマッチする)と Exact(完全一致のみ)があり、どちらを選ぶかでルールが想定より広く/狭く効くかが決まる。spec.rules のどれにも一致しないリクエストは、spec.defaultBackend を指定していればそこへ送られ、指定していなければ Controller 自身が返す 404 になる。spec.rules を書かずに defaultBackend だけの Ingress を作ることもでき、その場合は必須項目になる。

spec:
  tls:
  - hosts: ["foo.example.com"]
    secretName: foo-tls   # 証明書とキーを持つ Secret
  rules:
  - host: foo.example.com
    http:
      paths: [...]

TLS を設定すると Client と Ingress の間は HTTPS で暗号化されるが、Ingress は受け取った時点でその暗号を解いてしまう。この「暗号化された通信を復号して平文に戻す地点」をTLS 終端(TLS termination)と呼び、Ingress から先の Service・Pod へは平文の HTTP で転送される。これは「TLS を設定したから通信は最後まで暗号化されている」という思い込みを裏切る点で、Pod まで暗号化を続けたいなら Ingress とは別にサービスメッシュや mTLS の仕組みを重ねる必要がある。

Ingress の次: Gateway API

Ingress の仕様はホスト名とパスによる単純な振り分けしか標準では定義しておらず、ヘッダーによる振り分けやトラフィックの重み付けのような細かい制御は、これまで Controller ごとに異なる独自アノテーション(nginx.ingress.kubernetes.io/* など)に頼るしかなかった。Controller を乗り換えるとアノテーションごと書き直しになるのはこのためだ。

Kubernetes 公式は現在 Ingress API を凍結しており、今後の変更や新機能は追加されないと明言した上で、後継として Gateway API への移行を推奨している。Gateway API はヘッダーベースのマッチングやトラフィックの重み付けを、ベンダー独自のアノテーションではなく標準の API フィールドとして扱える設計になっている。Ingress 自体が撤去される予定はないが、新しい振り分け要件が出てきたときに「Ingress のアノテーションで頑張る」以外の選択肢があることは覚えておいて損はない。

💼 実務でどう出会うか

EKS の AWS Load Balancer Controller、GKE の GKE Ingress、自前構築の nginx-ingress など、クラウドやディストリビューションによって同梱される Ingress Controller が異なり、対応するアノテーションもバラバラなので、他所で見つけた Ingress の YAML をそのまま持ち込んでも動かないことが珍しくない。TLS 証明書の secretName は cert-manager のようなツールと組み合わせて自動発行・自動更新される運用が一般的で、Secret が空だと Ingress 自体は作成できても TLS ハンドシェイクが失敗する。

⌨️ 手を動かす(5 分)

Ingress Controller を入れていないクラスタに Ingress だけを作ると、公式ドキュメントの言う「何も起きない」を手元で確認できる。

kind create cluster --name ing-demo 2>/dev/null || true
kubectl create deployment web --image=nginx:alpine
kubectl expose deployment web --port=80
cat <<'EOF' | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-ingress
spec:
  rules:
  - host: web.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web
            port:
              number: 80
EOF
kubectl get ingress web-ingress
kubectl describe ingress web-ingress

kubectl get ingress の ADDRESS 欄はいつまで待っても空のままのはずだ。Ingress Controller をインストールしていないのでルールを読んで実行する主体が存在せず、Ingress オブジェクトは API サーバーの中にただ保存されているだけの状態になる。

🙅 よくある誤解
  • Ingress を kubectl apply すれば自動でルーティングが有効になる — Ingress は自分では何も処理しない API オブジェクトで、ルールを読んで実行する Ingress Controller がクラスタに稼働していなければ何も起きない。
  • TLS を設定すれば通信は最後まで暗号化される — TLS が終端するのは Ingress までで、そこから先の Service・Pod への転送は既定では平文の HTTP になる。
  • Service を LoadBalancer にすれば複数ドメインも 1 つでさばける — Service は L4 でしか区別できず Host ヘッダを見られないため、ドメインごとに振り分けたいなら Service だけでは足りず L7 で動く Ingress が必要になる。
📖 用語ミニ辞典
Ingress
Host・パスに基づく HTTP/HTTPS ルーティングのルールを宣言する Kubernetes API オブジェクト。それ自体はトラフィックを処理しない。
Ingress Controller
Ingress オブジェクトを監視し、そのルールを実際のリバースプロキシ設定に変換して動かすコンポーネント。nginx-ingress など複数の実装がある。
IngressClass
どの Ingress Controller が Ingress を処理するかを指定する仕組み。ingressClassName で参照する。
pathType
Ingress のパスマッチ方式。接頭辞一致の Prefix と完全一致の Exact がある。
TLS 終端(TLS termination)
暗号化された HTTPS 通信を復号する地点。Ingress で終端した場合、そこから先の Service への転送は既定で平文になる。
Gateway API
Kubernetes が Ingress の後継として推奨する L7 ルーティング API。ヘッダーマッチや重み付けを標準フィールドとして扱える。
🔗 もっと深く