🔀

GAになったGKE Gateway controller を改めて検証してみた

2022/12/12に公開

こんにちは、クラウドエースの阿部です。
こちらの記事では、2022/11/09に一般提供(GA)になった GKE Gateway controller について検証していこうと思います。

GKE Gateway controller の概要

GKE Gateway controller は、GKE における Kubernetes Gateway API の実装になります。
Kubernetes Gateway API は、 Ingress API で実現していた汎用的なAPIと様々なプラットフォーム上での実装仕様を持っていた特性を引き継ぎつつ、
Ingress API の課題を改善するためにNetwork-SIGによって開発されたAPIです。
なお、こちらのFAQにもありますが、Gateway APIはIngress APIの置き換えを目指しているわけではありません。

Kubernetes Gateway API について

Kubernetes Gateway API は以下のような仕様です。

  • GatewayClass, Gateway, HTTPRoute といった複数のリソース定義を使って複数のサービスのトラフィックを管理します。
  • GatewayHTTPRoute を分離して管理することで、クラスタ管理者とアプリケーション開発者のロールを分離することができます。
  • Ingress ではできなかった、Namespaceを跨いだ構成も可能です。

GKE Gateway controller について

GKE の Gateway controller は Gateway API をGKE上で実現するための実装になるため、基本的には Kubernetes Gateway API の仕様に準じます。
ただし、Kubernetes Gateway API は、 TCPRouteUDPRoute といったリソース定義が存在し、L7だけでなくL4のトラフィックも管理可能ですが、2022/12現在のGKE Gateway controllerはL7トラフィックのみ管理可能です。

GKE Gateway controller を使用するためには、最低限以下の要件を満たす必要があります。

  • GKEクラスタのバージョンがv1.24以降であること
  • GKEクラスタがVPCネイティブクラスタであること

また、Istioや内部負荷分散を使用したい場合は追加の要件を満たす必要があります。
詳細は「GKE Gateway コントローラの要件」のドキュメントを参照してください。

Gateway API のリソース

GKEがサポートするGateway APIのリソースは下記の表の通りです。

リソース(kind) 説明
GatewayClass Gateway APIを使って構築する負荷分散・トラフィック制御機能の種類を定義するリソースです。GKE Gateway controllerでは4つのGatewayClassが事前定義されており、ユーザーが追加で作成する必要はありません。
Gateway IPアドレスや受信プロトコル、TLS暗号の有無等、フロントエンド設定を定義するリソースです。
HTTPRoute ホスト名やパスに従って1つ以上のサービスにトラフィックを振り分ける設定を定義するリソースです。Cloud Load Balancing で言うところのURL Mapを設定します。複数の HTTPRoute を設定でき、Gateway API側で1つの負荷分散設定としてマージされます。
xxPolicy Gateway controllerで管理するCloud Load Balancingに追加設定を行うためのリソースです。記事執筆時点(2022/12)では、LBPolicyとHealthCheckPolicyの2種類が使用可能です。ドキュメントはこちらを参照してください。

GKE Gateway controller が定義する GatewayClass について

本記事執筆時点(2022年12月)における、GKE Gateway controller の GatewayClass は以下の通りです。

GatewayClass名 リリース段階 ロケーション クラスタタイプ ネットワーク到達性 高度なトラフィック管理
gke-l7-gxlb GA グローバル シングルクラスタ インターネット なし
gke-l7-rilb GA リージョン シングルクラスタ VPC内部 トラフィック分割、ミラーリング等
gke-l7-gxlb-mc Preview グローバル マルチクラスタ インターネット なし
gke-l7-rilb-mc Preview リージョン マルチクラスタ VPC内部 トラフィック分割、ミラーリング等

上記以外の機能の違いについては、下記のGatewayClassに関するドキュメントをご確認ください。

参考情報

https://cloud.google.com/kubernetes-engine/docs/how-to/gatewayclass-capabilities

