[アップデート] Amazon EKS Auto ModeでKubernetes Gateway APIが使えるようになりました

[アップデート] Amazon EKS Auto ModeでKubernetes Gateway APIが使えるようになりました

EKS Auto ModeがKubernetes Gateway APIに対応しました。クラスターの設定でGateway APIを有効にすれば、AWS Load Balancer Controllerを自分で入れなくても、GatewayやHTTPRouteを作成するだけでALBが作られます。今回は重み付けルーティングやACM証明書の自動検出などを実際に試した結果をまとめます。
2026.11.12

はじめに

皆様こんにちは、あかいけです。

EKS Auto ModeでGateway APIを使いたいけど、そのためだけにAWS Load Balancer Controllerを入れて運用したくない…と思ったことはありますか?私はあります。

そして先日、EKS Auto ModeがKubernetes Gateway APIに対応するアップデートがありました。
なんとこのアップデートにより、EKS側の設定を有効化するだけでKubernetes Gateway APIが使えるようになったようです。

https://aws.amazon.com/about-aws/whats-new/2026/10/amazon-eks-auto-mode-kubernetes-gateway-api/

というわけで今回は、EKS Auto ModeのクラスターでGateway APIを有効にして、各種機能を試してみた結果をまとめます。

アップデートの概要

これまでAuto ModeのクラスターでGateway APIを使うには、AWS Load Balancer Controllerなどのコントローラーを自分でインストールし、バージョンアップなどの運用も自分で行う必要がありました。

今回のアップデートで、Auto ModeがGateway API用のコントローラーを提供するようになりました。クラスターの設定でGateway APIを有効にすることで、GatewayやHTTPRouteを作成するだけで、Auto ModeがALBやNLBを作成します。

  • 主要機能
    • HTTPとgRPCのルーティング、Namespaceをまたいだルーティング、重み付けルーティング、ヘッダーベースのルーティング、ACM証明書の自動検出に対応
    • 既存のIngressやService(type: LoadBalancer)と共存できる
  • 設定方法
    • クラスターのgatewayApi.crds.managedをtrue(Managed)にすると、AWSがGateway APIのCRDをインストールして管理する
    • スキームなどのAWS固有の設定は、AWS Load Balancer Controllerと同じ拡張リソース(LoadBalancerConfigurationなど)で行う
  • コスト
    • 追加料金はない(作成されたロードバランサーの料金はかかる)

https://docs.aws.amazon.com/eks/latest/userguide/auto-configure-gateway-api.html

https://docs.aws.amazon.com/eks/latest/userguide/auto-change.html

検証環境

  • Kubernetesバージョン: 1.37
  • クラスター: マネジメントコンソールのカスタム設定で、EKS Auto Modeを有効にして作成
  • クラスターIAMロール: Auto Modeで標準の5つのマネージドポリシーを付与
    • AmazonEKSClusterPolicy、AmazonEKSComputePolicy、AmazonEKSBlockStoragePolicy、AmazonEKSLoadBalancingPolicy、AmazonEKSNetworkingPolicy

AWS 構成

AWS上の構成は以下のとおりです。

eks-auto-gwapi-aws-architecture.drawio

図のサブネットに付けているタグは、EKS Auto Modeがロードバランサーを置くサブネットを選ぶためのものです。
インターネット向けのALBはkubernetes.io/role/elb: 1、内部向けのALBはkubernetes.io/role/internal-elb: 1のタグが付いたサブネットに作られます。

EKSクラスター 構成

次にEKSクラスター内の構成です。

eks-auto-gwapi-k8s-architecture.drawio

Gatewayは共通インフラ用にgateway-infraのNamespaceに置き、アプリはそれぞれのアプリ用のNamespaceでHTTPRouteを作るイメージです。
またteam-aのNamespaceにだけgateway-access: "true"のラベルを付けて、Gatewayにルートを紐付けられるNamespaceを制限しています。

