4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

KubernetesのGateway APIとは

Last updated at Posted at 2024-03-27

1711495961417.png
KubernetesのGateway APIが'23/10にGAとなり、v1.0としてリリースされた
ベンダーでもサポートの動きがあり、例えば'24/2にリリースされたVMwareのTanzu Kubernretes Gridのv2.5からGateway APIがサポートされるようになった
今後Gateway APIを使う場面も増えてくると思われるので、どういうリソースかを改めて整理してみる。
また、Mac上でK8sクラスタを構築し、サクっと動作確認してみる。

Gateway APIとは

Kubernetesクラスタ内のサービスを外部に公開する場合、一般的にはIngressが利用されると思う。
ただ、Ingressでは以下のような課題があった。

  • インフラ管理者、クラスタ管理者、アプリ開発者の間での責任分界点が不明瞭
  • L7ロードバランサのみ提供され、L4が足りていない
  • トラフィックの細かなルーティング管理がIngress Controller独自の拡張機能(annotation利用や独自リソースによる拡張)でしか提供されず、環境依存が発生する

Gateway APIではIngressの標準機能では提供出来ていなかった以下のような機能が提供される。

責任分界点の明確化

Gateway SIGの中で、どのリソースを誰が責任を持つかが明確化された。
1711497306787.png
(Gatway SIGのIntroductionより引用)

これにより、以下のような分担が標準ロールとして定義された。

ロール 役割
インフラ管理者 Gatewayリソースを管理するためのGatewayClass(IngressClassみたいなもので、コントローラを指定)を提供・管理
クラスタ管理者 GatewayClassに紐づくロードバランサ(Gatewayリソース)を提供・管理
アプリケーション開発者 ロードバランサとアプリケーションを紐づけるためのルーティング(HTTPRouteTLSRouteなど)を提供・管理

様々な種類のルーティングの提供

IngressではL7のみだったが、Gateway APIでは以下のルーティング用のリソースが提供される。

  • HTTPRoute
  • TLSRoute
  • GRPCRoute
  • TCPRoute
  • UDPRoute

なお、'24/3時点ではHTTPRoute以外は正式GAされていなかったと思うので、利用する際にサポート等が必要な場合は要確認である。
(例えばTKG2.5だとHTTPRouteのみサポート

細かなルーティング制御の実現

今まではIngress内のannotationsで設定したり、Servie Mesh製品を入れて実現していたような細かなルーティングが設定可能である。
詳細はHTTPRouteRulesを見ると分かるが、ヘッダーでフィルタリングしたり、カナリアリリースやタイムアウトをパスや条件ごとに個別に設定することが出来るようになった。

以下はカナリアリリースの例であり、spec.rules[].backendRefs[].weightで重み付けを行っている事が分かる。

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: foo-route
  labels:
    gateway: prod-web-gw
spec:
  hostnames:
  - foo.example.com
  rules:
  - backendRefs:
    - name: foo-v1
      port: 8080
      weight: 90
    - name: foo-v2
      port: 8080
      weight: 10

Gateway APIが提供するリソース

APIの種類については公式リファレンスで確認できるが、ここでは代表的な3つのリソース(GatewayClassGatewayHTTPRoute)を掘り下げる。

GatewayClass

GatewayClassIngressClassと同様にGatewayリソースを作るための型みたいなもので、基本的にはソフトウェアの中に梱包されていて利用者がManifestを編集することはほとんどないと思われる。
クラスタリソースであり、全Namespaceで利用できる。
Manifestの例は以下となる。

kind: GatewayClass
metadata:
  name: cluster-gateway
spec:
  controllerName: "example.net/gateway-controller"

Gateway Controllerによってはspec.paramtersRefなどを使ってパラメータをコントローラに渡すこともあるが、利用者はこのリソースはあまり意識しないので書き方は知らなくても問題ないかと思われる。

Gateway

クラスタ管理者はどういうパケットをクラスタ内に流すのを許可するか、をGatewayリソースを使って設定・管理する。
Ingressでいうとエンドポイントのプロトコルやポートを管理するようなリソースで、AWSのSecurityGroupの設定に近いかもしれない。
以下は実際のGatewayリソースの例である。

spec:
  gatewayClassName: kong
  listeners:
  - allowedRoutes:
      namespaces:
        from: Same
    name: http
    port: 80
    protocol: HTTP
  - name: proxy-ssl
    port: 443
    protocol: HTTPS
  - name: proxy-tcp-9901
    port: 9901
    protocol: TCP
  - name: proxy-udp-9902
    port: 9902
    protocol: UDP

gatewayClassNameingressClassNameのGateway版でGatewayClassを指定する。
lisntersでホスト名、ポート、TLS設定等を定義する。
また例には載せていないが、spec.addressesGatewayがListenするアドレスも指定する事が出来る。
詳細な仕様はこちら

HTTPRoute

HTTPRouteではルーティング時の振る舞いを定義する事が出来る。
ここでパスごとの重み付け(B/Gやカナリアリリースで使用)、タイムアウト、ヘッダの書き換えなど柔軟なリクエスト処理が行えるようになる。

タイムアウトの例

以下ではtimeouts.requestでクライアントに対する応答時間のタイムアウト値を設定し、timeouts.backendRequestでGatewayからバックエンドへのタイムアウト値を設定している。

spec:
  parentRefs:
  - name: example-gateway
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /timeout
    timeouts:
      request: 10s
      backendRequest: 2s
    backendRefs:
    - name: timeout-svc
      port: 8080
ヘッダ書き換えの例

以下ではリクエストヘッダにmy-header: fooを追加している。

spec:
  hostnames:
    - my.filter.com
  rules:
    - filters:
        - type: RequestHeaderModifier
          requestHeaderModifier:
            add:
              - name: my-header
                value: foo

Gateway API利用時のサービスへのアクセスの流れ

クライアントからPodが提供するサービスへアクセスするまでの流れは以下となる。
image.png
Request flowより引用)

  1. クライアントがURLにリクエストを投げる
  2. DNSリゾルバがURLに対応するGatewayに割り当てられたIPを引っ張り出す
  3. クライアントはGatewayのIPにリクエストを送信する("Host:"ヘッダでURLアクセス名も渡す)
  4. Gateway側で対応するHTTPRouteを確認し、HTTPRouteの処理に移る
  5. HTTPRouteServiceに転送する。この際ヘッダによるフィルタリングやヘッダの加工などリクエストの修正をしたり、流すパスを変えたりする事が出来る。
  6. Service経由でPodが提供するサービスに到達する