GKE Gateway controller が作成する Cloud Load Balancing について

本記事執筆時点(2022年12月)における GKE Regular チャンネルのバージョン(v1.24.5-gke.600)では、実際にデプロイされるCloud Load Balancingとして「グローバル外部 HTTP(S) ロードバランサ」ではなく「グローバル外部 HTTP(S) ロードバランサ(従来)」を使用します。
そのためかは分かりませんが、 gke-l7-gxlbgke-l7-gxlb-mcでは高度なトラフィック管理機能を使う事ができません。

GKE Gateway controller の基本的な使い方

ここでは、GKE Gateway controller の基本的な使い方を説明していきたいと思います。
GKE Gateway controllerでは、外部負荷分散と内部負荷分散の両方を作成することができますが、この記事では外部負荷分散であるgke-l7-gxlbの使い方を説明します。

GKEクラスタのセットアップ

静的IPアドレスの予約

以下のコマンドでグローバル パブリック IP アドレス な静的IPアドレスを予約します。
ここでは、gke-gateway-vip という名前でIPアドレスリソースを作成します。
PROJECT_IDはお使いの Google Cloud プロジェクトIDに置き換えて下さい。

gcloud compute addresses create gke-gateway-vip --global --project=PROJECT_ID

コマンド実行後、以下のコマンドで予約したIPアドレスを確認します。後で使います。

gcloud compute addresses describe gke-gateway-vip --global --format="get(address)"

検証用ドメインの予約

次に、予約したIPアドレスにドメインを設定します。
今回の検証ではSSLを設定したいので、ドメインを設定します。検証用のドメインを持っている人はそちらを使って頂いてよいと思います。
ここでは、Google Cloud プロジェクトで無料で利用できるCloud Endpointsドメインを使います。

まず、以下のようなYAMLファイルを用意します。ファイル名は openapi.yaml とします。

openapi.yaml
openapi.yaml
swagger: '2.0'
info:
  title: 適当なタイトル
  version: 1.0.0
host: "{SERVICENAME}.endpoints.{PROJECT_ID}.cloud.goog"
x-google-endpoints:
  - name: "{SERVICENAME}.endpoints.{PROJECT_ID}.cloud.goog"
    target: "予約したIPアドレス"
paths: {}
  • title: 設定ファイルのタイトルを記述します。「Test project」のようなものでOKです。
  • host: ドメイン(FQDN)になります。SERVICENAMEの部分は数字・英小文字・ハイフンで構成された最大60文字程度の単語で置き換えてください。
    PROJECT_IDはお使いのGoogle Cloud プロジェクトIDに置き換えてください。
  • name: hostと同じにしてください。
  • target: 先の手順で予約したIPアドレスを記述してください。

次に、Cloud Endpoints APIが有効になっているかを確認します。

gcloud services list --enabled --filter=name:endpoints.googleapis.com

上記コマンドで、 Listed 0 items. と表示された場合はまだ Cloud Endpoints API が有効になっていませんので、以下のコマンドを実行します。

gcloud services enable endpoints.googleapis.com

Cloud Endpoints APIが有効な状態で、以下のコマンドを実行します。
PROJECT_IDは、お使いのGoogle Cloud プロジェクトIDに置き換えて下さい。

gcloud endpoints services deploy openapi.yaml --project=PROJECT_ID

特にエラーがでなければ、openapi.yamlに記述した内容でドメインを使うことが使う事ができると思います。

Google Managed SSL 証明書の作成

ドメインを予約した後は、ドメインに対応するGoogle Managed SSL証明書を作成します。
ここでは、gke-gateway-certという名前で証明書リソースを作成します。
--domainsオプションには、先ほどCloud Endpointsで設定したhost名を記述します。
以下のコマンドを実行します。

gcloud compute ssl-certificates create gke-gateway-cert \
    --domains="{SERVICENAME}.endpoints.{PROJECT_ID}.cloud.goog" \
    --global