なおサンプルアプリには、決まった文字列を返すだけのhashicorp/http-echoを使い、team-aにecho-v1とecho-v2、team-bにecho-bを置きます。

試してみた(Gatewayを作成するまで)

まずは、Gateway APIを有効にしたAuto Modeのクラスターを作成し、ALBが作られるまでを確認します。

クラスターを作成する

マネジメントコンソールから、カスタム設定でEKS Auto Modeを有効にしたクラスターを作成します。
Gateway APIの設定は、「Gateway API CRD 管理」の項目で選びます。
アンマネージドがmanaged: false、マネージドがmanaged: trueにあたります。
今回はAWSにCRDを管理してもらうため、マネージドを選びました。

EKS オートモード_あり

なお、Auto Modeを無効にしている場合は、「Gateway API CRD 管理は、EKS Auto Mode が有効になっている場合にのみご利用いただけます。」と表示され、どちらも選べません。

EKS オートモード_なし

また、クイック設定(EKS オートモード付き)では、画面に表示される設定値の一覧で「Gateway API CRD 管理」が「アンマネージド」になっていました。
作成時にマネージドを選ぶ項目は見当たらないため、クイック設定で作る場合は作成後にマネージドへ変更する必要がありそうです。

EKS_クイック設定 (EKS オートモード付き)

Gateway APIのCRDを確認する

クラスターの作成が完了したら、Gateway APIのCRDがインストールされているかを確認します。

$ kubectl get crd -o custom-columns='NAME:.metadata.name,BUNDLE:.metadata.annotations.gateway\.networking\.k8s\.io/bundle-version,CHANNEL:.metadata.annotations.gateway\.networking\.k8s\.io/channel' | grep -E 'NAME|gateway'
NAME                                         BUNDLE   CHANNEL
gatewayclasses.gateway.networking.k8s.io     v1.6.0   standard
gateways.gateway.networking.k8s.io           v1.6.0   standard
grpcroutes.gateway.networking.k8s.io         v1.6.0   standard
httproutes.gateway.networking.k8s.io         v1.6.0   standard
listenerruleconfigurations.gateway.k8s.aws   <none>   <none>
listenersets.gateway.networking.k8s.io       v1.6.0   standard
loadbalancerconfigurations.gateway.k8s.aws   <none>   <none>
referencegrants.gateway.networking.k8s.io    v1.6.0   standard
targetgroupconfigurations.gateway.k8s.aws    <none>   <none>
tcproutes.gateway.networking.k8s.io          v1.6.0   standard
tlsroutes.gateway.networking.k8s.io          v1.6.0   standard
udproutes.gateway.networking.k8s.io          v1.6.0   standard

Gateway APIのCRDと、AWS拡張リソース(gateway.k8s.aws)のCRDがインストールされていました。Gateway APIのCRDは、v1.6.0のstandardチャネルのものでした。

GatewayClassのCRDのアノテーションも見てみます。

$ kubectl get crd gatewayclasses.gateway.networking.k8s.io -o jsonpath='{.metadata.annotations}' | jq .
{
  "api-approved.kubernetes.io": "https://github.com/kubernetes-sigs/gateway-api/pull/4530",
  "eks.amazonaws.com/managed": "true",
  "gateway.networking.k8s.io/bundle-version": "v1.6.0",
  "gateway.networking.k8s.io/channel": "standard"
}

eks.amazonaws.com/managed: "true"のアノテーションが付いており、AWSが管理しているCRDだとわかります。

サンプルアプリをデプロイする

次に、Namespaceとサンプルアプリを作成します。
Namespaceはgateway-infra、team-a、team-bの3つで、team-aにだけgateway-access: "true"のラベルを付けます。

00-namespaces.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: gateway-infra
---
apiVersion: v1
kind: Namespace
metadata:
  name: team-a
  labels:
    gateway-access: "true"
---
apiVersion: v1
kind: Namespace
metadata:
  name: team-b

