学んだことや苦労したことを書いていきます。
本記事は個人的な見解であり、筆者の所属するいかなる団体にも関係ございません。
なお本記事は、Kubernetesをある程度触ったことがある方向けの内容です。
0. はじめに
現在Kubernetes環境では、Ingressがよく使われています。
しかし、Ingress NGINX コントローラーが2026年3月で廃止されることが決定しているため、代替方法を身に着けておく必要があります。
恐らくGateway APIを利用していく流れになるでしょうから、この機会に学んでみました。
1. Gateway APIとは
Gateway API は、Kubernetes クラスタの内外トラフィックを扱うための標準API(CRD群)です。
以下の4つのAPIがあります。
- GatewayClass:共通の設定を持つ Gateway の集合を定義します。また、そのクラスを実装するコントローラによって管理されます
- Gateway:クラウドのロードバランサなど、トラフィック処理インフラの「インスタンス」を定義します
- HTTPRoute:Gateway のリスナーから、バックエンドのネットワークエンドポイント(多くの場合 Service)へトラフィックを振り分けるための、HTTP 向けルールを定義します
- GRPCRoute:Gateway のリスナーから、バックエンドのネットワークエンドポイント(多くの場合 Service)へトラフィックを振り分けるための、gRPC 向けルールを定義します
これらのAPIは以下のロールに基づき作成されています。
| ロール | 役割 |
|---|---|
| インフラ管理者 | Gatewayリソースを管理するためのGatewayClassを提供・管理 |
| クラスタ管理者 | GatewayClassに紐づくGatewayリソースを提供・管理 |
| アプリケーション開発者 | Gatewayとアプリケーションを紐づけるためのルーティング(HTTPRoute、TLSRouteなど)を提供・管理 |