GKEクラスタの作成

本題のGKEクラスタを作成します。2022年12月時点では、Cloud consoleからはGateway controllerを有効化できないため、コマンドラインで作成する必要があります。
基本的な作成方法は通常のGKE Standardクラスタと同様ですが、 --gateway-api=standard オプションを付ける必要があります。また、クラスタのバージョンは1.24以降である必要があります。

最初に、以下のコマンドで、現在REGULARチャンネルで選択できるバージョンを確認します。

gcloud container get-server-config --region=asia-northeast1 | grep -A5 "channel: REGULAR"

このとき、1.24以降でなるべく新しいバージョンを確認します。記事執筆時点では、 1.24.5-gke.600 が選択可能でした。
以下のコマンドを実行します。 --cluster-version で指定するバージョンは、前述のコマンドで確認したものに適宜置き換えてください。
また、以下のコマンドラインでは、検証費用を抑えるための --spot オプションを付与していますので、検証の目的に応じて外してください。

gcloud container clusters create "gke-gateway-cluster" --region "asia-northeast1" \
  --cluster-version "1.24.5-gke.600" --release-channel "regular" \
  --machine-type "e2-medium" --disk-size "50" --spot --num-nodes "1" \
  --enable-ip-alias --gateway-api=standard

しばらくするとGKEクラスタが作成されます。(最近のgcloudコマンドは認証も設定してくれますが)GKEクラスタの認証設定も行います。

gcloud container clusters get-credentials gke-gateway-cluster --region asia-northeast1

GKEクラスタでGateway APIが有効化されているかを確認します。以下のコマンドを実行します。

kubectl get gatewayclass

以下のように gke-l7-gxlbgke-l7-rilbが表示されれば問題ありません。

NAME          CONTROLLER                  ACCEPTED   AGE
gke-l7-gxlb   networking.gke.io/gateway   True       158m
gke-l7-rilb   networking.gke.io/gateway   True       158m

同じ Namespace の Service へのルーティング

GatewayHTTPRoute、および、 ServicePod が同じ Namespace に配置するケースでk8sマニフェストを記述してみます。

デモアプリケーションのデプロイ

まず、Google公式リファレンスにあるデモアプリケーションをデプロイします。

kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/gke-networking-recipes/main/gateway/gke-gateway-controller/app/store.yaml

以下のように、default Namespace にstore-v1store-v2store-germanサービスとDeploymentがデプロイされます。

表示例
$ kubectl get pod
NAME                            READY   STATUS    RESTARTS   AGE
store-german-7dbb4b97cd-fr7hn   1/1     Running   0          21s
store-german-7dbb4b97cd-x4zst   1/1     Running   0          21s
store-v1-6d8d58d78-8s64q        1/1     Running   0          22s
store-v1-6d8d58d78-qlz96        1/1     Running   0          22s
store-v2-5d66fbc946-dxnr2       1/1     Running   0          21s
store-v2-5d66fbc946-llclw       1/1     Running   0          21s

$ kubectl get service
NAME           TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
kubernetes     ClusterIP   10.8.0.1     <none>        443/TCP    171m
store-german   ClusterIP   10.8.9.176   <none>        8080/TCP   24s
store-v1       ClusterIP   10.8.6.220   <none>        8080/TCP   24s
store-v2       ClusterIP   10.8.0.250   <none>        8080/TCP   24s

Gateway をデプロイ

次に、Gateway リソースをデプロイします。
下記のk8s YAMLを記述します。
クラスタ作成のセクションで、IPアドレスをgke-gateway-vip、証明書リソースをgke-gateway-certで作成しています。別名で作成している方は、YAMLファイルの該当箇所も読み替えて下さい。

gateway.yaml
gateway.yaml
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
  name: external-https
