KubeCon + CloudNativeCon Europe 2021 の [Gateway API: A New Set of Kubernetes APIs for Advanced Traffic Routing](Gateway API: A New Set of Kubernetes APIs for Advanced Traffic Routing) というセッションで powerful version of Ingress
として紹介されていた Gateway API について、調査した内容をまとめてみました。
※ 2021年5月時点では v1alpha1 バージョンの API であり、今後のリリースにおいて変更が加えられることが想定されます。以降の記載内容についても変更が入る可能性がありますので、その点ご留意頂ければ幸いです。
目次
Gateway API とは
Gateway API とは、外部からのアクセスやトラフィックを管理するための API やリソース (後述の GatewayClass、Gateway、HTTPRoute、TCPRoute など) の総称であり、SIG-Network コミュニティのプロジェクトとして開発が進められています。
Gateway API の実装は、Ingress と同じように Controller (Gateway Controller) 側で実装する必要があり、現在 Contour、Traefik、GKE Gateway Controller など様々なプロジェクトで開発が進められています。それぞれの実装のステータスは 公式ドキュメント - Implementations にまとめられています。
従って、実際にサポートされる機能は使用する Gateway Controller にもよりますが、Gateway API として定義されている機能としては以下のようなものがあります。
- サービスの外部公開
- SSL/TLS 終端
- トラフィックルーティング
- パスベースのトラフィックルーティング
- ヘッダー情報に基づくトラフィックルーティング
- 割合ベースのトラフィック分割
- トラフィックミラーリング
- リクエスト・レスポンスヘッダー情報の更新
- 複数の Namespace を対象としたルート選択
- 複数の Kubernetes クラスタを対象としたルート選択 など
現時点では提供されていませんが、Rate Limiting (issue#326) や認証機能 (issue#114) など、今後も新しい API・機能が追加されていくと予想されます。
以下の図は、Route リソースとして HTTPRoute を使用した場合の Gateway API の利用イメージです。各リソースの詳細については後ほど説明しますが、クライアントから Gateway 宛てに送信されたリクエストが、HTTPRoute に設定したルール (Routing rule) に従って Service へとルーティングされます。
Gateway API 開発の経緯
外部からのアクセス管理や、シンプルなトラフィックルーティングの機能を提供するリソースとしては既に Ingress がありますが、なぜ新たに Gateway API が開発されることになったのでしょうか。主に、以下の課題感をもとに開発が始まったようです。
- Ingress 自体の機能では、Gateway API とは で紹介したような機能が必要となる高度なユースケースに対応していなかった
- Ingress の機能拡張のために、各種 Ingress controller 等で多数のカスタムアノテーションや CRD が実装されることにより、管理の複雑化やポータビリティ低下が発生していた
- カスタムアノテーションの場合、シンプルな表現しかできず機能も制限される
これらの課題を解決するための新しい機能として、Gateway API (当時の名称は Service APIs) についての議論が KubeCon + CloudNativeCon North America 2019 San Diego で行われ、その後開発が進み、無事 2020年11月 に v1alpha1 バージョンがリリースされました。
なお、Ingress を置き換えることを目的に開発されているわけではなく、ユースケースに応じて Ingress と Gateway API を使い分けることを想定しており、現時点では Ingress の非推奨化や削除の予定はない
そうです。
リソースモデル
Gateway API の概要を理解したところで、リソースモデル のうち主要な部分について見ていきたいと思います。
-
GatewayClass
- Cluster Scoped のリソース
- 提供する Gateway (ロードバランサ) をテンプレート化したもの
- マネージド kubernetes サービスの場合はクラウドベンダが提供
-
Gateway
- Namespace Scoped のリソース
※ Cross-Namespace をサポートしており、異なる Namespace のサービスに紐づく Gateway も提供できる - 指定した GatewayClass をもとに作成されるロードバランサリソース
- Gateway (ロードバランサ) がトラフィックを受け付ける方法・ポート番号や、Route と紐づけるための条件を指定
- Namespace Scoped のリソース
-
xRoute
- Namespace Scoped のリソース
- Service にリクエストをルーティングするためのプロトコル固有のルール (例. HTTPRoute, TCPRoute)
- トラフィックルーティングの条件を指定 (パスやヘッダ情報、トラフィック分割の割合など)
-
Service
- 一般的な Kubernetes Service リソース
補足: 以下の図の通り、Gateway API はロール指向のデザインに基づいており、各リソースは組織のロールに対応するようにして用意されています。
動作確認
それでは、実際に Gateway API を使用してみたいと思います。動作環境としては Google Kubernetes Engine (GKE) を用いて、各種 Gateway API リソースのデプロイと、Gateway のデプロイ をベースにいくつかの基本機能を確認します。
1. GKE 環境準備
GKE Gateway コントローラの要件 を満たすような GKE クラスタを準備します。今回は、以下のような検証環境を作成しました。
※ GKE の Gateway API は現在プレビュー機能です
- 検証環境
- プラットフォーム: Google Kubernetes Engine
- バージョン: v1.20.6-gke.1000
- リージョン: us-east1
- VPC ネイティブ(エイリアス IP)クラスタ
- alpha 機能を有効化
※ alpha 機能を有効化する場合、--no-enable-autoupgrade
,--no-enable-autorepair
のオプション付与が必要
$ gcloud container clusters create gke1 \
--cluster-version 1.20.6-gke.1000 \
--zone us-east1-b \
--enable-kubernetes-alpha \
--no-enable-autoupgrade \
--no-enable-autorepair \
--enable-ip-alias
本投稿では 内部 GatewayClass
を用いた動作確認を行うため、プロキシ専用サブネット を作成します。
$ gcloud compute networks subnets create proxy-only-subnet \
--purpose=INTERNAL_HTTPS_LOAD_BALANCER \
--role=ACTIVE \
--region=us-east1 \
--network=default \
--range=10.10.0.0/20
2. GatewayClass リソースの存在確認
前述の GKE Gateway コントローラの要件 を満たしていれば、GKE Gateway コントローラ によって自動的に GatewayClass がデプロイされていると思います (クラスタ作成後から1, 2分かかることがあります)。
$ kubectl get gatewayclass
NAME CONTROLLER
gke-l7-gxlb networking.gke.io/gateway
gke-l7-rilb networking.gke.io/gateway
上記のうち、gke-l7-gxlb
が GCP 上で提供されるグローバル外部 HTTP(S) ロードバランサ、gke-l7-rilb
がリージョン内部 HTTP(S) ロードバランサです。
3. Gateway リソースのデプロイ
どちらを利用するにしてもそれほど手順は変わらないですが、よりシンプルな gke-l7-rilb
(内部 GatewayClass) を用いて動作確認を実施していきます。
以下のようなマニフェストを作成し、Gateway リソースをデプロイします。
$ cat gateway.yaml
kind: Gateway
apiVersion: networking.x-k8s.io/v1alpha1
metadata:
name: internal-http
spec:
gatewayClassName: gke-l7-rilb # specifies the GatewayClass
listeners:
- protocol: HTTP
port: 80
routes:
kind: HTTPRoute
selector:
matchLabels:
gateway: internal-http
$ kubectl apply -f gateway.yaml
gateway.networking.x-k8s.io/internal-http created
現状、Gateway によって作成されたロードバランサリソースは Google Cloud Console から確認不可ですが (プレビューの制限と既知の問題)、Gateway をデプロイしたことで、GCP 上にリージョン内部 HTTP(S) ロードバランサが作成されているはずです。
下記コマンドの実行結果に、内部 IP アドレスと SYNC on default/internal-http was a success
のようなメッセージが出力されていればデプロイ成功です。
補足: SYNC が完了する数分前に BackendService が Ready でない旨の Warning が出力されているのが気になりましたが、全てのリソースがデプロイされるまで数分かかる場合があると Gateway のデプロイ に書いてあるため、その範疇だと思われます。
$ kubectl describe gateway internal-http
...
Status:
Addresses:
Type: IPAddress
Value: 10.142.0.5
Conditions:
Last Transition Time: 1970-01-01T00:00:00Z
Message: Waiting for controller
Reason: NotReconciled
Status: False
Type: Scheduled
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ADD 5m34s regional-gke-gateway-ctlr default/internal-http
Warning SYNC 5m11s regional-gke-gateway-ctlr generic::invalid_argument: error ensuring load balancer: Insert: The resource 'projects/<project_id>/regions/us-east1/backendServices/gk3-gw-bf65-default-gw-serve404-80-mcfti8ucx6x5' is not ready
Normal UPDATE 102s (x3 over 5m34s) regional-gke-gateway-ctlr default/internal-http
Normal SYNC 102s regional-gke-gateway-ctlr SYNC on default/internal-http was a success
4. デモアプリケーションのデプロイ
デモ用に提供されている store アプリケーション (store-v1、store-v2、store-german) をデプロイします。
$ kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/gke-networking-recipes/master/gateway/gke-gateway-controller/app/store.yaml
$ kubectl get deploy,svc
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/store-german 2/2 2 2 4m52s
deployment.apps/store-v1 2/2 2 2 4m59s
deployment.apps/store-v2 2/2 2 2 4m55s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/store-german ClusterIP 10.100.4.137 <none> 8080/TCP 4m52s
service/store-v1 ClusterIP 10.100.11.236 <none> 8080/TCP 4m57s
service/store-v2 ClusterIP 10.100.11.173 <none> 8080/TCP 4m54s
5. HTTPRoute リソースのデプロイ
internal-http
Gateway が受信したリクエストを各 Service に転送するためのルールを設定した HTTPRoute リソースをデプロイします。
internal-http
Gateway の matchLabels に設定した gateway: internal-http
ラベルを付与することで、Gateway リソースとの紐付けを行っています。
$ cat store-route.yaml
kind: HTTPRoute
apiVersion: networking.x-k8s.io/v1alpha1
metadata:
name: store
labels:
gateway: internal-http
spec:
hostnames:
- "store.example.com"
rules:
- forwardTo:
- serviceName: store-v1
port: 8080
- matches:
- headers:
type: Exact
values:
env: canary
forwardTo:
- serviceName: store-v2
port: 8080
- matches:
- path:
type: Prefix
value: /de
forwardTo:
- serviceName: store-german
port: 8080
$ kubectl apply -f store-route.yaml
httproute.networking.x-k8s.io/store created
6. アクセス確認
これで、必要な Gateway API リソースのデプロイが完了しました。早速、internal-http
に設定したルール (Routing rule) に従って、適切な Service にトラフィックルーティングされるかを確認したいと思います。
今回は gke-l7-rilb (内部 GatewayClass) を用いて Gateway をデプロイしたため、クラスタ内部に作成した Pod から curl
コマンドでアクセス確認をしたいと思います。
$ kubectl run -it --rm alpine --image=alpine --generator=run-pod/v1 -- sh
If you don’t see a command prompt, try pressing enter.
/ # apk add curl
/ # curl -h
Usage: curl [options...] <url>
...
まずは、何も指定しない (env: canary
HTTP ヘッダーも、/de
パス指定もしない) 場合の結果からです。設定したルールの通りに、store-v1
にルーティングされています。
/ # curl -H "host: store.example.com" 10.142.0.5
{
"cluster_name": "gke1",
"host_header": "store.example.com",
"metadata": "store-v1",
"node_name": "gke-gke1-default-pool-beb2c84d-m3l1.us-east1-b.c.<project_id>.internal",
"pod_name": "store-v1-65b47557df-z5ls2",
"pod_name_emoji": "🧕🏻",
"project_id": "<project_id>",
"timestamp": "2021-05-21T09:21:24",
"zone": "us-east1-b"
}
次に、env: canary
HTTP ヘッダーを指定した場合の結果です。こちらも設定した通り、store-v2
にルーティングされています。
/ # curl -H "host: store.example.com" -H "env: canary " 10.142.0.5
{
"cluster_name": "gke1",
"host_header": "store.example.com",
"metadata": "store-v2",
"node_name": "gke-gke1-default-pool-beb2c84d-fvs6.us-east1-b.c.<project_id>.internal",
"pod_name": "store-v2-6856f59f7f-5wmc5",
"pod_name_emoji": "🗯️",
"project_id": "<project_id>",
"timestamp": "2021-05-21T09:32:31",
"zone": "us-east1-b"
最後に、/de
パス指定した場合の結果です。設定した通り、store-german
にルーティングされています。いい感じですね!
/ # curl -H "host: store.example.com" 10.142.0.5/de
{
"cluster_name": "gke1",
"host_header": "store.example.com",
"metadata": "Gutentag!",
"node_name": "gke-gke1-default-pool-beb2c84d-fvs6.us-east1-b.c.<project_id>.internal",
"pod_name": "store-german-66dcb75977-ljqr8",
"pod_name_emoji": "🌝",
"project_id": "<project_id>",
"timestamp": "2021-05-21T09:33:29",
"zone": "us-east1-b"
}
以上、基本機能のみではありますが、GKE 上で各種 Gateway API リソースをデプロイして、設定したルール (Routing rule) に従ってトラフィックルーティングされることを確認することができました。
今回は gke-l7-rilb (内部 GatewayClass) を使用しましたが、gke-l7-gxlb (外部 GatewayClass) を用いたデプロイ方法についても Gateway のデプロイ で紹介されており、手順通りに外部公開用の Gateway を作成することができたので、興味のある方は見て頂けたらと思います。
また、Gateway API の公式ドキュメント をみると、その他の機能についても詳細な説明がありとても参考になります。現時点では Gateway Controller によって動作する機能に制限はあるかもしれませんが、例えば以下のように割合ベースのトラフィック分割の設定をして、カナリアリリースができるかどうかなど、色々試してみるのも面白そうです (GKE 上では以下の設定で動作しました)。
kind: HTTPRoute
... <OMITTED>
- forwardTo:
- serviceName: store-v1
port: 8080
weight: 7
- serviceName: store-v2
port: 8080
weight: 3
今後のロードマップ
現時点では v1alpha1 バージョンの機能ですが、以下のような目標感で活発に開発が行われているようです。
- 近いうちに v1alpha2 リリース予定
- 可能であれば今年中に v1beta1 をリリースしたい
Gateway Controller の実装 (GKE Gateway Controller, Contour, Traefik など) も現在開発中とのことでしたので、今後の発展に期待したいと思います。
おわりに
以上、Gateway API の概要や開発経緯、GKE 上で実施した動作確認のまとめでした。
本投稿の内容について、ご指摘やアドバイス・質問等がありましたらぜひコメント欄に記入いただけたらと思います。