Ingress と Gateway API の違い

v1.37 時点(2026年10月26日 確認)

Kubernetes 基本仕様第24回 / 全45回 ・ 第4部 ネットワーク
目次

この記事ではクラスタの外から届く HTTP の通信を Service に振り分ける 2 つの API である Ingress と Gateway API の仕組みを、図で説明します。Ingress は 1 つのリソースに振り分けの規則を書き、Ingress コントローラーがそれを実現することがわかります。Gateway API は Kubernetes 本体とは別に CRD で足す API で、GatewayClass、Gateway、HTTPRoute の 3 つのリソースに役割を分けることもわかります。後半では kind のクラスタで、パスで 2 つの Service に振り分ける同じ規則を Ingress と HTTPRoute の両方で書き、マニフェストと動きの違いを比べる手順を示します。

Service をクラスタの外に公開する NodePort と LoadBalancer、kind のクラスタで LoadBalancer を動かす Cloud Provider KIND を扱った第22回の内容を前提にしています。まだの方は先にこちらをどうぞ。

基本仕様 ・ 第22回 NodePort と LoadBalancer の違いService をクラスタの外に公開する NodePort と LoadBalancer の違いを図で説明します。すべてのノードの同じポートで受ける NodePort と、その前に外部のロードバランサーを置く LoadBalancer の関係を押さえ、kind のクラスタで、ノードが Docker のコンテナであることによる制約と、手元のマシンから届ける方法を確かめる手順を紹介します。

Service そのものの仕組み(ラベルセレクタ、cluster IP、kube-proxy)は第21回で扱いました。

基本仕様 ・ 第21回 Service を動かして理解するPod の集まりに変わらない入り口を与える Service の仕組みを図で説明します。ラベルセレクタで Pod を選ぶこと、cluster IP、転送先の一覧を持つ EndpointSlice、ノードで通信を Pod へ届ける kube-proxy を押さえ、kind のクラスタで Pod を増減させたときに転送先が自動で変わる様子を確かめる手順を紹介します。

以下の図は、パスで 2 つの Service に振り分ける同じ規則を、Ingress で書く場合と Gateway API で書く場合に、使うリソースの違いを表しています。

パスで 2 つの Service に振り分ける同じ規則を、Ingress と Gateway API で書くときに使うリソース

Ingress では、振り分けの規則を Ingress という 1 つのリソースに書き、どのコントローラーに任せるかを IngressClass で選びます。Gateway API では、コントローラーの種類を GatewayClass、通信を受ける入り口を Gateway、振り分けの規則を HTTPRoute と、別々のリソースに書きます。どちらの場合も、リソースはオブジェクト(設定)で、通信を実際に受けて Service へ転送するのはコントローラーが用意するロードバランサーやプロキシ(通信を中継するプログラム)です。図ではコントローラーとプロキシを省いています。

参考:Ingress | Kubernetes、Gateway API | Kubernetes

Ingress と Gateway API とは

Ingress は クラスタの外からクラスタの中の Service への、HTTP と HTTPS の経路を公開する API です。公式ドキュメントによると、通信の振り分けは Ingress のリソースに書いた規則で決まります。Ingress を使うと、Service にクラスタの外から届く URL を与えたり、通信の負荷を分散したり、SSL / TLS(一般に、通信を暗号化する仕組み)の暗号を解いたり(終端したり)できます。名前に基づく仮想ホスト(1 つの IP アドレスで、複数のホスト名の通信を受け分けること)にも対応します。Ingress の API は v1.19 から安定版(GA)です。

参考:Ingress | Kubernetes

Gateway API は 役割に沿った(role-oriented)設定の仕組みで、ネットワークのサービスを公開する API の種類の集まり です。公式ドキュメントは Gateway API を、インフラの動的な用意と高度な通信の振り分けを提供する API の種類を含んだアドオン(add-on。あとから足す拡張)と説明しています。Gateway API のリソースは Kubernetes に組み込まれて実装されているのではなく、カスタムリソースとして定義されています。カスタムリソースは Kubernetes の API の拡張で、Kubernetes を入れただけの状態で使えるとは限りません。カスタムリソースを足す仕組みの 1 つが CRD(CustomResourceDefinition)で、第44回で扱います。

参考:Gateway API | Kubernetes、Gateway API | Kubernetes、Custom Resources | Kubernetes

Gateway API は Kubernetes 本体とは別の、gateway-api.sigs.k8s.io で開発されているプロジェクトです。Gateway API のプロジェクトは自らを、Kubernetes の公式のプロジェクトと説明しています。

参考:Gateway API

v1.37 時点の Ingress のページの冒頭には、次の注記があります(原文のまま引用します)。

The Kubernetes project recommends using Gateway instead of Ingress. The Ingress API has been frozen.

This means that:

  • The Ingress API is generally available, and is subject to the stability guarantees for generally available APIs. The Kubernetes project has no plans to remove Ingress from Kubernetes.
  • The Ingress API is no longer being developed, and will have no further changes or updates made to it.

参考:Ingress | Kubernetes

Kubernetes のプロジェクトは Ingress の代わりに Gateway を使うことを勧めています。Ingress の API は凍結されました(frozen)。GA の API のまま残り、取り除く計画もありませんが、もう開発されず、変更や更新は加えられません。同じ注記は Ingress コントローラーのページにもあります。

参考:Ingress | Kubernetes、Ingress Controllers | Kubernetes

この回には似た名前の言葉がいくつか出てきます。名前の対応を次の表にまとめます。

言葉指すもの
Ingress振り分けの規則を書く、Kubernetes 本体のリソース(networking.k8s.io/v1)
Ingress コントローラーIngress を実現するもの
IngressClassIngress を実現するコントローラーを選ぶリソース
GatewayClass、Gateway、HTTPRouteGateway API のリソース(gateway.networking.k8s.io/v1)。役目は「Gateway API:役割ごとにリソースを分ける」で説明する

参考:Ingress | Kubernetes、Ingress | Kubernetes、Gateway API | Kubernetes

なぜ Ingress と Gateway API が必要なのか

第22回で見た LoadBalancer の Service では、外部のロードバランサーが Service ごとに用意されました。たとえば /foo と /bar の要求を別々の Service に届けたいとき、LoadBalancer の Service を 2 つ作ると、ロードバランサーも入り口の IP アドレスも 2 つになると考えられます。Ingress のページの「Simple fanout」の例は、1 つの IP アドレスへの通信を、要求された HTTP の URI(URL のパスの部分など)に応じて、複数の Service へ振り分ける構成です。公式ドキュメントは、Ingress を使うとロードバランサーの数を最小限に抑えられる、と説明しています。

参考:Ingress | Kubernetes、Service | Kubernetes

一方で、Ingress が扱うのは HTTP と HTTPS だけです。公式ドキュメントによると、Ingress は任意のポートやプロトコルを公開しません。HTTP と HTTPS 以外のサービスをインターネットに公開するときは、ふつう第22回の NodePort や LoadBalancer の Service を使います。

参考:Ingress | Kubernetes

では、Ingress があるのに、なぜ Gateway API が作られたのでしょうか。Gateway API のプロジェクトの移行ガイドは、Ingress の API に備わるのは TLS の終端と、ホストのヘッダー(Host ヘッダー。要求に添えられる、接続先のホスト名)と URI による HTTP の振り分けといった基本の機能だけだと説明しています。それ以上の機能は、各 Ingress コントローラーが Ingress のアノテーション(オブジェクトに付ける、キーと値の文字列の情報。第42回)で、実装ごとの拡張として提供してきました。移行ガイドはこのやり方の問題として、アノテーションを別の実装のものに書き換える必要があるので移植しにくいこと、キーと値の文字列をリソースの先頭にまとめて書くので数が増えると使いにくいことの 2 つを挙げています。移行ガイドによると、Gateway API は Ingress のリソースの機能をすべて備え、アノテーションでしか使えなかった機能の多くにも対応しています。Kubernetes の公式ドキュメントも、ヘッダーによる振り分けや通信の重み付けのように、Ingress ではアノテーションでしかできなかった機能に Gateway API の種類が対応している、と説明しています。