spec:
  gatewayClassName: gke-l7-gxlb
  listeners:
    - name: https
      protocol: HTTPS
      port: 443
      tls:
        mode: Terminate
        options:
          networking.gke.io/pre-shared-certs: gke-gateway-cert
  addresses:
    - type: NamedAddress
      value: gke-gateway-vip

記述のポイントとしては以下の通りです。

  • metadata.name にはGatewayリソースの名前を記述します。
  • spec.gatewayClassName には定義済みのGatewayClassを指定します。
  • HTTPSを有効化する場合、spec.listeners.tlsを設定します。証明書リソースは、optionsnetworking.gke.io/pre-shared-certsを追加します。
  • IPアドレスを固定したい場合は、spec.addressesにIPアドレスリソース名を追加します。

HTTPSではなく、HTTPでよい場合は公式ドキュメントのサンプルを参考にしてください。

GatewayリソースのYAMLファイルを作成したら、以下のコマンドで反映します。

kubectl apply -f gateway.yaml

この段階で、GatewayリソースによるCloud Load Balancingは作成されます。ただし、作成されるのはIPアドレスと証明書リソースを使ったフロントエンドと、k8sデフォルトバックエンド(404ページ)へのルートのみです。

HTTPRoute をデプロイ

HTTPRouteリソースをデプロイします。
ほぼGoogle公式リファレンスにある記載内容と同じですが、 spec.hostnamesの部分をCloud Endpointsで取得したドメインに変更しています。
また、 spec.parentRefsに指定するGatewayリソースの名称を external-https に変更しています。

store-route.yaml
store-route.yaml
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
  name: store
spec:
  parentRefs:
  - kind: Gateway
    name: external-https
  hostnames:
  - "{SERVICENAME}.endpoints.{PROJECT_ID}.cloud.goog"
  rules:
  - backendRefs:
    - name: store-v1
      port: 8080
  - matches:
    - headers:
      - name: env
        value: canary
    backendRefs:
    - name: store-v2
      port: 8080
  - matches:
    - path:
        value: /de
    backendRefs:
    - name: store-german
      port: 8080

作成したら、以下のコマンドを実行して反映します。

kubectl apply -f store-route.yaml

各リソースの作成状況について

リソースの作成状況は以下のコマンドで確認することができます。

Gatewayの作成状況

kubectl get gateway

※READY列がTrueならひとまず作成できたことが確認できる

kubectl describe gateway external-https

※StatusやEventsから作成状況を確認できる

HTTPRouteの作成状況

kubectl describe httproute store

※Eventsからなんとなく判断する

HTTPRouteリソースのEventsのメッセージはちょっと分かりにくいです。

Managed SSL証明書の作成状況確認

今回の作成方法ではManaged SSL証明書を使用しており、Gatewayが実際のCloud Load Balancingを作成した後、証明書リソースがIPアドレスとドメインの紐付けをチェックして証明書のセットアップが完了するまで時間がかかります。(公式には30分~1時間程度)
作成状況は以下のコマンドで確認できます。

gcloud compute ssl-certificates describe gke-gateway-cert --format="get(managed)"

※Status=ACTIVEになっていれば作成完了と判断できる

Gatewayへのアクセステスト

では、実際にアクセスしてみます。
curlコマンドでアクセス確認します。

store-v1 (デフォルトパス)へのアクセス

store-v1のcurlコマンド結果
$ curl https://{SERVICENAME}.endpoints.{PROJECT_ID}.cloud.goog
{
  "cluster_name": "gke-gateway-cluster",
  "host_header": "{SERVICENAME}.endpoints.{PROJECT_ID}.cloud.goog",
  "metadata": "store-v1",
  "pod_name": "store-v1-6d8d58d78-qlz96",
  "pod_name_emoji": "🎅🏽",
  "project_id": "{PROJECT_ID}",
  "timestamp": "2022-12-07T06:47:20",
  "zone": "asia-northeast1-a"
}

