2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

この先のためGateway APIを学んでみる

Posted at

学んだことや苦労したことを書いていきます。
本記事は個人的な見解であり、筆者の所属するいかなる団体にも関係ございません。

なお本記事は、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など)を提供・管理

image.png
引用: 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を作成します。

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 controllerstatus: "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
# 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
# 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
# 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は一筋縄ではいきませんね。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?