参考:Migrating from Ingress | Gateway API、Gateway API | Kubernetes

もう 1 つの違いは、誰が何を書くかです。移行ガイドによると、Ingress の API にははじめ Ingress という 1 つの種類しかなく、その持ち主である利用者が、外のクライアントへの公開の仕方をまとめて決めていました。Gateway API では、クラスタの運用者などが外からの通信の入り口を決め、アプリケーションの開発者がその入り口に結び付ける振り分けの規則を決める、という分担ができます。この分担は「Gateway API:役割ごとにリソースを分ける」で説明します。

参考:Migrating from Ingress | Gateway API

Ingress と Gateway API の仕組み

Ingress:1 つのリソースに規則を書く

公式ドキュメントの前提(Prerequisites)の節は、Ingress を実現するには Ingress コントローラーが必要(must) で、Ingress のリソースを作るだけでは何の効果もない、と書いています。Ingress コントローラーのページも、少なくとも 1 つを選んでクラスタに設定する必要がある、としています。公式ドキュメントは、理想ではすべての Ingress コントローラーが仕様に合うべき(should)だが、実際には動きが少しずつ違う、としています。

参考:Ingress | Kubernetes、Ingress Controllers | Kubernetes

Ingress コントローラーは第6回で見たコントローラーの一種と考えられます。公式ドキュメントによると、Deployment のコントローラーのような組み込みのコントローラーは kube-controller-manager の中で動きます。一方で、Kubernetes を拡張するためにコントロールプレーンの外で動くコントローラーもあり、Pod として動かすことも、Kubernetes の外で動かすこともできます。Ingress コントローラーは選んで入れるものなので、kube-controller-manager には含まれないと読めます。この回の手順では、手元のマシンで動かす Cloud Provider KIND がこの役を担います。

参考:Controllers | Kubernetes、Ingress Controllers | Kubernetes

公式ドキュメントの「Simple fanout」の例は、foo.bar.com 宛ての通信のうち、パスが /foo で始まるものを service1 へ、/bar で始まるものを service2 へ振り分ける Ingress です。後半の手順で使う example-ingress.yaml も、host を書かない点などを除いて同じ形なので、マニフェストはそちらで見ます。

参考:Ingress | Kubernetes

Ingress のマニフェストで振り分けにかかわるフィールドを、v1.37 時点の公式ドキュメントと API リファレンスに沿って次の表にまとめます。

フィールド内容
spec.ingressClassName使う IngressClass の名前。Ingress コントローラーは、この名前から自分が受け持つ Ingress かどうかを判断する
spec.rules[].host規則を当てるホスト名。書かなければ、その規則はすべての HTTP の通信に当てはまる
spec.rules[].http.paths[].path要求のパスと照らし合わせる値。/ で始める
spec.rules[].http.paths[].pathTypeパスの照らし合わせ方。書かないと検証で失敗し、kubectl apply などがエラーになると考えられる
spec.rules[].http.paths[].backend.service転送先の Service の名前(name)とポート(port.number か port.name)。Service は Ingress と同じ Namespace にある必要がある
spec.defaultBackendどの規則にも当てはまらない要求を受け持つ転送先。rules を書かないときは必須

参考:Ingress | Kubernetes、Ingress | Kubernetes、Ingress | Kubernetes、Ingress | Kubernetes API Reference、Ingress | Kubernetes API Reference

公式ドキュメントによると、ホストとパスの両方が要求の内容に合ったときに、ロードバランサーは参照先の Service へ通信を向けます。defaultBackend を書かなかった場合、どの規則にも合わない要求をどう扱うかは Ingress コントローラー次第です。API リファレンスによると、Ingress のポートは今のところ、HTTP では暗黙に 80、HTTPS では 443 です。

参考:Ingress | Kubernetes、Ingress | Kubernetes、Ingress | Kubernetes API Reference

パスの照らし合わせ方

pathType には 3 つの値があります。公式ドキュメントの説明を次の表にまとめます。

pathType照らし合わせ方
ExactURL のパスと完全に一致するものだけ。大文字と小文字を区別する
PrefixURL のパスを / で区切った要素ごとに、先頭から一致するか見る。大文字と小文字を区別する
ImplementationSpecific照らし合わせ方は IngressClass 次第。実装は、別の種類として扱うことも、Prefix や Exact と同じに扱うこともできる

参考:Ingress | Kubernetes

Prefix は文字列の先頭一致ではない点に注意が要ります。公式ドキュメントの例では、/foo/bar は /foo/bar/baz に一致しますが、/foo/barbaz には一致しません。/aaa/bbb は /aaa/bbb/ と /aaa/bbb/ccc に一致し、/aaa/bbbxyz には一致しません。

参考:Ingress | Kubernetes

IngressClass でコントローラーを選ぶ

1 つのクラスタで複数の Ingress コントローラーを使うこともあります。公式ドキュメントによると、Ingress はそれぞれ別の設定を持つ異なるコントローラーで実現されることがあるので、各 Ingress は IngressClass を指すべき(should)です。IngressClass はそのクラスを実現するべきコントローラーの名前(spec.controller)などの設定を持つリソースです。IngressClass はいつもクラスタ全体のリソースで、Namespace には属しません。

参考:Ingress | Kubernetes、Ingress | Kubernetes

アノテーション ingressclass.kubernetes.io/is-default-class: "true" を付けた IngressClass はクラスタの既定になり、ingressClassName を書かずに新しく作った Ingress に割り当てられます。割り当てを行うのは、v1.37 で既定で有効なアドミッションコントローラー(kube-apiserver がリクエストを保存する前に、内容を確かめたり書き換えたりする仕組み。第40回)の 1 つ、DefaultIngressClass です。既定の IngressClass についての注意は「注意点」の表にまとめます。

参考:Ingress | Kubernetes、Admission Control in Kubernetes | Kubernetes

以下の図は、Ingress、IngressClass、Ingress コントローラー、転送先の Service の関係を表しています。

Ingress を読み取ってロードバランサーなどを設定する Ingress コントローラーと、コントローラーを選ぶ IngressClass

Ingress コントローラーは ingressClassName から自分が受け持つ Ingress を判断し、その規則に沿って振り分けるように、ロードバランサーなどを設定します。公式ドキュメントによると、Ingress コントローラーはふつうロードバランサーで Ingress を実現し、エッジルーター(クラスタの入り口のルーター)やほかのフロントエンドを設定することもあります。Ingress を変更すると、kubectl が kube-apiserver の Ingress を更新し、それを受けて Ingress コントローラーがロードバランサーを設定し直します。図の実線は通信と選択(Ingress から IngressClass、IngressClass からコントローラーを指す関係)、破線は設定する関係、点線は監視を表します。Ingress コントローラーが Ingress をどう読み取るかは公式ドキュメントに書かれていないので、点線の「監視」は、変更に合わせて設定し直すには Ingress を読み取る必要がある、という意味で描いたものです。

参考:Ingress | Kubernetes、Ingress | Kubernetes、Ingress | Kubernetes API Reference

用意されたロードバランサーのアドレスは、kubectl get ingress の ADDRESS 列に出ます。公式ドキュメントは、Ingress コントローラーやロードバランサーが IP アドレスを割り当てるまで 1〜2 分かかることがあり、それまでは <pending> と表示されることが多い、と注意しています。

参考:Ingress | Kubernetes

Ingress コントローラーの選び方と ingress-nginx の引退

v1.37 時点の Ingress コントローラーのページは、Kubernetes のプロジェクトがサポートし保守している Ingress コントローラーとして AWS と GCE のものを挙げ、そのほかにプロジェクトの外の(third party)ものを 30 ほど並べています。広く使われてきた Kubernetes のプロジェクトの Ingress NGINX(ingress-nginx)は、2025年11月11日の Kubernetes の公式ブログで引退(retirement)が告知されました。できる範囲での(best-effort)保守は 2026年3月までです。2026年10月26日に確かめた時点で、GitHub のリポジトリは 2026年3月24日にアーカイブされた(読み取り専用になった)と表示されています。引退した後の扱いと勧められている移行先は「注意点」の表にまとめます。後半の手順では Ingress NGINX は使わず、kind の公式ドキュメントが案内している Cloud Provider KIND を使います。