{SERVICENAME}{PROJECT_ID}の箇所は実際のサービス名やプロジェクトIDで表示されます。

store-v2 (Canaryリリース)へのアクセス

store-v2のcurlコマンド結果
$ curl https://{SERVICENAME}.endpoints.{PROJECT_ID}.cloud.goog -H "env: canary"
{
"cluster_name": "gke-gateway-cluster",
"host_header": "{SERVICENAME}.endpoints.{PROJECT_ID}.cloud.goog",
"metadata": "store-v2",
"pod_name": "store-v2-5d66fbc946-llclw",
"pod_name_emoji": "🦶🏼",
"project_id": "{PROJECT_ID}",
"timestamp": "2022-12-07T06:50:25",
"zone": "asia-northeast1-c"
}

{SERVICENAME}{PROJECT_ID}の箇所は実際のサービス名やプロジェクトIDで表示されます。

store-german (別パス)へのアクセス

store-germanのcurlコマンド結果
$ curl https://{SERVICENAME}.endpoints.{PROJECT_ID}.cloud.goog/de
{
  "cluster_name": "gke-gateway-cluster",
  "host_header": "{SERVICENAME}.endpoints.{PROJECT_ID}.cloud.goog",
  "metadata": "Gutentag!",
  "pod_name": "store-german-7dbb4b97cd-fr7hn",
  "pod_name_emoji": "🧗🏻‍♂️",
  "project_id": "{PROJECT_ID}",
  "timestamp": "2022-12-07T06:51:28",
  "zone": "asia-northeast1-a"
}

{SERVICENAME}{PROJECT_ID}の箇所は実際のサービス名やプロジェクトIDで表示されます。

HTTPRouteリソースで定義したパスやヘッダルーティングの通りにトラフィックが制御されたことが分かります。

検証用リソースのクリーンアップ

以下のコマンドを実行して、リソースをクリーンアップしておきましょう。

kubectl delete -f store-route.yaml
kubectl delete -f gateway.yaml
kubectl delete -f https://raw.githubusercontent.com/GoogleCloudPlatform/gke-networking-recipes/main/gateway/gke-gateway-controller/app/store.yaml

別 Namespace の Service へのルーティング

別の設定方法として、GatewayとHTTPRoute、Serviceを別のNamespaceで管理するケースで設定します。
Ingressリソースによる負荷分散設定では、IngressとServiceは全て同一のNamespaceで管理しなければならず、ルーティング先のサービス毎に別Namespace管理するような設定が困難でした。
Gateway APIは、Gateway,HTTPRoute,Serviceを別のNamespaceに配置する事が可能なため、非常に柔軟なリソース配置が可能になっています。

ここでは、GatewayとHTTPRouteは同じNamespaceで管理し、別NamespaceのServiceと連携するような構成を想定します。
この手順を始める前に、前セクションの「同じ Namespace の Service へのルーティング」で設定したGatewayとHTTPRouteは事前に削除しておきましょう。
また、扱うサンプルはGoogleのリファレンスで提供されているstoreデモアプリケーションのNamespaceを修正したものを流用します。

Namespace のセットアップ

今回デプロイするNamespaceは以下の3つにします。

Namespace名 デプロイするリソース ラベル
gateway Gateway 不要
store store-v1とstore-v2デモアプリ、及び、HTTPRoute shared-gateway-access: "true"
store-de store-germanデモアプリ、及び、HTTPRoute shared-gateway-access: "true"

実際に作成するYAMLファイルは以下の通りです。

namespace.yaml
namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: gateway
---
apiVersion: v1
kind: Namespace
metadata:
  name: store
  labels:
    shared-gateway-access: "true"
---
apiVersion: v1
kind: Namespace
metadata:
  name: store-german
  labels:
    shared-gateway-access: "true"