ローカルPCでGateway APIを利用する

ここではMacにMicroK8sでKubernetes環境を作成し、そこにGateway APIを入れて動作確認する。

Kubernetes環境の構築

microk8sのインストール手順に従って構築・起動する。

brew install ubuntu/microk8s/microk8s
microk8s install

multipassをセットアップするか?みたいな質問が出るのでyを押して併せてセットアップする。
また途中でsudoで動くためにパスワードを聞かれるので、それも素直に入力する。
インストールが終わったらMetalLBを使ってtype: LoadBalancerが使えるようにする。
multipassが使っているネットワークをNodeのIPから確認する。

microk8s kubectl get node microk8s-vm -o jsonpath={.status.addresses[0].address}

ここでは以下の結果となった。

192.168.64.3

なので、192.168.64.xxxであれば使えそうなので、ここをMetalLBが割り当てるレンジとしてMetalLBをデプロイする。

microk8s enable metallb:192.168.64.201-192.168.64.210

動作確認する。

microk8s kubectl run nginx --image nginx --port 80
microk8s kubectl expose pod nginx --port 80 --type LoadBalancer
NGINX_IP=$(microk8s kubectl get svc nginx -o jsonpath={.status.loadBalancer.ingress[0].ip})
curl $NGINX_IP

curlで以下が取得できればOK。

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
:(省略)

また、これ以降の作業はMicroK8s以外のKubernetes環境と手順を共通化させるため、microk8sコマンドにエイリアスを貼ってkubectlhelmmicrok8sを意識せずに叩けるようにする。

alias 'kubectl=microk8s kubectl'
alias 'helm=microk8s helm'

Gateway APIの導入:インフラ管理者作業

Gateway APIを利用するためにはGateway Controllerが必要となる。
これはKubernetes標準では提供されておらず、各種ベンダから提供されている。
Gateway Controllerの一覧はこちら
どれを使ってもそれほど手順は変わらないと思うが、ここではどこのインフラでも使えそうなKong Ingress Controllerで試す。
最初にGateway APIで提供されるリソース定義をインストールする。

kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/standard-install.yaml

導入すると以下のリソースが見えるようになる。

$ kubectl api-resources |grep gateway
gatewayclasses                    gc           gateway.networking.k8s.io/v1           false        GatewayClass
gateways                          gtw          gateway.networking.k8s.io/v1           true         Gateway
httproutes                                     gateway.networking.k8s.io/v1           true         HTTPRoute
referencegrants                   refgrant     gateway.networking.k8s.io/v1beta1      true         ReferenceGrant

次にGateway Controllerを導入する。

helm repo add kong https://charts.konghq.com
helm repo update
helm install kong kong/ingress -n kong --create-namespace 

しばらくすると、以下のような感じでGateway Controllerを含む各種コンポーネントが立ち上がる。

$ kubectl get all -n kong
NAME                                   READY   STATUS    RESTARTS   AGE
pod/kong-controller-55b777f995-b7zsg   1/1     Running   0          37s
pod/kong-gateway-868d48556c-m9vdf      1/1     Running   0          37s

NAME                                         TYPE           CLUSTER-IP       EXTERNAL-IP      PORT(S)                         AGE
service/kong-gateway-admin                   ClusterIP      None             <none>           8444/TCP                        38s
service/kong-controller-validation-webhook   ClusterIP      10.152.183.107   <none>           443/TCP                         38s
service/kong-gateway-proxy                   LoadBalancer   10.152.183.96    192.168.64.202   80:31493/TCP,443:32243/TCP      38s
service/kong-gateway-manager                 NodePort       10.152.183.34    <none>           8002:32171/TCP,8445:32729/TCP   38s