参考:Ingress Controllers | Kubernetes、Ingress NGINX Retirement: What You Need to Know | Kubernetes、kubernetes/ingress-nginx、Ingress | kind

Gateway API:役割ごとにリソースを分ける

Kubernetes の公式ドキュメントは Gateway API の安定版の種類として GatewayClass、Gateway、HTTPRoute、GRPCRoute の 4 つを挙げています。この回では GRPCRoute を除く 3 つを扱います。Gateway API のドキュメントは、HTTPRoute や GRPCRoute のように、Gateway から Service への振り分けの規則をプロトコルごとに定めるリソースを、まとめて Route と呼んでいます。3 つの役目を次の表にまとめます。

リソース公式ドキュメントの説明
GatewayClass共通の設定を持ち、そのクラスを実装するコントローラーが管理する Gateway の集まりを定める。コントローラーの名前を spec.controllerName に持つ
Gateway通信を扱うインフラの実体(クラウドのロードバランサーなど)を表す。受け口(listener)ごとにポートやプロトコルを決める
HTTPRouteGateway の受け口から Service などへ、HTTP の要求を振り分ける規則を書く

Gateway API のドキュメントによると、GatewayClass はクラスタ全体のリソースで、Namespace に属しません。Gateway と HTTPRoute は、公式ドキュメントの例で Namespace を指定して作られているので、Namespace に属するリソースと読めます。

参考:Gateway API | Kubernetes、Gateway API | Kubernetes、API Overview | Gateway API、API Overview | Gateway API、API specification (v1.6) | Gateway API、API specification (v1.6) | Gateway API

3 つのリソースは順に指していきます(HTTPRoute → Gateway → GatewayClass)。公式ドキュメントによると、Gateway はちょうど 1 つの GatewayClass に結び付き、GatewayClass はそのクラスの Gateway を管理するコントローラーを表します。HTTPRoute などの Route は、Gateway に結び付けます。Gateway API のドキュメントによると、Gateway が働くには、GatewayClass が少なくとも 1 つ定められている必要があります(must)。Gateway API を実装するコントローラーは、利用者が Gateway から指せる GatewayClass を用意することで実装を提供します。

参考:Gateway API | Kubernetes、API Overview | Gateway API

公式ドキュメントの Gateway の例は、受け口(listeners)に name: http、protocol: HTTP、port: 80 を書き、80 番のポートで HTTP の通信を待ち受けるものです(後半の手順の example-gateway.yaml は、この例をもとにしています)。addresses(Gateway のアドレスを自分で指定するフィールド)を書かなければ、アドレスかホスト名は、実装のコントローラーが Gateway に割り当てます。Ingress では HTTP のポートが暗黙に 80 だったのに対し、Gateway では受け口ごとにポートとプロトコルを書きます。

参考:Gateway API | Kubernetes

HTTPRoute を Gateway に結び付ける

HTTPRoute は spec.parentRefs で、結び付く Gateway を指します。Gateway API のドキュメントによると、Route が Gateway に結び付くには、Route の parentRefs がその Gateway を指していることと、Gateway の受け口の少なくとも 1 つがその結び付きを許していることの、2 つがそろう必要があります。受け口の allowedRoutes.namespaces.from の既定値は Same で、Gateway と同じ Namespace の Route だけが結び付けます。All にすると、すべての Namespace の Route を許します。

参考:API Overview | Gateway API、API Overview | Gateway API、API specification (v1.6) | Gateway API

HTTPRoute の規則(spec.rules[])は要求の条件(matches)と転送先(backendRefs)の組です。Gateway API v1.6 の API の仕様に沿って、主なフィールドを次の表にまとめます。

フィールド内容既定値
spec.parentRefs[].name結び付く相手(ふつうは Gateway)の名前。必須なし
spec.parentRefs[].namespace相手の NamespaceRoute と同じ Namespace
spec.hostnamesHTTP の Host ヘッダーと照らし合わせるホスト名なし
spec.rules[].matches[].path.typeExact、PathPrefix、RegularExpression のどれかPathPrefix
spec.rules[].matches[].path.value照らし合わせるパス/
spec.rules[].backendRefs[]転送先の名前(name)とポート(port)。転送先が Service のとき、port は必須なし

参考:API specification (v1.6) | Gateway API、API specification (v1.6) | Gateway API、API specification (v1.6) | Gateway API、API specification (v1.6) | Gateway API

matches を書かない規則は、パス / の先頭一致(PathPrefix)とみなされます。PathPrefix は Ingress の Prefix と同じく、URL のパスを / で区切った要素ごとに照らし合わせ、大文字と小文字を区別します。

参考:API specification (v1.6) | Gateway API、API specification (v1.6) | Gateway API

役割と、書く権限の分け方

Gateway API のリソースは Kubernetes のサービスのネットワークを管理する組織上の役割をもとに作られています。Kubernetes の公式ドキュメントは、インフラの提供者(Infrastructure Provider。たとえばクラウドプロバイダーのように、複数の利用者が使う複数のクラスタのためのインフラを管理する)、クラスタの運用者(Cluster Operator。クラスタを管理する)、アプリケーションの開発者(Application Developer。クラスタで動くアプリケーションを管理する)の 3 つの役割を挙げています。

参考:Gateway API | Kubernetes

Gateway API のセキュリティモデルのページは、この 3 つの役割について、各リソースを書く(write)権限の例を表にしています。ページによると、ほとんどの場合はすべてのリソースをほとんどの役割が読めるようにしておくのが望ましく、表は書く権限に絞ったものです。「シンプルな 3 層のモデル」の表は次のとおりです。

役割GatewayClassGatewayRoute
インフラの提供者書ける書ける書ける
クラスタの運用者書けない書ける書ける
アプリケーションの開発者書けない書けない書ける

参考:Security Model | Gateway API

表によると、GatewayClass を書けるのはインフラの提供者だけ、Gateway を書けるのはクラスタの運用者まで、Route はアプリケーションの開発者も書けます。後半の手順のように手元の kind のクラスタで 1 人で試すときは、3 つのリソースをすべて自分で書くことになります。役割の分担が意味を持つのは、複数の人やチームが 1 つのクラスタを使う場合だと考えられます。

参考:Security Model | Gateway API

以下の図は、GatewayClass、Gateway、HTTPRoute、転送先の Service の関係と、それぞれを書く役割を表しています。

HTTPRoute から Gateway、Gateway から GatewayClass へと順に指す関係と、それぞれを書く役割

Gateway は gatewayClassName で GatewayClass を、HTTPRoute は parentRefs で Gateway を、backendRefs で Service を指します。枠の見出しは、上の権限の表で、そのリソースを書ける役割のうち、最も範囲の狭いものです。表のとおり、上の段の役割の人は、下の段のリソースも書けます。図の実線は、指す(選ぶ)関係を表します。

参考:Gateway API | Kubernetes、Security Model | Gateway API

Gateway API のバージョンと CRD

Gateway API の CRD を用意するには、多くの場合 CRD も入れてくれる Gateway のコントローラーを入れるか、CRD を自分で入れるかのどちらかです。Gateway API は Kubernetes とは別にバージョンを重ね、少なくとも最新の 5 つの Kubernetes のマイナーバージョンをサポートすると約束しています(原文は “we commit to”)。

参考:Gateway API | Kubernetes、Getting started | Gateway API、Getting started | Gateway API、Versioning | Gateway API

機能の安定度は Standard と Experimental の 2 つのリリースチャンネルで表し、新しい機能やリソースはすべて Experimental チャンネルから始まります(注意点の表も参照)。GatewayClass、Gateway、HTTPRoute は v0.5.0 から Standard チャンネルにあり、v1.0 で GA の API バージョン(v1)を含むようになりました。2026年10月26日に確かめた時点で、GitHub の配布ページで最新(Latest)とされている Gateway API のバージョンは v1.6.3 です。