アプリはteam-aにecho-v1とecho-v2、team-bにecho-bを作ります。
3つとも同じイメージを使い、返す文字列だけを変えています。

02-apps.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: echo-v1
  namespace: team-a
spec:
  replicas: 1
  selector:
    matchLabels:
      app: echo-v1
  template:
    metadata:
      labels:
        app: echo-v1
    spec:
      containers:
        - name: echo
          image: hashicorp/http-echo:1.0
          args: ["-text=hello from team-a v1"]
          ports:
            - containerPort: 5678
          resources:
            requests:
              cpu: 50m
              memory: 32Mi
---
apiVersion: v1
kind: Service
metadata:
  name: echo-v1
  namespace: team-a
spec:
  selector:
    app: echo-v1
  ports:
    - port: 80
      targetPort: 5678
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: echo-v2
  namespace: team-a
spec:
  replicas: 1
  selector:
    matchLabels:
      app: echo-v2
  template:
    metadata:
      labels:
        app: echo-v2
    spec:
      containers:
        - name: echo
          image: hashicorp/http-echo:1.0
          args: ["-text=hello from team-a v2"]
          ports:
            - containerPort: 5678
          resources:
            requests:
              cpu: 50m
              memory: 32Mi
---
apiVersion: v1
kind: Service
metadata:
  name: echo-v2
  namespace: team-a
spec:
  selector:
    app: echo-v2
  ports:
    - port: 80
      targetPort: 5678
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: echo-b
  namespace: team-b
spec:
  replicas: 1
  selector:
    matchLabels:
      app: echo-b
  template:
    metadata:
      labels:
        app: echo-b
    spec:
      containers:
        - name: echo
          image: hashicorp/http-echo:1.0
          args: ["-text=hello from team-b"]
          ports:
            - containerPort: 5678
          resources:
            requests:
              cpu: 50m
              memory: 32Mi
---
apiVersion: v1
kind: Service
metadata:
  name: echo-b
  namespace: team-b
spec:
  selector:
    app: echo-b
  ports:
    - port: 80
      targetPort: 5678
$ kubectl apply -f 00-namespaces.yaml -f 02-apps.yaml
namespace/gateway-infra created
namespace/team-a created
namespace/team-b created
deployment.apps/echo-v1 created
service/echo-v1 created
deployment.apps/echo-v2 created
service/echo-v2 created
deployment.apps/echo-b created
service/echo-b created

デプロイ後に全NamespaceのPodを確認すると、表示されたのはアプリのPodだけでした。

$ kubectl get pods -A
NAMESPACE   NAME                       READY   STATUS    RESTARTS   AGE
team-a      echo-v1-d747fdc94-6gxzz    1/1     Running   0          71s
team-a      echo-v2-56445bbf74-mvg62   1/1     Running   0          71s
team-b      echo-b-76b465cc87-7zvwv    1/1     Running   0          71s

このことからもゲートウェイのコントローラーは、クラスター内のPodとしては動いていないようです。なのでコントローラーのインストールやバージョンアップを、利用者が行う必要はなさそうですね。

GatewayClassとGatewayを作成する

いよいよGateway APIのリソースを作ります。

まずはGatewayClassを作成します。
controllerNameにはALBを作るコントローラーを指定します。

01-gatewayclass.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: eks-auto-alb
spec:
  controllerName: eks.amazonaws.com/gateway-alb

ACCEPTEDがTrueになればOKです。

$ kubectl apply -f 01-gatewayclass.yaml
gatewayclass.gateway.networking.k8s.io/eks-auto-alb created

$ kubectl get gatewayclass
NAME           CONTROLLER                      ACCEPTED   AGE
eks-auto-alb   eks.amazonaws.com/gateway-alb   True       1s

次にGatewayを作成します。

Gatewayから作られるALBのスキームは、既定ではinternalですが、今回は手元のPCからALBに直接アクセスするため、LoadBalancerConfigurationでscheme: internet-facingを指定します。