引用: https://gateway-api.sigs.k8s.io/
示したAPIと画像が微妙に違うのは、TLSRouteはまだGAではないという事情があります。
見ての通り、Ingressと違って複数のリソースをデプロイすることによってルーティングするので、面倒が増えたとも言えますね。
ロール分割できるほどのkubernetes開発体制が作れる企業はどれだけいるのか…
2. Gateway APIを有効化
ここからは k3s で Gateway API を検証してみます。
k3s はデフォルトで Traefik が同梱されており、さらに Traefik は Gateway API(Kubernetes Gateway)コントローラとしても動作できます。
Gateway API CRDをデプロイ
Gateway API は CRD のため、まずCRDをデプロイします。
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/latest/download/standard-install.yaml
customresourcedefinition.apiextensions.k8s.io/backendtlspolicies.gateway.networking.k8s.io created
Warning: resource customresourcedefinitions/gatewayclasses.gateway.networking.k8s.io is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.
customresourcedefinition.apiextensions.k8s.io/gatewayclasses.gateway.networking.k8s.io configured
Warning: resource customresourcedefinitions/gateways.gateway.networking.k8s.io is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.
customresourcedefinition.apiextensions.k8s.io/gateways.gateway.networking.k8s.io configured
Warning: resource customresourcedefinitions/grpcroutes.gateway.networking.k8s.io is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.
customresourcedefinition.apiextensions.k8s.io/grpcroutes.gateway.networking.k8s.io configured
Warning: resource customresourcedefinitions/httproutes.gateway.networking.k8s.io is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.
customresourcedefinition.apiextensions.k8s.io/httproutes.gateway.networking.k8s.io configured
Warning: resource customresourcedefinitions/referencegrants.gateway.networking.k8s.io is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.
customresourcedefinition.apiextensions.k8s.io/referencegrants.gateway.networking.k8s.io configured
「アノテーションが無い」と警告が出ていますが、これは過去に kubectl apply 以外の方法で作られた CRD に対して apply したときに出る警告です。(TraefikはHelmでデプロイされた)
kubectl が差分管理に使う annotation を自動で補完してくれるため、基本的に無視して問題ありません。
デプロイされたかを確認します。
kubectl get crd | grep gateway.networking.k8s.io
backendtlspolicies.gateway.networking.k8s.io 2025-12-19T07:47:58Z
gatewayclasses.gateway.networking.k8s.io 2025-12-12T05:55:53Z
gateways.gateway.networking.k8s.io 2025-12-12T05:55:53Z
grpcroutes.gateway.networking.k8s.io 2025-12-12T05:55:53Z
httproutes.gateway.networking.k8s.io 2025-12-12T05:55:53Z
referencegrants.gateway.networking.k8s.io 2025-12-12T05:55:53Z
Gatewayコントローラーを有効化
手順が少しわかりにくいので、手前みそですが私の書いた記事を見て有効化していただけると助かります。
https://qiita.com/s_oshima/items/2bf2a8cdf0177f3e88fb
Traefik用RBACを適用
Traefik の公式でも Gateway API 用に RBAC を適用する手順が案内されています。
https://doc.traefik.io/traefik-hub/api-gateway/reference/install/providers/ref-provider-gatewayapi
kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.5/docs/content/reference/dynamic-configuration/kubernetes-gateway-rbac.yml
最小の GatewayClass を作って Accepted を確認
gatewayclass-traefik.yamlを作成します。
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: traefik
spec:
controllerName: traefik.io/gateway-controller
作成後デプロイします。
kubectl apply -f gatewayclass-traefik.yaml
Warning: resource gatewayclasses/traefik is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.
gatewayclass.gateway.networking.k8s.io/traefik configured
デプロイされたかを確認します。
kubectl get gatewayclass traefik -o yaml | sed -n '/status:/,$p'
status:
conditions:
- lastTransitionTime: "2025-12-12T07:21:21Z"
message: Handled by Traefik controller
observedGeneration: 1
reason: Handled
status: "True"
type: Accepted
message: Handled by Traefik controller、status: "True"、type: Acceptedの3つが揃っているため、Traefik が Gateway API のコントローラとして GatewayClass を認識している(Accepted)ことが確認できました。
次章では、Gateway / HTTPRoute / Service を用意して、実際に curl で疎通できるところまで検証します。
3. Gateway / HTTPRoute で疎通確認してみる
k3s のデフォルト構成では、Traefik が LoadBalancer Service として公開されることが多いです。まずは Traefik の Service を確認します。
kubectl -n kube-system get svc traefik
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
traefik LoadBalancer 10.43.25.70 <node-ip> 80:31028/TCP,443:30158/TCP 7d2h
EXTERNAL-IPにノードのIPが表示されるはずですので、このIPにアクセスすることとなります。
(環境によっては変わるかもしれません)
Traefik の Gateway API では、Gateway の listener port が Traefik 側の EntryPoint のポートと一致している必要があります。一致しないとエラーになり、ステータスにも反映されます。
Traefik がどのポートで待ち受けているか確認します。
kubectl -n kube-system describe deploy traefik | grep -i entrypoint -n
32: --entryPoints.metrics.address=:9100/tcp
33: --entryPoints.traefik.address=:8080/tcp
34: --entryPoints.web.address=:8000/tcp
35: --entryPoints.websecure.address=:8443/tcp
39: --metrics.prometheus.entrypoint=metrics
48: --entryPoints.websecure.http.tls=true
--entryPoints.web.address=:8000 のように見えたら HTTP は 8000
--entryPoints.websecure.address=:8443 のように見えたら HTTPS は 8443
となります。
検証用アプリをデプロイ
検証用のDeploymentを作成します。
# whoami.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: whoami
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: whoami
template:
metadata:
labels:
app: whoami
spec:
containers:
- name: whoami
image: traefik/whoami
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: whoami
namespace: default
spec:
selector:
app: whoami
ports:
- port: 80
targetPort: 80
デプロイします。
kubectl apply -f whoami.yaml
deployment.apps/whoami created
service/whoami created
kubectl get deploy,svc whoami
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/whoami 2/2 2 2 11s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/whoami ClusterIP 10.43.40.110 <none> 80/TCP 11s
Gateway を作成(HTTP)
Gatewayを作成します。
# gateway.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: web-gw
namespace: default
spec:
gatewayClassName: traefik
listeners:
- name: http
protocol: HTTP
port: 8000
デプロイします。
kubectl apply -f gateway.yaml
gateway.gateway.networking.k8s.io/web-gw created
kubectl get gateway web-gw -n default -o yaml | sed -n '/status:/,$p'
status:
addresses:
- type: IPAddress
value: <node-ip>
conditions:
- lastTransitionTime: "2025-12-19T08:31:20Z"
message: Gateway successfully scheduled
observedGeneration: 1
reason: Accepted
status: "True"
type: Accepted
- lastTransitionTime: "2025-12-19T08:31:20Z"
message: Gateway successfully scheduled
observedGeneration: 1
reason: Programmed
status: "True"
type: Programmed
listeners:
- attachedRoutes: 0
conditions:
- lastTransitionTime: "2025-12-19T08:31:20Z"
message: No error found
observedGeneration: 1
reason: Accepted
status: "True"
type: Accepted
- lastTransitionTime: "2025-12-19T08:31:20Z"
message: No error found
observedGeneration: 1
reason: ResolvedRefs
status: "True"
type: ResolvedRefs
- lastTransitionTime: "2025-12-19T08:31:20Z"
message: No error found
observedGeneration: 1
reason: Programmed
status: "True"
type: Programmed
name: http
supportedKinds:
- group: gateway.networking.k8s.io
kind: HTTPRoute
- group: gateway.networking.k8s.io
kind: GRPCRoute
ここでstatus: "True"、type: Programmedになっていれば、Traefik が Gateway を反映できています。
HTTPRoute を作成(whoami に振り分け)
HTTPRoute を作成します。
# httproute.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: whoami-route
namespace: default
spec:
parentRefs:
- name: web-gw
hostnames:
- "whoami.example.test"
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: whoami
port: 80
デプロイします。
kubectl apply -f httproute.yaml
httproute.gateway.networking.k8s.io/whoami-route created
kubectl get httproute whoami-route -n default -o yaml | sed -n '/status:/,$p'
status:
parents:
- conditions:
- lastTransitionTime: "2025-12-22T00:19:04Z"
message: ""
observedGeneration: 1
reason: Accepted
status: "True"
type: Accepted
- lastTransitionTime: "2025-12-22T00:19:04Z"
message: ""
observedGeneration: 1
reason: ResolvedRefs
status: "True"
type: ResolvedRefs
controllerName: traefik.io/gateway-controller
parentRef:
group: gateway.networking.k8s.io
kind: Gateway
name: web-gw
ここでstatus: "True"、type: ResolvedRefsになっていれば、参照(Service 等)が解決され Route が受理されています。
疎通確認
curlで疎通確認します。章の頭に確認したノードIPを利用します。
HTTPRoute で定義したホスト名をheadersに入れてアクセスします。
curl -H "Host: whoami.example.test" http://<node-ip>/
Hostname: whoami-64f6cf779d-m6dql
IP: 127.0.0.1
IP: ::1
IP: 10.42.0.20
IP: fe80::74a2:c0ff:fe99:1dea
RemoteAddr: 10.42.0.18:49992
GET / HTTP/1.1
Host: whoami.example.test
User-Agent: curl/7.81.0
Accept: */*
Accept-Encoding: gzip
X-Forwarded-For: 10.42.0.1
X-Forwarded-Host: whoami.example.test
X-Forwarded-Port: 80
X-Forwarded-Proto: http
X-Forwarded-Server: traefik-758d67f9f9-gt4ck
X-Real-Ip: 10.42.0.1
whoami の応答(Host/headers 情報)が返れば成功です。
4. まとめ
ということで、GatewayAPIの簡単な使い方を確認してみました。
改めて、必要なリソースは以下となります。
- Gateway API CRD
- Traefik 用 Gateway API RBAC
- GatewayClass(Traefik 用)
- Gateway(HTTP listener)
- HTTPRoute
- 接続先アプリケーション
こう見ると、Ingressと比べてずいぶん大変だなぁという感想ですね。
継続して使っていって、勘所を掴むまで苦労しそうです。
やっぱりKubernetesは一筋縄ではいきませんね。