NAME                              READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/kong-controller   1/1     1            1           38s
deployment.apps/kong-gateway      1/1     1            1           38s

NAME                                         DESIRED   CURRENT   READY   AGE
replicaset.apps/kong-controller-55b777f995   1         1         1       37s
replicaset.apps/kong-gateway-868d48556c      1         1         1       37s

GatewayClassを作成する。公式サイトの記述をそのまま適用する。

cat <<EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: kong
  annotations:
    konghq.com/gatewayclass-unmanaged: 'true'

spec:
  controllerName: konghq.com/kic-gateway-controller
EOF

なお、annotationの記述の意味はこちらによるとGatewayリソースをコントローラに監視させるためのKong Ingress Controller固有の設定となるため、他のGateway Controllerだと変わる。
他のGatway Controllerを使う場合、基本的にGatewayClassは各種ベンダから作成方法の指示があるので、そちらの指示に従って作成する。
以上でインフラ管理者作業は終了となり、クラスタ管理者に引き渡す。

クラスタへのインバウンドルーティングの設定(Gatewayリソースの作成):クラスタ管理者作業

ここではクラスタ管理者として、HTTPのみクラスタ内に流すことを許可すると想定して、以下のGatewayリソースを作成する。

kubectl create ns demo
cat <<EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: gateway
  namespace: demo
spec:
  gatewayClassName: kong
  listeners:
  - name: http
    port: 80
    protocol: HTTP
EOF

作成するとADDRESSにMetalLBによって払い出されたIPが表示される。

$ kubectl get gateway  -n demo
NAME      CLASS   ADDRESS          PROGRAMMED   AGE
gateway   kong    192.168.64.202   True         18m

このIPを使ってアプリケーション開発者はアプリケーションとGatewayをつなぐ。

アプリケーションへのルーティングの設定(HTTPRouteリソースの作成):アプリケーション開発者作業

ここではサンプルとして、クラスタ内のnginx Podとクラスタ外のhttpbin.orgService経由でアクセス出来るようにし、HTTPRouteを使ってそれらに分散してアクセスできるかを試す。
最初にサンプルとなるnginxのPodを立て、Serviceを作成する。

kubectl run nginx --image nginx --port 80 -n demo
kubectl expose pod nginx --port 80 -n demo

次に外部サービスにつなぐためのExternalNameなServiceを作成する。

kubectl create svc externalname httpbin --external-name httpbin.org -n demo

サービスにアクセスするためのホスト名を定義する。DNSサーバがある人は先程のGatewayのアドレスを向いたドメインを用意する。
ここではDNSサーバがなかったので、フリーのワイルドカードDNSサービスのnip.ioを使って代用する。

PROXY_IP=$(kubectl get gateway gateway -n demo -o jsonpath={.status.addresses[].value})
export HOSTNAME="my-service.$(sed "s/\./-/g" <<< $PROXY_IP).nip.io"

${HOSTNAME}/serviceにアクセスすると、先ほどの2つのサービスに分散するようなHTTPRouteを作成する。

cat <<EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: route
  namespace: demo
  annotations:
    konghq.com/strip-path: 'true'
spec:
  hostnames: 
  - $HOSTNAME
  parentRefs:
  - name: gateway
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /service
    backendRefs:
    - name: nginx
      kind: Service
      port: 80
      weight: 50
    - name: httpbin
      kind: Service
      port: 80
      weight: 50
EOF

ちなみにExternalNameではポート番号は指定しないが、HTTPRoute作成時は必須となっているのでとりあえず80を入れておく。
問題なければ以下のようにHTTPRouteが作成される。

$ kubectl get httproute -n demo
NAME    HOSTNAMES                              AGE
route   ["my-service.192-168-64-202.nip.io"]   8m4s

アクセスしてみる。

curl -s my-service.192-168-64-202.nip.io/service

何度か実行すると、nginxとhttpbin.orgのそれぞれにアクセスする。
これにより、weightが適切に働き分散していることが確認できる。

所感

自分の経験としてIngressやServiceMesh製品を使ってルーティングの設定をする場合、製品や環境個々の設定方法を知る必要があり、環境が変わるたびにキャッチアップするのが大変だった。
Gateway APIがあればこの辺の環境差異が容易に吸収できそうなので、Gateway APIが普及すれば少し楽が出来そうな気がする。
また、ServiceMesh製品を入れないと難しかった機能も提供されており、構成がシンプルかつサポート利用時の問い合わせルートを簡素化出来るのもありがたい。
その他機能以外にはなるが、責任分界点をコミュニティ側で用意してくれたのもいい感じだと思う。
Kubernetes環境では責任分界点で揉めることも多いので、揉めそうな時はデファクトとして提示できればと思う。

参考文献

4
3
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
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?