https://kubernetes-sigs.github.io/aws-load-balancer-controller/latest/guide/gateway/loadbalancerconfig/

LoadBalancerConfigurationはGatewayと同じNamespaceに置き、Gatewayのspec.infrastructure.parametersRefで参照します。
また、allowedRoutesで、gateway-access: "true"のラベルが付いたNamespaceのルートだけを受け付けるようにしています。

03-gateway.yaml
apiVersion: gateway.k8s.aws/v1
kind: LoadBalancerConfiguration
metadata:
  name: internet-facing
  namespace: gateway-infra
spec:
  scheme: internet-facing
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: shared-gateway
  namespace: gateway-infra
spec:
  gatewayClassName: eks-auto-alb
  infrastructure:
    parametersRef:
      group: gateway.k8s.aws
      kind: LoadBalancerConfiguration
      name: internet-facing
  listeners:
    - name: http
      protocol: HTTP
      port: 80
      allowedRoutes:
        namespaces:
          from: Selector
          selector:
            matchLabels:
              gateway-access: "true"

数分でPROGRAMMEDがTrueになり、ALBが作成されました。
ALBはinternet-facingのスキームで、パブリックサブネットに作成されています。

$ kubectl apply -f 03-gateway.yaml
loadbalancerconfiguration.gateway.k8s.aws/internet-facing created
gateway.gateway.networking.k8s.io/shared-gateway created

$ kubectl wait --for=condition=Programmed gateway/shared-gateway -n gateway-infra --timeout=300s
gateway.gateway.networking.k8s.io/shared-gateway condition met

$ kubectl get gateway -n gateway-infra
NAME             CLASS          ADDRESS                                                                       PROGRAMMED   AGE
shared-gateway   eks-auto-alb   k8s-gatewayi-sharedga-f98c6e5eb2-326408971.ap-northeast-1.elb.amazonaws.com   True         4m40s

試してみた(ルーティングとHTTPSの確認)

次に、ここまでで作ったGatewayを使って、アップデート記事で紹介されている機能を試します。
今回は重み付けルーティング、ヘッダーベースのルーティング、Namespaceをまたいだルーティング、ACM証明書の自動検出を試してみます。

なお、試す機能自体はもともとAWS Load Balancer ControllerのGateway API対応で使えていたものです。対応しているフィールドなどの詳細は、AWS Load Balancer Controllerのドキュメントにまとまっています。

https://aws.amazon.com/blogs/networking-and-content-delivery/aws-load-balancer-controller-adds-general-availability-support-for-kubernetes-gateway-api/

重み付けルーティングとヘッダーベースのルーティング

team-aにHTTPRouteを作成します。
ルールは次の2つです。

  • x-canary: trueヘッダーが付いたリクエストはecho-v2へ送る
  • それ以外のリクエストはecho-v1とecho-v2に80:20の割合で振り分ける

Gatewayは別のNamespaceにあるので、parentRefsにnamespaceを指定します。

04-httproute-team-a.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: echo-route
  namespace: team-a
spec:
  parentRefs:
    - name: shared-gateway
      namespace: gateway-infra
  rules:
    # x-canary: true ヘッダー付きのリクエストはv2へ
    - matches:
        - headers:
            - name: x-canary
              type: Exact
              value: "true"
      backendRefs:
        - name: echo-v2
          port: 80
    # それ以外はv1:v2 = 80:20で振り分け
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        - name: echo-v1
          port: 80
          weight: 80
        - name: echo-v2
          port: 80
          weight: 20

適用すると、HTTPRouteはAcceptedになりました。

$ kubectl get httproute echo-route -n team-a -o jsonpath='{.status.parents}' | jq -c '.[] | .conditions[] | {type,status,reason,message}'
{"type":"ResolvedRefs","status":"True","reason":"ResolvedRefs","message":""}
{"type":"Accepted","status":"True","reason":"Accepted","message":""}