参考:Versioning | Gateway API、Versioning | Gateway API、Gateway API、Releases · kubernetes-sigs/gateway-api

Kubernetes の公式ドキュメントによると、Gateway API は Ingress の API の後継(successor)ですが Ingress の種類は含まないので、今ある Ingress のリソースを Gateway API のリソースに一度変換する必要があります。Gateway API の移行ガイドには、手で変換する例と、Ingress to Gateway(ingress2gateway)というプロジェクトで自動で変換する方法の両方が載っています。移行ガイドは、変換の結果はいつも試して確かめるべき(should)だとしています。

参考:Gateway API | Kubernetes、Migrating from Ingress | Gateway API、Migrating from Ingress | Gateway API

kind のクラスタでの Ingress と Gateway API

第22回で見たとおり、kind のクラスタには cloud-controller-manager がありません。kind の公式ドキュメントは LoadBalancer の Service に Cloud Provider KIND を案内していました。kind v0.33.0 の時点の Ingress のページによると、Cloud Provider KIND は v0.9.0 から Ingress にそのまま(natively)対応しており、既定ではプロジェクトの外の Ingress コントローラーは要りません。同じページには、Gateway API にも Ingress と並んでそのまま対応している、という注記もあります。

参考:Ingress | kind、Ingress | kind、kind のソースコード(Ingress のページ)

Kubernetes の公式ブログ、Cloud Provider KIND の README とリリースノートによると、Cloud Provider KIND は Gateway API の CRD をクラスタに自動で入れ、cloud-provider-kind という名前の GatewayClass を用意します。Gateway API のコントローラーとして Gateway と HTTPRoute を実装し(既定で Standard チャンネル)、Ingress は Gateway と HTTPRoute に映して(mirror)扱います。Cloud Provider KIND の IngressClass を既定にする設定は、既定で有効です。2026年10月26日に確かめた時点で最新の v0.12.0 のリリースノートには、Gateway API v1.6.1 に適合させたとあります。

参考:Experimenting with Gateway API using kind | Kubernetes、Experimenting with Gateway API using kind | Kubernetes、kubernetes-sigs/cloud-provider-kind、Release v0.9.0 · kubernetes-sigs/cloud-provider-kind、Release v0.12.0 · kubernetes-sigs/cloud-provider-kind

さらにソースコードからは、Ingress のある Namespace ごとに kind-ingress-gateway という名前の Gateway を作り、Gateway ごとに kindccm-gw- で始まる名前のプロキシのコンテナを動かすように読めます。このプロキシのコンテナは、クラスタのノードでも Pod でもなく、ノードのコンテナと並んで手元の Docker で動くものと考えられます。後半の手順では Ingress と HTTPRoute のどちらで書いても、最終的に同じ Gateway API の実装が通信を振り分けることになると考えられます。

参考:cloud-provider-kind のソースコード(Ingress のコントローラーの説明)、cloud-provider-kind のソースコード(Ingress のコントローラー)、cloud-provider-kind のソースコード(Gateway のコンテナ)

以下の図は、後半の手順のクラスタで、Cloud Provider KIND、プロキシのコンテナ、kind のクラスタがどう関係するかを表しています。

kind のクラスタを外から見張り、Gateway ごとにプロキシのコンテナを用意する Cloud Provider KIND

README によると、Cloud Provider KIND は手元のマシンで動き、すべての kind のクラスタを見張ります。前の段落で見たとおり、Gateway ごとにプロキシのコンテナが用意され、クライアントの要求はそのコンテナから Pod へ転送されると考えられます。図の実線は通信、点線は監視、破線は作る(設定する)関係を表します。Gateway API の CRD をクラスタに入れるのも Cloud Provider KIND ですが(上で見た公式ブログの説明)、図では線を省いています。左下の枠のコンテナの名前と kind-ingress-gateway は、ソースコードからの予想です(「手元で試す」の冒頭にまとめています)。

参考:Experimenting with Gateway API using kind | Kubernetes、kubernetes-sigs/cloud-provider-kind、cloud-provider-kind のソースコード(Ingress のコントローラーの説明)

手元で試す

ここまでの内容を手元の kind のクラスタで試す手順を示します。パスが /foo の要求を foo-service へ、/bar の要求を bar-service へ振り分ける同じ規則を、Ingress と HTTPRoute の両方で書きます。まず、コントローラーが無い状態では Ingress は作れても働かず、Gateway は作ることもできないことを確かめます。続けて Cloud Provider KIND を動かし、どちらの書き方でも同じように振り分けられることと、状態の表し方の違いを確かめます。

この節で引用する出力の例は、公式ドキュメントにある、ほかのクラスタのものです。表示の形を見るためのもので、名前や IP アドレスなどの値は手元とは違います。各段の最後には成功かどうかを見分ける点を短く挙げます。どれも公式ドキュメントから予想される結果で、手元で実行して確かめたものではありません。

手順の中には、公式ドキュメントではなく Cloud Provider KIND のソースコードから予想している点が 4 つあります。各段では「ソースコードからの予想」とだけ書くので、ここでまとめて挙げます。

  • IngressClass の名前が cloud-provider-kind であること(v0.9.0 のリリースノートの「the cloud-provider-kind IngressClass」という書き方とも合う)
  • Ingress を作ると、kind-ingress-gateway という Gateway と、example-ingress で始まる名前の HTTPRoute ができること
  • プロキシのコンテナの名前が kindccm- で始まること
  • 片付けで Ingress を消しても、kind-ingress-gateway が残ることがあること(Ingress を持ち主とする印、第11回で見た ownerReferences が付かないように読めるため)

参考:Release v0.9.0 · kubernetes-sigs/cloud-provider-kind、cloud-provider-kind のソースコード(Ingress のコントローラー)、cloud-provider-kind のソースコード(Gateway のコンテナ)

コマンドは macOS や Linux のシェル(bash や zsh)で実行する前提で書いています。Windows では WSL(Windows の中で Linux を動かす仕組み)などを使ってください。問い合わせ方は第22回と同じく Docker の動かし方で分けて示します。試す環境は次のとおりです。

項目内容
kindv0.33.0
Kubernetesv1.37.0(kind v0.33.0 の既定のノードイメージ)
ノード構成マルチノード(クラスタ名 multi-node):コントロールプレーン 1、ワーカー 2
Cloud Provider KINDv0.12.0(2026年10月26日時点の最新。Ingress には v0.9.0 以降が要る)
必要なものDocker、kubectl(v1.36〜v1.38)、curl、テキストエディタ、端末(ターミナル)3 つ

参考:Release v0.33.0 · kubernetes-sigs/kind、Version Skew Policy | Kubernetes、Ingress | kind、Release v0.12.0 · kubernetes-sigs/cloud-provider-kind

kind でクラスタを作る手順は、以下の記事で解説しています。この回は第20回・第21回と同じマルチノードのクラスタを使います。第22回のようなポートの対応付けは使いません。

基本仕様 ・ 第2回 kind で検証用のクラスタを作るDocker のコンテナをノードにして、手元のマシンに Kubernetes のクラスタを作るツール kind の仕組みを図で説明します。kind create cluster で何が起きるかを押さえ、シングルノードとマルチノードのクラスタを作って、ノードの正体が Docker のコンテナであることを確かめる手順を紹介します。

マルチノードのクラスタを用意する

第2回と同じ次の設定ファイルを使います。公式ドキュメントの例をそのまま引用しています。kind-multi-node.yaml という名前で、コマンドを実行するディレクトリに保存します。前の回までに保存したファイルが残っていれば、それを使えます。

yaml
# three node (two workers) cluster config
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker

参考:Quick Start | kind

この回はコントローラーが無い状態から始めるので、新しいクラスタを使います。まず、前の回のクラスタが残っていないかを確かめます。

console
$ kind get clusters

