Private AI周りでAPIを触ってて、APIをキャッシュして発行回数減らせないかな、と思ってたところ、Kong Gatewayがそれっぽい機能を持っていたのでKongに入門してみた。
せっかくなので、ここではProxy Cacheだけではなく、Get Startedにある
- ServiceとRoute
- レート制限
- 鍵認証
- ロードバランシング
も合わせて確認する。
ただ、Get Startedに沿って入門したいのだが、Get StartedはDocker前提で書かれていてHelmベースでKongを導入した人にはなぞりにくい内容となっている。
ここではHelmベースに置き換えたらどうなるかと、せっかくのUIも活用したいのでなるべくCLIを使わずに設定できるか、の観点でGet Startedの内容を少しアレンジして検証する。
インストール
Get Startedを始めるにあたり、Kong Gatewayを用意する。
Helmのインストール方法を参照してインストールする。
前提
ここでは以下の環境があるものとして進める
- Kubernetesクラスタ(ここではTanzu Kubernetes Gridを利用)
- Kubernetesクラスタは
type: LoadBalancer
が利用可能 - 作業端末にkubectl, helmコマンドがインストール済み
また、今回検証で使ったKong Gatewayのバージョンは3.5である。
前準備
Ingressを使うので、Ingress Controllerを入れておく。
KongはIngress Controllerも配布しているので、これを使ってみる。
なお、Kong GatewayのHelm ChartにもIngress Controllerの項目があるのでそちらからインストールすることも出来そうだが、ここでは分けて入れた。
最初にCRDをインストールする。
配布されているものが標準的なGateway
リソースのCRDと、ExperimentalでTCPRoute
とかも含むCRDの2種類用意されている。
せっかくなのでExperimental版をインストールする。
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/experimental-install.yaml
Ingress Controllerをインストールする。
helm install kong-ingress --namespace kong --create-namespace --repo https://charts.konghq.com ingress
問題なければこんな感じでオブジェクトが作成される。
$ kubectl get all -n kong
NAME READY STATUS RESTARTS AGE
pod/kong-ingress-controller-6f8bf5f6bc-g9bql 1/1 Running 0 32s
pod/kong-ingress-gateway-7778fbf68f-dhqm5 1/1 Running 0 32s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kong-ingress-controller-validation-webhook ClusterIP 10.100.137.100 <none> 443/TCP 34s
service/kong-ingress-gateway-admin ClusterIP None <none> 8444/TCP 34s
service/kong-ingress-gateway-manager NodePort 10.106.59.205 <none> 8002:31176/TCP,8445:31808/TCP 34s
service/kong-ingress-gateway-proxy LoadBalancer 10.102.184.40 10.220.35.70 80:31070/TCP,443:30384/TCP 34s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/kong-ingress-controller 1/1 1 1 33s
deployment.apps/kong-ingress-gateway 1/1 1 1 33s
NAME DESIRED CURRENT READY AGE
replicaset.apps/kong-ingress-controller-6f8bf5f6bc 1 1 1 33s
replicaset.apps/kong-ingress-gateway-7778fbf68f 1 1 1 33s
次にKong GatewayのHelmのvalues.yamlを取ってくる。(公式手順にあわせて名前はquickstart.yamlにしている)
curl -o quickstart.yaml -L https://bit.ly/KongGatewayHelmValuesAIO
次に自身のドメインの設定を行うが、ここは公式手順と異なりフリーのワイルドカードDNSサービスであるnip.ioを利用するため、sedの処理を以下のように変える。
export BASE_DOMAIN=$(sed "s/\./-/g" <<< $(kubectl get svc -n kong kong-ingress-gateway-proxy -o jsonpath={.status.loadBalancer.ingress[0].ip}))
sed -i "s/127-0-0-1\.nip\.io/${BASE_DOMAIN}.nip.io/g" quickstart.yaml
また、他に以下の変更を加えた。
-
admin.ingress
にingressClassName: kong
を追記 -
ingressController.enabled
をfalse
に変更
初期パスワードやPostgresの接続先などを定義したSecretを作成する。Ingress ControllerをKongのものにしていない場合はNamespace(ここではkong)を作成すること。
kubectl create secret generic kong-config-secret -n kong \
--from-literal=portal_session_conf='{"storage":"kong","secret":"super_secret_salt_string","cookie_name":"portal_session","cookie_same_site":"Lax","cookie_secure":false}' \
--from-literal=admin_gui_session_conf='{"storage":"kong","secret":"super_secret_salt_string","cookie_name":"admin_session","cookie_same_site":"Lax","cookie_secure":false}' \
--from-literal=pg_host="enterprise-postgresql.kong.svc.cluster.local" \
--from-literal=kong_admin_password=kong \
--from-literal=password=kong
LicenseのSecretを作成する。ライセンスが無い場合は--from-literal
でlicense="'{}'"
のように空を渡す。
kubectl create secret generic kong-enterprise-license --from-literal=license="'{}'" -n kong --dry-run=client -o yaml | kubectl apply -f -
cert-managerをインストールする。
helm repo add jetstack https://charts.jetstack.io ; helm repo update
helm upgrade --install cert-manager jetstack/cert-manager \
--set installCRDs=true --namespace cert-manager --create-namespace
cert-managerを使ってオレオレ証明書を作成する。証明書を持ってる人は自分のものに差し替えることも可能。
bash -c "cat <<EOF | kubectl apply -n kong -f -
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: quickstart-kong-selfsigned-issuer-root
spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: quickstart-kong-selfsigned-issuer-ca
spec:
commonName: quickstart-kong-selfsigned-issuer-ca
duration: 2160h0m0s
isCA: true
issuerRef:
group: cert-manager.io
kind: Issuer
name: quickstart-kong-selfsigned-issuer-root
privateKey:
algorithm: ECDSA
size: 256
renewBefore: 360h0m0s
secretName: quickstart-kong-selfsigned-issuer-ca
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: quickstart-kong-selfsigned-issuer
spec:
ca:
secretName: quickstart-kong-selfsigned-issuer-ca
EOF"
Kong Gatewayのデプロイ
Kongのリポジトリを追加する。
helm repo add kong https://charts.konghq.com ; helm repo update
Helm Chartをデプロイする。公式手順に従ってリリース名はquickstart
にしている。
helm install quickstart kong/kong --namespace kong --values quickstart.yaml
デプロイが終わって、"https://kong.${BASE_DOMAIN}.nip.io"をブラウザで開くとUI画面が確認できる。
ちなみにNew Workspace
のボタンが活性化されていないのはFree ModeだとWorkspaceを新規作成できないためである。
基本的にはここからの作業はこのdefault
のworkspaceを使っていく。
ServiceとRoute
Kongではトラフィック管理にServiceとRouteと呼ばれるオブジェクトを用いている。
Serviceはアプリケーションが提供するサービスを抽象化したもので、プロトコルやホスト、パスなどを含む。
Service Objectの例:
{
"id": "9748f662-7711-4a90-8186-dc02f10eb0f5",
"created_at": 1422386534,
"updated_at": 1422386534,
"name": "my-service",
"retries": 5,
"protocol": "http",
"host": "example.com",
"port": 80,
"path": "/some_api",
:(省略)
Routeはアプリケーション内のリソースへのパスで、クライアントからのリクエストにマッチするルールを定義する。
(引用元:Services and Routes)
RouteはServiceと関連付けられ、Serviceは複数のRouteを持つことが出来る。これにより、異なるパスへの異なるリクエストを単一のAPIで処理することも出来る。
ここではGet Startedの一部であるManaging services and routesの内容をKong Admin UIから試してみる。
Serviceの作成
Admin UIにアクセスし、Workspace内のdefault
をクリックし、Gateway Services
からNew Gateway Service
をクリックする。
クリックすると入力フォームが出てくるので入力する。
ここではGet StartedのService作成コマンドにあわせて、以下のように設定した。
-
Name
: example_service -
Upstream URL
: http://httpbin.org
無事Serviceが作成されたらUIから確認できるはずだ。
作成されたServiceの右の︙をクリックし、View Details
をクリックすると内容が確認できる。
Formatのところを選択するとJSONでも表示できる。
また、curlで取得できることも確認する。
Helmでインストールした場合、Adminへのアクセス方法はIngressオブジェクトの中身を見ると/api
になっており、Serviceの参照URLは/services/<Service nameかID>
となっているため、以下で取得を確認する。
curl -X GET https://kong.${BASE_DOMAIN}.nip.io/api/services/example_service -k -L | jq .
無事参照出来た。
{
"port": 80,
"id": "0ad9fb49-c198-4620-80b2-9305808bc625",
"client_certificate": null,
"write_timeout": 60000,
"connect_timeout": 60000,
"name": "example_service",
"read_timeout": 60000,
"host": "httpbin.org",
"protocol": "http",
"tags": null,
"ca_certificates": null,
"tls_verify": null,
"tls_verify_depth": null,
"path": null,
"retries": 5,
"enabled": true,
"created_at": 1703572948,
"updated_at": 1703572948
}
Routeの作成
こちらも同じくUIから作成する。
default
Workspaceの左サイドバーからRoutes
をクリックし、New Route
をクリックする。
こちらも元のGet Startedの記載にあわせて入力する。
設定した値は以下となる。(設定していない箇所はデフォルト値を使用)
-
Name
: example_route -
Service
: example_serviceを選択 -
Paths
: /mock
無事作成できたら、Serviceと同じように詳細を確認する。
また、Serviceと同じようにcurlでも確認する。
curl -X GET https://kong.${BASE_DOMAIN}.nip.io/api/routes/example_route -k -L | jq .
こちらも無事取得できた。
{
"service": {
"id": "0ad9fb49-c198-4620-80b2-9305808bc625"
},
"id": "e3aaef3d-8b12-46bb-b7f0-37c94bfc5f58",
"request_buffering": true,
"response_buffering": true,
"destinations": null,
"https_redirect_status_code": 426,
"regex_priority": 0,
"headers": null,
"snis": null,
"sources": null,
"protocols": [
"http",
"https"
],
"tags": [],
"paths": [
"/mock"
],
"path_handling": "v0",
"preserve_host": false,
"strip_path": true,
"name": "example_route",
"updated_at": 1703574216,
"methods": null,
"created_at": 1703574216,
"hosts": null
}
エンドサービスへのアクセス
KongはRouteに定義したPathを使ってトラフィックをルーティングする。
-kong-proxyというkind: Service
のオブジェクトがtype: LoadBalancer
でIPを公開しており、これへのアクセスをルーティングしてくれるようなので、さっそくアクセスしてみる。
先程作成したRouteのPathは/mock
だったので、kong-proxyのIPに/mock
をつけたものがhttpbin.orgと思ってアクセスする。
$ export PROXY_IP=$(kubectl get svc -n kong quickstart-kong-proxy -o jsonpath={.status.loadBalancer.ingress[0].ip})
$ curl -X GET http://${PROXY_IP}/mock/ip -k
{
"origin": "192.168.3.1, xxx.xxx.xxx.xxx"
}
無事httpbinへ飛ばしてくれて、結果を取得することが出来た。
レート制限
次にレート制限を確認する。
レート制限にはRate Limiting Pluginを利用する。
こちらもGet Startedの手順(CLI)では実施せずに、Admin UIベースで進める。
default
Workspaceの左サイドバーからPlugins
をクリックし、New Plugin
をクリックする。
こちらも元のGet Startedの記載にあわせて入力する。
Pluginの一覧画面になるので、検索窓で"Rate Limit"を検索する。
Enable
をクリックするとインストール画面に遷移するので、値を設定する。
ここでは以下の値を設定した。
-
Instance Name
: rate-limiting -
minute
: 5
これにより、1分あたり5回を超えてアクセスする分には制限が掛かるようになる。
他はデフォルト値とした。
なお、Get Startedではconfig.policy=local
としているが、これはUIから設定する場合はデフォルトがlocal
となっているため、設定不要となる。
Install
をクリックするとインストールされる。
ServiceやRouteと同じようにView Details
をクリックすると同じように詳細情報が取得できる。
設定を一部抜粋する。
"error_code": 429,
"error_message": "API rate limit exceeded",
"minute": 5,
これは1分間に5回以上アクセスすると、429と設定されたエラーメッセージを返すことを意味している。
早速試してみる。
curlを連続して実行したところ、6回目に以下のエラーを得ることが出来た。
$ curl -i http://$PROXY_IP/mock/ip
HTTP/1.1 429 Too Many Requests
Date: Tue, 26 Dec 2023 11:06:09 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
RateLimit-Reset: 51
X-RateLimit-Limit-Minute: 5
Retry-After: 51
X-RateLimit-Remaining-Minute: 0
RateLimit-Remaining: 0
RateLimit-Limit: 5
Content-Length: 92
X-Kong-Response-Latency: 0
Server: kong/3.5.0.2-enterprise-edition
X-Kong-Request-Id: 0dd34b10979c3b4bcf68517b51361e5c
{
"message":"API rate limit exceeded",
"request_id":"0dd34b10979c3b4bcf68517b51361e5c"
}
これにより、レート制限が適切に効いたことが確認できる。
また、ヘッダでレート制限の情報が取得できることも確認できる。
Proxy Cache
Kong GatewayではProxy Cache Pluginを使ってキャッシュ機能を提供している。
これを使ってみる。
なお、以下のキャッシュの種類が用意されている。
- グローバル:全てのリクエストに対してキャッシュする
- Serviceレベル:特定のServiceに対してキャッシュする
- Routeレベル:特定のRouteに対してキャッシュする
- コンシューマレベル:コンシューマ(ユーザを抽象化したもの)単位でのキャッシュ
それぞれのレイヤでPluginをインストールして利用することになる。
ここではServiceレベルで試してみる。
default
Workspaceの左サイドバーからGateway Services
で作成したexample_service
をクリックし、Plugins
->New Plugin
をクリックする。
こちらも元のGet Startedの記載にあわせて入力する。
Pluginの一覧画面になるので、検索窓で"Proxy Caching"を検索し、Enable
をクリックする。
こうすると、最初からProxyの範囲が指定された状態でインストール画面となる。
Get Startedに合わせて値を設定していく。
ここでは以下の値を設定し、他はデフォルト値とした。
-
cache_ttl
: 30 -
request_method
: GET -
response_code
: 200
cache_ttl
を30に設定することで、キャッシュは30秒間保持され、期間を超えると破棄される。
インストール完了後、再度Proxyに対してcurlを実行する。
$ curl -i http://$PROXY_IP/mock/ip
:(省略)
X-Cache-Key: 8d9a6010fe0c02b2924e1f7bf7a1d4e4ce22fe87e91021c697cc28f8de0fc3f1
X-Cache-Status: Miss
:(省略)
キャッシュを見に行ったことが確認できた。
再度実行すると、以下のようにキャッシュを使ったことが読み取れる。
X-Cache-Key: 8d9a6010fe0c02b2924e1f7bf7a1d4e4ce22fe87e91021c697cc28f8de0fc3f1
X-Cache-Status: Hit
また、30秒以上間をあけて再実行すると、Miss
が表示されることが確認できる。
これによりキャッシュが使えていることが確認できる。
鍵認証
Kong GatewayではPluginで認証を提供しており、以下のような認証が利用できる。
- 鍵認証
- Basic認証
- OAuth 2.0
- LDAP
- OpenID Connect
ここでは鍵認証を試してみる。
最初に鍵認証を有効化する。
Proxy Cacheと同様にグローバル、Service、Routeの単位で設定できるため、ここではProxy Cacheと同様にServiceに設定する。
default
Workspaceの左サイドバーからGateway Services
で作成したexample_service
をクリックし、Plugins
->New Plugin
をクリックする。
"key"で検索するとKey Authorization Pluginが見えるのでEnable
にする。
値の設定画面ではInstance Nameをkey-auth
にし、他はデフォルト値のままインストールする。
これでキー認証が有効化された。
次にユーザを抽象化したオブジェクトであるコンシューマを作成する。
default
Workspaceの左サイドバーからConsumers
をクリックし、New Consumer
をクリックする。
Get Startedと同じくluka
というUsernameで作成する。
作成したコンシューマにキーを割り当てる。
作成したコンシューマのCredentrials
からNew Key Auth Credential
を選択し、キーを作成する。
ここではKeyにtop-secret-key
を設定した。
なお、この設定値はUIから確認できるし、Admin APIの/consumers/<ユーザ名>/key-auth
をGETすることでも確認できる。
先程までアクセスできていた、httpbinのサービスにアクセスしてみる。
$ curl -i http://$PROXY_IP/mock/ip
HTTP/1.1 401 Unauthorized
Date: Thu, 28 Dec 2023 00:01:45 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
WWW-Authenticate: Key realm="kong"
Content-Length: 96
X-Kong-Response-Latency: 2
Server: kong/3.5.0.2-enterprise-edition
X-Kong-Request-Id: b723b10614ac7a3e1ecc431880199603
{
"message":"No API key found in request",
"request_id":"b723b10614ac7a3e1ecc431880199603"
}
鍵認証が設定されたことで、キーなしではアクセスに失敗するようになった。
ヘッダにキーを設定してアクセスしてみる。
$ curl -i http://$PROXY_IP/mock/ip -H 'apikey:top-secret-key'
HTTP/1.1 200 OK
Content-Type: application/json
:(省略)
X-Kong-Request-Id: 7970436b3bcdfc6a0f6e583c6b11d32c
{
"origin": "192.168.3.1, xxx.xxx.xxx.xxx"
}
無事取得できた。試しに設定していないキーでも試してみる。
$ curl -i http://$PROXY_IP/mock/ip -H 'apikey:nokey'
HTTP/1.1 401 Unauthorized
Date: Thu, 28 Dec 2023 00:07:49 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
Content-Length: 81
X-Kong-Response-Latency: 15
Server: kong/3.5.0.2-enterprise-edition
X-Kong-Request-Id: b2cc5178a000f45450462543b2f0712f
{
"message":"Unauthorized",
"request_id":"b2cc5178a000f45450462543b2f0712f"
}
ちゃんと弾かれた。
ロードバランシング
Kong Gatewayにはロードバランサの機能もあり、負荷分散やヘルスチェック、サーキットブレーカー的な機能を提供する模様。
ここではGet Startedに従って、ラウンドロビンで以下のようにhttpbin.org
とhttpbun.com
に分散する。
なお、httpbun.com
は今回初めて知ったが、httpbin.org
と同様の機能を提供するサービスのようだ。
$ curl httpbun.com/ip
{
"origin": "xxx.xxx.xxx.xxx"
}
早速設定していく。
default
Workspaceの左サイドバーからUpstreams
をクリックし、New Upstream
をクリックする。
設定画面になるので値を入れてSave
をクリックする。
ここでは、Name
にexample_upstream
を設定した。
なお、名前を入力する際に上記の画像のように(Add new value)
と表示されるが、これをクリックしないと名前が正確に反映されず、Save
が活性化されずにクリックできないため注意。
次にロードバランスする先を設定する。
保存後、作成されたexample_upstream
をクリックし、Targets
からNew Target
をクリックしてhttpbin.org:80
とhttpbun.com:80
を追加する。なお、ポートを指定しない場合は8000ポートが割り当てられる。
最後にServiceを変更する。
今はexample_service
のエンドポイントはhttpbin.org
を向いているが、これを今作成したexample_upstream
に変更する。
default
Workspaceの左サイドバーからGateway Services
->example_service
で画面遷移後、Gateway Service actions
からEdit configuration
をクリックする。
Host
の項目をexample_upstream
に書き換え、Save
をクリックして保存する。
これでアクセスが分散する。
試してみる。何度かアクセスし、Hostが変わることを確認する。
キャッシュの設定が残っている場合は少し間を置いてアクセスしないと駄目かも。
$ curl -i http://$PROXY_IP/mock/headers -H 'apikey:top-secret-key' 2>&1 | grep '"Host"'
"Host": "httpbin.org",
$ curl -i http://$PROXY_IP/mock/headers -H 'apikey:top-secret-key' 2>&1 | grep '"Host"'
"Host": "httpbun.com",
httpbin.org
とhttpbun.com
にアクセスしていることが確認できた。
まとめ
Kong Gatewayでの各種機能を一通り触って、以下を確認できた。
- Kong GatewayでAPIに対するキャッシュ、認証、制限、LBなどの機能が簡単に設定できる
- Get Startedに書かれているCLIでの手順は全てUIでも確認できる
- Helmでインストールする環境でもGet Startedの内容は全て同等に実施できる
所感としては、特別なコマンドを使わず設定できること、UIは癖がないことなどから非常に使いやすいツールだと感じた。
また、Podのログも少し見たが、しっかり出ているようなのでトラシュもやりやすそう。
なお、どうも似たようなことがKong Ingress Controllerでも出来そうなので、こちらも機会があれば確認してみたいと思う。