curlで100回リクエストして、振り分けの割合を見てみます。

$ ADDRESS=$(kubectl get gateway shared-gateway -n gateway-infra -o jsonpath='{.status.addresses[0].value}')

$ for i in $(seq 1 100); do curl -s http://$ADDRESS/; done | sort | uniq -c
  77 hello from team-a v1
  23 hello from team-a v2

77:23で、指定した80:20に近い割合で振り分けられています。

次に、x-canary: trueヘッダーを付けて20回リクエストします。

$ for i in $(seq 1 20); do curl -s -H 'x-canary: true' http://$ADDRESS/; done | sort | uniq -c
  20 hello from team-a v2

20回すべてecho-v2に届きました。

ALBのHTTP:80リスナーのルールを確認すると、HTTPRouteの内容は次のように変換されていました。

優先度 条件 アクション
1 パスが/*、かつヘッダーx-canaryがtrue echo-v2のターゲットグループへ転送
2 パスが/* echo-v1(重み80)とecho-v2(重み20)のターゲットグループへ転送
default なし 固定レスポンス(404)

重み付けは、ALBの転送アクションのターゲットグループの重みとして設定されています。
ヘッダー条件だけを書いたルールにも、パスが/*の条件が付いていました。
Gateway APIではmatchesにパスを書かない場合、パスは/の前方一致(PathPrefix)として扱われます。
この既定値が、ALBのルールにも反映されています。

ターゲットグループは、ターゲットタイプがip、ポートがコンテナのポート(5678)で作成されていました。
ALBはServiceのポート(80)を経由せず、PodのIPアドレスの5678番ポートへ直接転送します。

Namespaceをまたいだルーティング

先ほどのteam-aのHTTPRouteは、別のNamespace(gateway-infra)にあるGatewayに紐付けていたので、実はこれ自体がNamespaceをまたいだルーティングになっていました。

念のため、Gatewayが許可していないNamespaceから紐付けようとするとどうなるかも試してみます。
ラベルを付けていないteam-bから、同じGatewayにHTTPRouteを紐付けます。

05-httproute-team-b.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: echo-route
  namespace: team-b
spec:
  parentRefs:
    - name: shared-gateway
      namespace: gateway-infra
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /team-b
      backendRefs:
        - name: echo-b
          port: 80

AcceptedがFalse、理由はNotAllowedByListenersとなり、紐付けは拒否されました。
拒否されたのは、team-bのNamespaceにGatewayが求めるラベルがないためです。

$ kubectl get httproute echo-route -n team-b -o jsonpath='{.status.parents}' | jq -c '.[] | .conditions[] | {type,status,reason,message}'
{"type":"ResolvedRefs","status":"True","reason":"ResolvedRefs","message":""}
{"type":"Accepted","status":"False","reason":"NotAllowedByListeners","message":"Listener does not allow route attachment, namespace does not match between listener and route"}

もちろんALBのリスナールールにもteam-bのルールは追加されておらず、/team-bへのリクエストはteam-aの/のルールで処理されました。
(team-aのHTTPRouteは/の前方一致ですべてのパスに一致するため、/team-bへのリクエストもこのルールで処理されています)

$ curl -s http://$ADDRESS/team-b
hello from team-a v2

結論として、ルートを受け付けるNamespaceは、Gatewayを管理する側が決められます。
そのため複数のチームで1つのALBを共有する場合も、許可していないチームが勝手にALBへルールを追加する、といったことはできなそうですね。(各チームがGatewayやNamespaceのラベルを編集できないよう、KubernetesのRBACで権限を絞っている前提ではありますが…)

ACM証明書の自動検出

ACMでgwapi.example.comの証明書を東京リージョンで発行します。
GatewayではHTTPSのリスナーを追加し、hostnameに証明書のドメイン名 gwapi.example.com を指定します。

09-gateway-https.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: shared-gateway
  namespace: gateway-infra
spec:
  gatewayClassName: eks-auto-alb
  infrastructure:
    parametersRef:
      group: gateway.k8s.aws
      kind: LoadBalancerConfiguration
      name: internet-facing
  listeners:
    - name: http
      protocol: HTTP
      port: 80
      allowedRoutes:
        namespaces:
          from: Selector
          selector:
            matchLabels:
              gateway-access: "true"
    - name: https
      protocol: HTTPS
      port: 443
      hostname: gwapi.example.com
      allowedRoutes:
        namespaces:
          from: Selector
          selector:
            matchLabels:
              gateway-access: "true"

Gateway APIの仕様では、リスナーのtls.certificateRefsでKubernetesのSecretを参照して証明書を指定します。
一方、AWS Load Balancer ControllerのドキュメントではcertificateRefsは未対応とされており、証明書はLoadBalancerConfigurationで指定するか、hostnameをもとに自動検出させます。

https://kubernetes-sigs.github.io/aws-load-balancer-controller/latest/guide/gateway/l7gateway/

今回はtlsの設定を書かずにhostnameだけを指定しましたが、Gatewayはそのまま受け付けられました。

適用後にALBのリスナーを確認すると、HTTPS:443のリスナーが追加され、ACMの証明書が自動で設定されていました。

リスナー 証明書 SSLポリシー
HTTPS:443 ACMで発行したgwapi.example.comの証明書 ELBSecurityPolicy-2016-08
HTTP:80 なし なし

HTTPS:443のリスナールールには、HTTP:80と同じ条件に加えて、host-headerがgwapi.example.comという条件が付いていました。
Gatewayのリスナーに書いたhostnameは、ALBではリスナールールの条件になります。

Route 53でgwapi.example.comのエイリアスレコードをALBに向けて、HTTPSでアクセスしてみます。

$ curl -sv https://gwapi.example.com/ 2>&1 | grep -E 'SSL connection|subject:|expire date|issuer:|< HTTP/|^hello'
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256 / [blank] / UNDEF
*  subject: CN=gwapi.example.com
*  expire date: May 24 23:59:59 2027 GMT
*  issuer: C=US; O=Amazon; CN=Amazon RSA 2048 M01
< HTTP/2 200
hello from team-a v1

ACMで発行した証明書でHTTPSの通信ができました。

余談:デフォルトのSSLポリシーについて

Gatewayで作成されたALBのHTTPSリスナーには、SSLポリシーとして既定でELBSecurityPolicy-2016-08が設定されていました。
このポリシーはTLS 1.0とTLS 1.1も許可するため、本番環境ではLoadBalancerConfigurationのlistenerConfigurationsでsslPolicyを指定しておくのがよさそうです。

今回は試していませんが、ドキュメントの記載に沿うと次のように書くようです。

apiVersion: gateway.k8s.aws/v1
kind: LoadBalancerConfiguration
metadata:
  name: internet-facing
  namespace: gateway-infra
spec:
  scheme: internet-facing
  listenerConfigurations:
    - protocolPort: HTTPS:443
      sslPolicy: ELBSecurityPolicy-TLS13-1-2-2021-06

https://kubernetes-sigs.github.io/aws-load-balancer-controller/latest/guide/gateway/loadbalancerconfig/

さいごに

以上、EKS Auto ModeのKubernetes Gateway API対応の紹介でした。

Auto Modeのクラスターであれば、「Gateway API CRD 管理」をマネージドにするだけでGateway APIが使えるようになります。
コントローラーのインストールやバージョンアップを気にせず、重み付けルーティングやNamespaceをまたいだルーティングを使えるため、なかなか嬉しいのではないでしょうか。

これからEKS Auto ModeでGateway APIを使おうとしている方の参考になれば幸いです。

この記事をシェアする

AWSのお困り事はクラスメソッドへ

関連記事