第22回の Cloud Provider KIND がまだ動いていたら、Ctrl+C で止めておきます。README によるとすべての kind のクラスタを見張るので、動いたままだと最初の段で CRD がすぐに入ってしまうと考えられます。第22回の片付けでクラスタを削除していれば、multi-node は表示されないはずです。表示されたら kind delete cluster --name multi-node で消してから、クラスタを作り、もう一度一覧します。

console
$ kind create cluster --name multi-node --config kind-multi-node.yaml
$ kind get clusters

kind get clusters に multi-node が表示されれば、クラスタができています。第3回で決めたとおり、ここからのコマンドには --context kind-multi-node を付けて、操作するクラスタをはっきりさせます。続けて、ノードを一覧します。

console
$ kubectl get nodes --context kind-multi-node

ノードが 3 つ表示され、STATUS 列が Ready になっていれば準備ができています。Ready になっていなければ、少し待ってからもう一度実行します。

参考:Quick Start | kind、kubectl get | Kubernetes、Node Status | Kubernetes、kubernetes-sigs/cloud-provider-kind

マニフェストを用意する

使うマニフェストは 5 つです。1 つ目は転送先の Pod と Service です。kind の公式ドキュメントの Ingress のページで使う例(usage.yaml)から、Ingress 以外の部分を取り出したものです(そのまま引用しています)。ingress-backends.yaml という名前で、コマンドを実行するディレクトリに保存します。

yaml
kind: Pod
apiVersion: v1
metadata:
  name: foo-app
  labels:
    app: foo
spec:
  containers:
  - command:
    - /agnhost
    - serve-hostname
    - --http=true
    - --port=8080
    image: registry.k8s.io/e2e-test-images/agnhost:2.39
    name: foo-app
---
kind: Service
apiVersion: v1
metadata:
  name: foo-service
spec:
  selector:
    app: foo
  ports:
  # Default port used by the image
  - port: 8080
---
kind: Pod
apiVersion: v1
metadata:
  name: bar-app
  labels:
    app: bar
spec:
  containers:
  - command:
    - /agnhost
    - serve-hostname
    - --http=true
    - --port=8080
    image: registry.k8s.io/e2e-test-images/agnhost:2.39
    name: bar-app
---
kind: Service
apiVersion: v1
metadata:
  name: bar-service
spec:
  selector:
    app: bar
  ports:
  # Default port used by the image
  - port: 8080

参考:Ingress | kind、usage.yaml | kind

ラベル app: foo の Pod foo-app と、それを選ぶ Service foo-service の組、ラベル app: bar の Pod bar-app と Service bar-service の組を作ります。Pod は第22回と同じ agnhost のイメージで、8080 番で HTTP の要求を受け、Pod のホスト名(Pod の名前)を返すと考えられます。Service は type を書いていないので ClusterIP の Service で(第21回)、targetPort を書いていないので、Pod の 8080 番へ転送します。第22回では foo-service が LoadBalancer の Service でしたが、この回の foo-service は別のものです。

参考:Service | Kubernetes、DNS for Services and Pods | Kubernetes

2 つ目は Ingress です。同じ usage.yaml の Ingress に、spec.ingressClassName: cloud-provider-kind の 1 行を足したものです。example-ingress.yaml という名前で保存します。

yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example-ingress
spec:
  ingressClassName: cloud-provider-kind
  rules:
  - http:
      paths:
      - pathType: Prefix
        path: /foo
        backend:
          service:
            name: foo-service
            port:
              number: 8080
      - pathType: Prefix
        path: /bar
        backend:
          service:
            name: bar-service
            port:
              number: 8080

参考:usage.yaml | kind、Ingress | Kubernetes API Reference

kind の例の Ingress は ingressClassName を書いていません。v0.9.0 のリリースノートに、Cloud Provider KIND の IngressClass を既定にできる(既定で有効)とあるので、既定の IngressClass に任せていると読めます。この回では Gateway の gatewayClassName と見比べられるように、ingressClassName を明示しました(名前はソースコードからの予想で、手順の途中で確かめます)。host を書いていないので、この規則は IP アドレス宛ての HTTP の通信すべてに当てはまります。

参考:Release v0.9.0 · kubernetes-sigs/cloud-provider-kind、Ingress | Kubernetes

3 つ目は Gateway です。公式ドキュメントの Gateway の例をもとに、gatewayClassName を cloud-provider-kind にし、metadata.namespace、hostname、allowedRoutes を省いたものです。example-gateway.yaml という名前で保存します。

yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: example-gateway
spec:
  gatewayClassName: cloud-provider-kind
  listeners:
  - name: http
    protocol: HTTP
    port: 80

参考:Gateway API | Kubernetes、API specification (v1.6) | Gateway API、API specification (v1.6) | Gateway API

80 番で HTTP を待ち受ける http という受け口を 1 つ持つ Gateway です。name、port、protocol は受け口の必須のフィールドです。hostname を書いていないので、すべてのホスト名に当てはまります。allowedRoutes を書いていないので、既定の from: Same になります(「HTTPRoute を Gateway に結び付ける」)。metadata.namespace を書いていないので、Gateway は kubectl のコンテキストの Namespace(このシリーズの kind のクラスタでは default)に作られると考えられます。

参考:API specification (v1.6) | Gateway API、API specification (v1.6) | Gateway API、Namespaces | Kubernetes

4 つ目は HTTPRoute です。2 つ目の Ingress と同じ振り分けを書いています。example-httproute.yaml という名前で保存します。

yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: example-httproute
spec:
  parentRefs:
  - name: example-gateway
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /foo
    backendRefs:
    - name: foo-service
      port: 8080
  - matches:
    - path:
        type: PathPrefix
        value: /bar
    backendRefs:
    - name: bar-service
      port: 8080

参考:Gateway API | Kubernetes、API specification (v1.6) | Gateway API、API specification (v1.6) | Gateway API

parentRefs で同じ Namespace の example-gateway に結び付き、パスが /foo で始まる要求を foo-service の 8080 番へ、/bar で始まる要求を bar-service の 8080 番へ送ります。hostnames を書いていないので、ホスト名では絞りません。

5 つ目は存在しない Service を転送先にした HTTPRoute です。状態の表し方を比べる段で使います。broken-httproute.yaml という名前で保存します。

yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: broken-httproute
spec:
  parentRefs:
  - name: example-gateway
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /baz
    backendRefs:
    - name: baz-service
      port: 8080

baz-service という Service はこの手順では作りません。パスを /baz にしたのは、example-httproute の /foo と /bar の振り分けと重ならないようにするためです。

2 つ目の Ingress と、3 つ目・4 つ目の Gateway と HTTPRoute の違いを、次の表にまとめます。

項目Ingress(example-ingress.yaml)Gateway API(example-gateway.yaml と example-httproute.yaml)
apiVersionnetworking.k8s.io/v1gateway.networking.k8s.io/v1
実装の指定spec.ingressClassName(IngressClass の名前)Gateway の spec.gatewayClassName(GatewayClass の名前)
入り口書かない。Ingress コントローラーが HTTP(暗黙に 80)と HTTPS の入り口を用意するGateway の listeners に port: 80 と protocol: HTTP を書く
入り口と規則の結び付き同じ Ingress の中に書くHTTPRoute の parentRefs で Gateway を指す
パスの一致pathType: Prefix と pathpath.type: PathPrefix と path.value
転送先backend.service.name と port.numberbackendRefs[].name と port

参考:Ingress | Kubernetes API Reference、API specification (v1.6) | Gateway API、Migrating from Ingress | Gateway API、Migrating from Ingress | Gateway API

コントローラーが無い状態で Ingress と Gateway を作る

kind の Ingress のページでは、Cloud Provider KIND を動かしてから Ingress を作ります。この回はコントローラーが無い状態を見るために順序を入れ替えています。

参考:Ingress | kind

ここからは端末を 3 つ使います。第22回と同じく、1 つ目の端末で変化を追い続け、2 つ目の端末でマニフェストを渡したり、curl で問い合わせたりします。3 つ目の端末は Cloud Provider KIND を動かし続けるのに使います。2 つ目の端末を開いたら、cd でマニフェストを保存したディレクトリに移動しておきます。