このとき、 store Namespace と store-german Namespace には必ず shared-gateway-access: "true" というラベルを付与します。
このラベルは後ほどGatewayリソース設定時に使用します。
以下のコマンドで反映します。

kubectl apply -f namespace.yaml

store デモアプリケーションのデプロイ

store デモアプリケーションをデプロイします。Googleリファレンスのstore.yamlを以下のように修正します。
基本的には、 各リソース定義に metadata.namespace を追加しただけです。

store.yaml
store.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: store-v1
  namespace: store
spec:
  replicas: 2
  selector:
    matchLabels:
      app: store
      version: v1
  template:
    metadata:
      labels:
        app: store
        version: v1
    spec:
      containers:
      - name: whereami
        image: us-docker.pkg.dev/google-samples/containers/gke/whereami:v1.2.11
        ports:
          - containerPort: 8080
        env:
        - name: METADATA
          value: "store-v1"
---
apiVersion: v1
kind: Service
metadata:
  name: store-v1
  namespace: store
spec:
  selector:
    app: store
    version: v1
  ports:
  - port: 8080
    targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: store-v2
  namespace: store
spec:
  replicas: 2
  selector:
    matchLabels:
      app: store
      version: v2
  template:
    metadata:
      labels:
        app: store
        version: v2
    spec:
      containers:
      - name: whereami
        image: us-docker.pkg.dev/google-samples/containers/gke/whereami:v1.2.11
        ports:
          - containerPort: 8080
        env:
        - name: METADATA
          value: "store-v2"
---
apiVersion: v1
kind: Service
metadata:
  name: store-v2
  namespace: store
spec:
  selector:
    app: store
    version: v2
  ports:
  - port: 8080
    targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: store-german
  namespace: store-german
spec:
  replicas: 2
  selector:
    matchLabels:
      app: store
      version: german
  template:
    metadata:
      labels:
        app: store
        version: german
    spec:
      containers:
      - name: whereami
        image: us-docker.pkg.dev/google-samples/containers/gke/whereami:v1.2.11
        ports:
          - containerPort: 8080
        env:
        - name: METADATA
          value: "Gutentag!"
---
apiVersion: v1
kind: Service
metadata:
  name: store-german
  namespace: store-german
spec:
  selector:
    app: store
    version: german
  ports:
  - port: 8080
    targetPort: 8080

以下のコマンドで反映します。

kubectl apply -f store.yaml

Gateway のデプロイ

Gatewayリソースをデプロイします。基本的な設定は「同じ Namespace の Service へのルーティング」と同じですが、2点異なる部分があります。
1点目は、external-httpsリソースを配置するmetadata.namespaceの指定、もう1点はlistenersの指定にallowRoutesを追加したことです。

gateway.yaml
gateway.yaml
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
  name: external-https
  namespace: gateway
spec:
  gatewayClassName: gke-l7-gxlb
  listeners:
  - name: https
    protocol: HTTPS
    port: 443
    tls:
      mode: Terminate
      options:
        networking.gke.io/pre-shared-certs: gke-gateway-cert
    allowedRoutes:
      namespaces:
        from: Selector
        selector:
          matchLabels:
            shared-gateway-access: "true"
  addresses:
  - type: NamedAddress
    value: gke-gateway-vip

allowRoutes はGatewayとHTTPRouteを別のNamespaceに配置する場合のどのNamespaceとの連携を許可するかの設定になります。
デフォルトは allowRoutes.namespaces.from: Sameであり、同一のNamespaceのみ許可する設定になっています。
今回の設定は、Selectorを指定し、 matchLabels によって特定のラベルを持つNamespaceに限定する設定しています。

以下のコマンドで反映します。

kubectl apply -f gateway.yaml

HTTPRoute のデプロイ