1 つ目の端末で、Ingress の一覧を監視します。第3回で見たとおり、--watch を付けると、一覧を表示したあとも変化を追い続けます。この監視は Gateway を作る段の前まで続けます。

console
$ kubectl get ingresses --watch --context kind-multi-node

参考:kubectl get | Kubernetes

まだ Ingress が無いので、何も表示されないか、No resources found のような、見つからないことを知らせる 1 行が出て、止まったように見えると考えられます。

2 つ目の端末で、Gateway API の API グループ(gateway.networking.k8s.io)にどんなリソースがあるかを確かめます。kubectl api-resources はサーバーが対応している API のリソースを一覧するコマンドで、--api-group で API グループを絞れます。

console
$ kubectl api-resources --api-group=gateway.networking.k8s.io --context kind-multi-node

参考:kubectl api-resources | Kubernetes

リソースの行は 1 つも出ないはずです。Gateway API の CRD をまだ入れていないので、gateways や httproutes はこのクラスタの API にありません。IngressClass も確かめます。

console
$ kubectl get ingressclasses --context kind-multi-node

No resources found のような、見つからないことを知らせる 1 行が出ると考えられます。まだ Cloud Provider KIND を動かしていないこの時点では、IngressClass を用意するものがいないと考えられるからです。

参考:Gateway API | Kubernetes、Ingress | kind

この状態で、転送先の Pod と Service、Ingress のマニフェストを順に渡します。

console
$ kubectl apply -f ingress-backends.yaml --context kind-multi-node
$ kubectl apply -f example-ingress.yaml --context kind-multi-node

pod/foo-app、service/foo-service、pod/bar-app、service/bar-service、ingress.networking.k8s.io/example-ingress が作られたことを知らせる行が出るはずです。Ingress の API は Kubernetes 本体にあるので、IngressClass がまだ無くても Ingress のオブジェクトは作れると考えられます。

1 つ目の端末には example-ingress の行が現れるはずです。公式ドキュメントの例では、kubectl get ingress の出力は次のとおりです。

NAME           CLASS         HOSTS   ADDRESS         PORTS   AGE
test-ingress   external-lb   *       203.0.113.123   80      59s

参考:Ingress | Kubernetes

例は test-ingress という別の Ingress のもので、Ingress コントローラーが IP アドレスを割り当てたあとの表示です。手元では NAME 列が example-ingress、CLASS 列が cloud-provider-kind になるはずです。host を書いていないので、HOSTS 列は例と同じく * になると考えられます。ADDRESS 列には IP アドレスが出ず、空欄のまま(上で見た <pending> のこともある)になると考えられます。上で見たとおり、Ingress のリソースを作るだけでは効果が無く、ここにはまだ Ingress コントローラーがいないからです。

参考:Ingress | Kubernetes、Ingress | Kubernetes

次に、Gateway のマニフェストを渡してみます。

console
$ kubectl apply -f example-gateway.yaml --context kind-multi-node

今度は Gateway は作られず、no matches for kind "Gateway" のような、Gateway という種類が見つからないことを知らせるエラーが出ると考えられます。Gateway API の CRD をまだ入れていないので、kube-apiserver は gateway.networking.k8s.io/v1 の Gateway を知らないはずだからです。Ingress は作れても働かず、Gateway はそもそも作れない、というのがこの段で見たい違いです。

参考:Gateway API | Kubernetes、Custom Resources | Kubernetes

この段では、次の 3 点が合っていれば成功です。

  • gateway.networking.k8s.io の API グループに、リソースの行が 1 つも無い
  • 1 つ目の端末に example-ingress の行が現れ、ADDRESS 列に IP アドレスが出ない
  • example-gateway.yaml を渡すとエラーになる

Cloud Provider KIND を動かす

Cloud Provider KIND を入れます。Ingress に対応したのは v0.9.0 からなので、第22回で入れた古いバージョンが残っている場合に備えて、入れ直しておきます。kind の公式ドキュメントでは、Go で入れる方法と、配布ページのバイナリを使う方法が案内されています。Go で入れる場合のコマンドは次のとおりです。環境の表に合わせて、バージョンを v0.12.0 に固定しています。README によると、入れたプログラムは $GOBIN(ふつうは ~/go/bin)に置かれます。

console
$ go install sigs.k8s.io/cloud-provider-kind@v0.12.0

Go を入れていない場合は、配布ページの v0.12.0 から自分の OS と CPU 用の .tar.gz を取って展開し、中の cloud-provider-kind を PATH の通った場所(一般に、シェルがコマンドを探すディレクトリ)に置きます。

参考:LoadBalancer | kind、Ingress | kind、kubernetes-sigs/cloud-provider-kind、Release v0.12.0 · kubernetes-sigs/cloud-provider-kind

3 つ目の端末で、Cloud Provider KIND を動かします。README によると、Cloud Provider KIND にはインターフェースに IP アドレスを足す権限と、特権ポートで待ち受ける権限が要ります。macOS と WSL2 では README のとおり sudo を付けて、Windows では「管理者として実行」で開いたシェルから実行する必要があります。

console
$ sudo cloud-provider-kind

Linux で sudo が要るかどうかは、README に明記がありません。第22回と同じく、まず sudo を付けずに cloud-provider-kind を実行し、権限が足りないことを知らせるエラーで止まるか、そのエラーが出続ける場合は、sudo を付けて実行してください。どちらの場合も、コマンドが見つからないことを知らせるエラーが出たら、~/go/bin/cloud-provider-kind のように置き場所を含めて指定します。Cloud Provider KIND は動き続け、3 つ目の端末にはその記録が流れると考えられます。この端末は片付けの前まで、このままにしておきます。

参考:kubernetes-sigs/cloud-provider-kind

しばらくすると、1 つ目の端末に example-ingress の行が書き足され、ADDRESS 列に IP アドレスが出るはずです。Ingress は Cloud Provider KIND を動かす前に作ってありましたが、Cloud Provider KIND は動き始めたあとに、今ある Ingress も見つけて処理すると考えられます。いつまでも IP アドレスが出なければ、3 つ目の端末にエラーが出ていないか確かめてから、example-ingress.yaml を kubectl delete -f で消し、kubectl apply -f で作り直します。

参考:Ingress | kind

2 つ目の端末で、もう一度 IngressClass を一覧します。

console
$ kubectl get ingressclasses --context kind-multi-node

cloud-provider-kind という名前の IngressClass が表示されるはずです(ソースコードからの予想)。手元で表示された名前が違う場合は、example-ingress.yaml の ingressClassName をその名前に直し、kubectl apply -f example-ingress.yaml --context kind-multi-node で渡し直します。

参考:kubectl get | Kubernetes

続けて、Gateway API の API グループと GatewayClass を一覧します。

console
$ kubectl api-resources --api-group=gateway.networking.k8s.io --context kind-multi-node
$ kubectl get gatewayclasses --context kind-multi-node

1 つ目のコマンドでは、今度は gatewayclasses、gateways、httproutes などの行が出るはずです。Kubernetes の公式ブログのとおり、Cloud Provider KIND が Gateway API の CRD をクラスタに入れたと考えられます。2 つ目のコマンドでは、cloud-provider-kind という GatewayClass が表示されるはずです。

参考:Experimenting with Gateway API using kind | Kubernetes、Experimenting with Gateway API using kind | Kubernetes

この段では、次の 3 点が合っていれば成功です。

  • 1 つ目の端末の example-ingress の行で、ADDRESS 列に IP アドレスが出る
  • cloud-provider-kind という IngressClass と GatewayClass がある
  • gateway.networking.k8s.io の API グループに、gateways と httproutes の行がある

Ingress で振り分ける

Ingress の入り口の IP アドレスは、Ingress の status.loadBalancer.ingress に書かれます。この名前は第22回で Service のフィールドとして見たもので、ingress は「入り口」の意味です。Ingress というリソースとは別のものですが、Ingress のリソースにも同じ名前のフィールドがあります。

参考:Ingress | Kubernetes API Reference

問い合わせ方は第22回の LoadBalancer の段と同じく、Docker の動かし方で次のように分かれると考えられます。README の macOS と Windows の説明は LoadBalancer の Service についてのものですが、リリースノートには macOS と Windows での Ingress(v0.10.0)と Gateway API(v0.8.0-alpha.2)の修正が載っているので、Ingress や Gateway でも同じくポートの対応付けで届けると考えられます。

環境問い合わせ先
Linux(Docker Desktop を使わない)と WSL2Ingress の IP アドレス
macOS と Windows(Linux で Docker Desktop を使う場合も)localhost と、プロキシのコンテナに対応付けられた手元のマシンのポート

参考:kubernetes-sigs/cloud-provider-kind、Releases · kubernetes-sigs/cloud-provider-kind

Linux(Docker Desktop を使わない)と WSL2 では、2 つ目の端末で Ingress の IP アドレスを変数 INGRESS_IP に入れ、/foo と /bar に問い合わせます。kind の公式ドキュメントの Ingress のページと同じ問い合わせ方です。$( ... ) は、中のコマンドの出力をその場に埋め込むシェルの書き方です。

console
$ INGRESS_IP=$(kubectl get ingress example-ingress -o jsonpath='{.status.loadBalancer.ingress[0].ip}' --context kind-multi-node)
$ curl ${INGRESS_IP}/foo
$ curl ${INGRESS_IP}/bar

参考:Ingress | kind

macOS と Windows(Linux の Docker Desktop も)では、2 つ目の端末で、名前に kindccm を含むコンテナを一覧します。

console
$ docker ps --filter name=kindccm

参考:docker container ls | Docker Docs、kubernetes-sigs/cloud-provider-kind

ソースコードからの予想では、Ingress から作られた Gateway のためのコンテナが 1 つあるはずです。PORTS 列に、->80/tcp の形で、手元のマシンのポートとコンテナの 80 番との対応が出ると考えられます。矢印の左の、: の後ろの番号を <手元のマシンのポート> に入れて問い合わせます。

console
$ curl localhost:<手元のマシンのポート>/foo
$ curl localhost:<手元のマシンのポート>/bar

kind の公式ドキュメントのコメントでは、/foo への問い合わせは foo-app を、/bar への問い合わせは bar-app を返すはず(should output)とされています。応答は Pod のホスト名(Pod の名前)と考えられ、パスによって別の Pod が応答したことがわかります。応答が無いまま止まったら Ctrl+C で止め、もう一方の環境の問い合わせ方を試してください。

この段では、/foo への問い合わせで foo-app、/bar への問い合わせで bar-app が返れば成功です。

Gateway と HTTPRoute を作る

1 つ目の端末で Ctrl+C を押して、Ingress の監視を止めます。代わりに、Gateway の一覧を監視します。Gateway API の CRD が入ったので、gateways も一覧できるようになっているはずです。この監視は片付けの前まで止めずに続けます。

console
$ kubectl get gateways --watch --context kind-multi-node

参考:kubectl get | Kubernetes

まだ example-gateway は作っていませんが、ソースコードからの予想では、Cloud Provider KIND が Ingress から作った kind-ingress-gateway の行が出ることがあります。

2 つ目の端末で、Gateway と HTTPRoute のマニフェストを順に渡します。

console
$ kubectl apply -f example-gateway.yaml --context kind-multi-node
$ kubectl apply -f example-httproute.yaml --context kind-multi-node

前の段とは違い、今度は gateway.gateway.networking.k8s.io/example-gateway と httproute.gateway.networking.k8s.io/example-httproute が作られたことを知らせる行が出るはずです。1 つ目の端末には example-gateway の行が現れます。Kubernetes の公式ブログの例では、kubectl get gateway の出力は次のとおりです。

NAME      CLASS                 ADDRESS      PROGRAMMED   AGE
gateway   cloud-provider-kind   172.18.0.3   True         5m6s

参考:Experimenting with Gateway API using kind | Kubernetes

例は gateway-infra という Namespace に作った gateway という別の Gateway のものです。手元では、NAME 列が example-gateway、CLASS 列が cloud-provider-kind になるはずです。ブログによると、PROGRAMMED 列は True になり、ADDRESS 列には IP アドレスが入るはずです。作った直後は PROGRAMMED 列が True ではなく、少しあとに行が書き足されることもあると考えられます。ADDRESS 列の IP アドレスは Ingress の IP アドレスとは別のものになると考えられます。Gateway API の移行ガイドによると、Gateway の実装はふつう Gateway ごとに専用のデータプレーン(通信を実際に運ぶ部分)を用意するからです。

参考:Experimenting with Gateway API using kind | Kubernetes、Migrating from Ingress | Gateway API

この段では、1 つ目の端末の example-gateway の行で、PROGRAMMED 列が True、ADDRESS 列に IP アドレスが出れば成功です。kubectl get httproutes --context kind-multi-node で HTTPRoute を一覧すると、example-httproute のほかに、ソースコードからの予想では example-ingress で始まる名前の HTTPRoute も並ぶことがあります。

HTTPRoute で振り分ける

Gateway のアドレスは Kubernetes の公式ブログと同じく status.addresses から取り出します。Ingress の status.loadBalancer.ingress とは、取り出す場所が違います。Linux(Docker Desktop を使わない)と WSL2 では、2 つ目の端末で Gateway の IP アドレスを変数 GW_ADDR に入れ、/foo と /bar に問い合わせます。

console
$ GW_ADDR=$(kubectl get gateway example-gateway -o jsonpath='{.status.addresses[0].value}' --context kind-multi-node)
$ curl ${GW_ADDR}/foo
$ curl ${GW_ADDR}/bar

参考:Experimenting with Gateway API using kind | Kubernetes

macOS と Windows(Linux の Docker Desktop も)では、前の段と同じく docker ps --filter name=kindccm を実行します。->80/tcp の対応を持つコンテナが、前の段より 1 つ増えているはずです。CREATED 列が新しいほうが example-gateway のコンテナと考えられるので、その手元のマシンのポートで curl localhost:<手元のマシンのポート>/foo と /bar のように問い合わせます。

参考:docker container ls | Docker Docs、kubernetes-sigs/cloud-provider-kind

Ingress と同じ転送先を同じパスで指定したので、/foo への問い合わせでは foo-app、/bar への問い合わせでは bar-app が返るはずです。マニフェストの書き方は違っても、振り分けの結果は同じになることがわかります。

この段では、Ingress のときと同じく、/foo で foo-app、/bar で bar-app が返れば成功です。

状態の表し方を比べる

最後に、設定がうまく働いているかどうかが、リソースの状態にどう表れるかを比べます。v1.37 時点の API リファレンスによると、Ingress の状態(status)にあるのは loadBalancer(ロードバランサーの今の状態)だけです。そのため Ingress で転送先の Service の名前を間違えても status には表れず、コントローラーによっては Events に出ることもありますが、curl の結果などで気づくことになると考えられます。

参考:Ingress | Kubernetes API Reference、Ingress | Kubernetes

一方、Gateway API のリソースは、コントローラーが受け入れたかどうかなどを状態の条件(conditions。第5回・第9回で見た Pod の conditions と同じ考え方)で表します。2 つ目の端末で、存在しない Service を転送先にした HTTPRoute を渡します。

console
$ kubectl apply -f broken-httproute.yaml --context kind-multi-node

httproute.gateway.networking.k8s.io/broken-httproute が作られたことを知らせる行が出るはずです。転送先の Service が無くても、HTTPRoute のオブジェクトそのものは作れると考えられます。続けて、この HTTPRoute を YAML の形で表示し、status を見ます。

console
$ kubectl get httproute broken-httproute -o yaml --context kind-multi-node

Kubernetes の公式ブログは、HTTPRoute の status.parents の条件を確かめるよう案内しています。転送先が見つからないときの例として、次の表示が載っています(そのまま引用しています)。