HTTPRouteをデプロイします。HTTPRouteはデモアプリケーションを管理しているNamespaceに寄せてデプロイする設定です。
下記の様に2つのHTTPRouteリソースを作成します。
store HTTPRouteリソースは store-v1store-v2 Deploymentを配置している store Namespaceに配置し、store-german HTTPRouteリソースは store-german Namespaceに配置します。
その他のルーティング条件については基本的に「同じ Namespace の Service へのルーティング」のときと同じにしています。

store-route.yaml
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
  name: store
  namespace: store
spec:
  parentRefs:
    - kind: Gateway
      name: external-https
      namespace: gateway
  hostnames:
    - "{SERVICENAME}.endpoints.{PROJECT_ID}.cloud.goog"
  rules:
    - backendRefs:
        - name: store-v1
          port: 8080
    - matches:
        - headers:
            - name: env
              value: canary
      backendRefs:
        - name: store-v2
          port: 8080
---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
  name: store-german
  namespace: store-german
spec:
  parentRefs:
    - kind: Gateway
      name: external-https
      namespace: gateway
  hostnames:
    - "{SERVICENAME}.endpoints.{PROJECT_ID}.cloud.goog"
  rules:
    - matches:
        - path:
            value: /de
      backendRefs:
        - name: store-german
          port: 8080

以下のコマンドで反映します。

kubectl apply -f store-route.yaml

Gatewayへのアクセステスト

しばらく待ってから、動作確認を行います。確認コマンドは「同じ Namespace の Service へのルーティング」と同じcurlコマンドです。

store-v1 (デフォルトパス)へのアクセス

store-v1のcurlコマンド結果
$ curl https://{SERVICENAME}.endpoints.{PROJECT_ID}.cloud.goog
{
  "cluster_name": "gke-gateway-cluster",
  "host_header": "{SERVICENAME}.endpoints.{PROJECT_ID}.cloud.goog",
  "metadata": "store-v1",
  "pod_name": "store-v1-6d8d58d78-ttzgl",
  "pod_name_emoji": "✍🏼",
  "project_id": "{PROJECT_ID}",
  "timestamp": "2022-12-08T08:03:32",
  "zone": "asia-northeast1-c"
}

{SERVICENAME}{PROJECT_ID}の箇所は実際のサービス名やプロジェクトIDで表示されます。

store-v2 (Canaryリリース)へのアクセス

store-v2のcurlコマンド結果
$ curl https://{SERVICENAME}.endpoints.{PROJECT_ID}.cloud.goog -H "env: canary"
{
  "cluster_name": "gke-gateway-cluster",
  "host_header": "{SERVICENAME}.endpoints.{PROJECT_ID}.cloud.goog",
  "metadata": "store-v2",
  "pod_name": "store-v2-5d66fbc946-z6lfh",
  "pod_name_emoji": "🏃🏾‍♂️",
  "project_id": "{PROJECT_ID}",
  "timestamp": "2022-12-08T08:03:43",
  "zone": "asia-northeast1-a"
}

{SERVICENAME}{PROJECT_ID}の箇所は実際のサービス名やプロジェクトIDで表示されます。

store-german (別パス)へのアクセス

store-germanのcurlコマンド結果
$ curl https://{SERVICENAME}.endpoints.{PROJECT_ID}.cloud.goog/de
{
  "cluster_name": "gke-gateway-cluster",
  "host_header": "{SERVICENAME}.endpoints.{PROJECT_ID}.cloud.goog",
  "metadata": "Gutentag!",
  "pod_name": "store-german-7dbb4b97cd-h9s26",
  "pod_name_emoji": "👨🏻‍🚀",
  "project_id": "{PROJECT_ID}",
  "timestamp": "2022-12-08T08:03:47",
  "zone": "asia-northeast1-c"
}

{SERVICENAME}{PROJECT_ID}の箇所は実際のサービス名やプロジェクトIDで表示されます。

Gateway controller の課題点

ここからは、Gateway API(GKE Gateway controller)を使ってみて感じた課題について述べていきたいと思います。

Google Cloud Load Balancing 機能のサポート