status:
  parents:
  - conditions:
    - lastTransitionTime: "2026-01-19T17:13:35Z"
      message: backend not found
      observedGeneration: 2
      reason: BackendNotFound
      status: "False"
      type: ResolvedRefs
    controllerName: kind.sigs.k8s.io/gateway-controller

参考:Experimenting with Gateway API using kind | Kubernetes

ブログによると、ResolvedRefs の status が "False" で reason が BackendNotFound のときは、転送先の Service が無いか、名前が間違っています。手元の broken-httproute でも、この条件が出るはずです。ブログの例は条件を 1 つだけ載せていますが、手元では type: Accepted の条件や parentRef など、例に無い行も並ぶと考えられます。lastTransitionTime の日時と observedGeneration の値は、手元では違うはずです。ブログによると、Accepted が "False" のときは、Route が Gateway に結び付けなかったことを表します。

参考:Experimenting with Gateway API using kind | Kubernetes

比べるために、正しく書いた example-httproute も、名前を替えた同じコマンドで表示してみます。status.parents の条件で、Accepted と ResolvedRefs の status がどちらも "True" になっていると考えられます。ブログは Gateway についても、Accepted と Programmed が True になり、status.addresses に IP アドレスが入っているはずだとしています。Gateway API ではこのように、誰が(どのコントローラーが)、何を受け入れたかを、リソースの状態から読み取れます。

参考:Experimenting with Gateway API using kind | Kubernetes、Experimenting with Gateway API using kind | Kubernetes

この段では、broken-httproute の status.parents に type: ResolvedRefs、status: "False"、reason: BackendNotFound の条件があり、example-httproute では Accepted と ResolvedRefs が "True" なら成功です。

片付け

1 つ目の端末で Ctrl+C を押して、監視を止めます。2 つ目の端末で、マニフェストを指定して、作ったものを削除します。Cloud Provider KIND が動いているうちに消します。Cloud Provider KIND がリソースの削除に気づいて、用意したプロキシのコンテナを片付けると考えられるからです。

console
$ kubectl delete -f broken-httproute.yaml --context kind-multi-node
$ kubectl delete -f example-httproute.yaml --context kind-multi-node
$ kubectl delete -f example-gateway.yaml --context kind-multi-node
$ kubectl delete -f example-ingress.yaml --context kind-multi-node
$ kubectl delete -f ingress-backends.yaml --context kind-multi-node

参考:kubectl delete | Kubernetes

Pod を消すコマンドは Pod の終了までの猶予の時間(既定では 30 秒)のぶん、終わるまで少しかかることがあります(第20回)。少し待ってから、残っていないかを確かめます。

console
$ kubectl get pods,services,ingresses,gateways,httproutes --context kind-multi-node

参考:Pod Lifecycle | Kubernetes、kubectl get | Kubernetes

kubernetes という Service だけが表示されると考えられます。この回で作ったものではないので、消さずに残します。ソースコードからの予想では、kind-ingress-gateway が残って見えることもあります。この Gateway は、このあとクラスタを削除すると一緒に消えます。

3 つ目の端末で Ctrl+C を押して、Cloud Provider KIND を止めます。そのあと、2 つ目の端末で、プロキシのコンテナが残っていないかを確かめます。

console
$ docker ps -a --filter name=kindccm

-a は、止まっているコンテナも含めて一覧する指定です。見出しの行だけが表示されれば、残っていません。kindccm- で始まるコンテナが残っていたら、NAMES 列の名前を入れて docker rm -f <コンテナの名前> で削除します。-f は、動いているコンテナも止めて削除する指定です。

参考:docker container ls | Docker Docs、docker container rm | Docker Docs

最後に、クラスタを削除し、消えたことを確かめます。Cloud Provider KIND がクラスタに入れた Gateway API の CRD や IngressClass も、クラスタと一緒に消えます。

console
$ kind delete cluster --name multi-node
$ kind get clusters

kind delete cluster は、存在しないクラスタを指定してもエラーを返しません。kind get clusters に multi-node が表示されなくなったことを確かめます。kubeconfig に kind-multi-node のコンテキストが残っていたら、第3回の片付けと同じく kubectl config delete-context kind-multi-node で消します。

参考:Quick Start | kind、kubectl config | Kubernetes

注意点

Ingress と Gateway API でつまずきやすい点と、バージョンや時期で変わる点を、次の表にまとめます。

項目内容
Ingress の API は凍結されている(v1.37 時点)GA のまま残り、取り除く計画は無いが、変更や更新はもう加えられない。Kubernetes のプロジェクトは Ingress の代わりに Gateway を勧めている
Ingress NGINX は 2026年3月に引退した引退した後は、リリースも、不具合や脆弱性の修正も出ない。動いているものはそのまま動き続けるが、Gateway API かほかの Ingress コントローラーへの移行をすぐに始めるよう勧められている
Ingress コントローラーごとに動きが違う実際には動きが少しずつ違い、TLS の機能にも差がある。動きの設定にはアノテーションがよく使われ、使えるものはコントローラーのドキュメントで確かめる
Ingress の pathType は必須書かないと検証で失敗する。Prefix は / で区切った要素ごとの一致で、/foo は /foobar に一致しない
既定の IngressClass は 1 つまで2 つ以上を既定にすると、ingressClassName を書かない Ingress は作れない。既定のクラスの割り当ては作成のときだけ行われる
Ingress の TLS は 443 番の 1 つだけ入り口で TLS を終端する前提で、Service と Pod へは暗号化しないで送る。Secret(第18回)のキーは tls.crt と tls.key
Gateway API は CRD を入れてから使うKubernetes 本体には入っていないので、実装に入れてもらうか自分で入れる。使用中の CRD や、コントローラーが入れた CRD は消さない
Experimental チャンネルには互換性の保証が無いいつでも互換性を壊す変更が出ることがある。既定では Standard チャンネルを使うことが勧められている

参考:Ingress | Kubernetes、Ingress | Kubernetes、Ingress | Kubernetes、Ingress | Kubernetes、Ingress | Kubernetes、Ingress | Kubernetes、Admission Control in Kubernetes | Kubernetes、Ingress NGINX Retirement: What You Need to Know | Kubernetes、kubernetes/ingress-nginx、Getting started | Gateway API、Getting started | Gateway API、Versioning | Gateway API

関連記事

Service の種類の NodePort と LoadBalancer、kind のクラスタで LoadBalancer を動かす Cloud Provider KIND については、以下の記事で解説しています。

基本仕様 ・ 第22回 NodePort と LoadBalancer の違いService をクラスタの外に公開する NodePort と LoadBalancer の違いを図で説明します。すべてのノードの同じポートで受ける NodePort と、その前に外部のロードバランサーを置く LoadBalancer の関係を押さえ、kind のクラスタで、ノードが Docker のコンテナであることによる制約と、手元のマシンから届ける方法を確かめる手順を紹介します。

Pod に入ってくる通信と出ていく通信を、ラベルで選んだ相手だけに絞る NetworkPolicy については、以下の記事で解説しています。

基本仕様 ・ 第25回 NetworkPolicy を動かして理解するPod の通信を IP アドレスやポートの単位で制限する NetworkPolicy の仕組みを図で説明します。ポリシーに選ばれた Pod だけが隔離されること、許可の規則の書き方、通信を実際に止めるのはネットワークプラグインであることを押さえ、kind のクラスタでポリシーの前後に届き方が変わる様子を確かめる手順を紹介します。

Gateway API のように、Kubernetes の API にリソースの種類を足す CRD については、以下の記事で解説しています。

基本仕様 ・ 第44回 CRD とは何かKubernetes の API に自分のリソースの種類を足す CRD(CustomResourceDefinition)の仕組みを図で説明します。CRD のマニフェストの書き方、登録すると kube-apiserver が新しいエンドポイントを用意すること、スキーマによる検証、コントローラーと組み合わせる Operator の考え方を押さえ、kind のクラスタで CRD を登録して kubectl get で扱う手順を紹介します。

Kubernetes 基本仕様の目次へ