こちらの制限事項に記載されているとおり、Ingressリソースでサポートされていた下記の機能は、 Gateway controller ではサポートされていません。

  • Cloud CDN
  • Identity-Aware Proxy
  • Google Cloud Armor
  • SSL ポリシー
  • HTTP から HTTPS へのリダイレクト

また、それ以外にも幾つか制限事項が存在していますので、詳細は制限事項のページをご確認ください。
このあたりの機能は負荷分散としては必須の機能なので(特にCloud Armor, SSLポリシーあたり)、今後のGateway controllerの開発に期待です。

Gateway API リソース削除後の Google Cloud リソースが残ることがある

Ingress と同様ですが、検証等でクラスタを削除したい場合に、クラスタ削除前に Gateway API のリソース(GatewayHTTPRouteService)を削除しておかないと、実体であるCloud Load Balancingのリソースが残ってしまいます。
少々面倒ですが、k8s管理リソースを丁寧に削除してからクラスタを削除しましょう。
また、綺麗に消してからクラスタ削除しても、 Network Endpoint Group や Health Check といったリソースも残ってしまう場合があるようです。

エラーメッセージが分かりづらい

GatewayやHTTPRouteの設定に誤りがあったとき、APIスキーマとして設定箇所に誤りがある場合は比較的する分かるのですが、スキーマ自体は問題無いものの設定に誤りがある(もしくは何らか別の原因がある)場合のメッセージが非常に分かりにくいです。
以下は、検証している中でHTTPRouteリソースの設定が上手く反映されなかったケースで kubectl describe httproute コマンドで確認した際のメッセージです。

HTTPRoute作成失敗時のエラーメッセージ
Events:
Type    Reason  Age   From                   Message
  ----    ------  ----  ----                   -------
Normal  ADD     62s   sc-gateway-controller  store/store
Normal  SYNC    19s   sc-gateway-controller  Bind of HTTPRoute "store/store" to ParentRef {Group:       "gateway.networking.k8s.io",
Kind:        "Gateway",
Namespace:   "gateway",
Name:        "external-https",
SectionName: nil,
Port:        nil} was a success
Warning  SYNC  19s  sc-gateway-controller  ReconciliationFailed: Reconciliation of HTTPRoute "store/store" bound to ParentRef {Group:       "gateway.networking.k8s.io",
Kind:        "Gateway",
Namespace:   "gateway",
Name:        "external-https",
SectionName: nil,
Port:        nil} failed: error cause: gceSync: generic::not_found: 2 errors occurred: Update: The resource 'projects/******/global/backendServices/gkegw1-lofr-store-german-s
tore-german-8080-6z7zjb560but' was not found; Insert: The resource 'projects/******/zones/asia-northeast1-c/networkEndpointGroups/k8s1-ac924807-store-german-store-german-8080-
3f5a19b0' was not found

なんとなく、Gateway controller が Cloud Load Balancing のリソースであるバックエンドサービスとネットワークエンドポイントのリソースの作成に失敗しているのですが、これを見てどうすればよいか分からなかったです。
結局、このときはいったん GatewayとHTTPRouteのリソースを全部削除してから再作成することで対処できました。
こうしたエラーメッセージがもっと分かりやすくなってくれればなと思います。

Terraform Google Provider でサポートされていない

まだ Terraform Google Provider では Gateway API の有効化がサポートされていないようです。
下記のIssueで提案されているようですので、気になる方は 👍 を押しましょう。

https://github.com/hashicorp/terraform-provider-google/issues/13117

まとめ

GAになったとは言え、すぐ商用環境で使えるかというと微妙な部分がいくつかあるなという印象でした。
しかし、Ingressでは構成できなかった柔軟な設定が可能になっている点は将来的な可能性を感じます。
現在Ingressで負荷分散を使っている方は、まずはお試し頂くのがよいかなと思います。

Discussion