前回、Kong Gatewayに入門したが、KongのIngress Controller(KIC)でも似たような事が出来るようなので、それを検証した時のメモ。
以下の機能をKICのGet Startedを元に一通り触ってみる。
- ロードバランシング
- レート制限
- Proxy Cache
- 鍵認証
Kong Ingress Controllerとは
Kongが提供するIngress Controllerで、特徴としてKubernetesリソースの設定を読み取り、Kong Gateway側に設定を送って反映してくれる。
(Kong Ingress Controller
- Architectureより引用)
要はKong Gatewayの全ての設定をManifestで実現できることになり、GitOpsでKong Gateway(=API)を設定・管理できるようになることを意味する。
Tanzu Mission Controlと組み合わせれば、クラスタのデプロイ完了と同時にKong Gatewayの設定も完了、みたいなのも実現できそうだ。
あと、前回検証時は使っていなかったが、実はKIC自体がKong Gatewayを持っているため、Kong GatewayをデプロイしなくてもGatewayを利用することが出来る。(ただしUIはない)
検証時の前提
ここでは以下の環境があるものとして進める
- Kubernetesクラスタ(ここではTanzu Kubernetes Gridを利用)
- Kubernetesクラスタはtype: LoadBalancerが利用可能
- 作業端末にkubectl, helmコマンドがインストール済み
また、今回検証で使ったKICのバージョンは3.4である。
事前準備
KICをインストールする。
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/experimental-install.yaml
helm install kong-ingress --namespace kong --create-namespace --repo https://charts.konghq.com ingress
こちらの手順に従ってGatewayとGatewayClassを作成する。
echo "
---
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: kong
annotations:
konghq.com/gatewayclass-unmanaged: 'true'
spec:
controllerName: konghq.com/kic-gateway-controller
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: kong
spec:
gatewayClassName: kong
listeners:
- name: proxy
port: 80
protocol: HTTP
" | kubectl apply -n dev -f -
また、Ingress ControllerがデプロイしたKong Gatewayを経由してエンドサービスにアクセスするため、External-IP
で公開しているIPを環境変数に設定しておく。
export PROXY_IP=$(kubectl get svc -n kong kong-ingress-gateway-proxy -o jsonpath={.status.loadBalancer.ingress[0].ip})
ロードバランシング
最初にロードバランシングする先を用意する。
アクセス先を用意するのが面倒だったので、externalName
を使って適当な外部サービスを参照させる。httpbin.org
とhttpbun.com
を使おうと思ったが、httpbun.com
がHostヘッダを適切に設定しないと値を返してくれなかったので、こちらは代わりにipaddr.show
に変更した。
※ipaddr.showは聞き慣れないが、こちらの方が作成したサービス
以下のkind: Service
を作成する。
cat << EOF > ./ipcheck-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: ipaddr
namespace: dev
spec:
type: ExternalName
externalName: ipaddr.show
---
apiVersion: v1
kind: Service
metadata:
name: httpbin
namespace: dev
spec:
type: ExternalName
externalName: httpbin.org
EOF
kubectl apply -f ./ipcheck-svc.yaml -n dev
次にHTTPRoute
リソースを作成する。こちらによるとspec.rules[0].backendRefs
に宛先を複数書けるので、以下のような感じで/new-mock
にアクセスするとhttpbinかhttpbunにアクセスするHTTPRoute
を作成する。
cat << EOF > ./httproute.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: ipcheck
annotations:
konghq.com/strip-path: 'true'
spec:
# Gatewayリソース
parentRefs:
- name: kong
rules:
- matches:
- path:
type: PathPrefix
value: /new-mock
backendRefs:
- name: ipaddr
kind: Service
port: 80
- name: httpbin
kind: Service
port: 80
EOF
kubectl apply -n dev -f ./httproute.yaml
HTTPRoute
が作成されると、Kong Gateway側でもServiceやRouteが作成される。
$ kubectl exec -it centos -- curl https://kong-ingress-gateway-admin.kong.svc:8444/routes -k | jq .
{
"data": [
{
"id": "f61597cf-65e3-5da0-b4fb-60193a8102ba",
:(省略)
"name": "httproute.dev.ipcheck.0.0",
"snis": null,
"methods": null,
"sources": null,
"paths": [
"~/new-mock$",
"/new-mock/"
],
:(省略)
分散先を確認したい場合はUpstreamのTargetを見るとよい。
$ kubectl exec -it centos -- curl https://kong-ingress-gateway-admin.kong.svc:8444/upstreams/httproute.dev.ipcheck.0/targets -k | jq .
"data": [
:(省略)
"target": "ipaddr.show:80",
"weight": 1,
:(省略)
"target": "httpbin.org:80",
"weight": 1,
:(省略)
アクセスしてみる。httpbin.org/ipとipaddr.show/ipで見え方が異なるため、何度かアクセスして見え方が変われば分散していることが確認できる。
$ curl $PROXY_IP/new-mock/ip
{
"origin": "192.168.3.1, xxx.xxx.xxx.xxx"
}
$ curl $PROXY_IP/new-mock/ip
xxx.xxx.xxx.xxx
問題ないようだ。
レート制限
レート制限などPluginを利用する場合、適用範囲に応じて2パターンの利用方法がある。
- 特定の対象にPluginを適用する場合
-
KongPlugin
リソースを作成する - 適用対象(
Service
、HTTPRoute
など)のannotation
にkonghq.com/plugins=<作成したKongPluginの名前>
を設定する
-
- 全体(グローバル)にPluginを適用する場合
-
KongClusterPlugin
リソースを作成する
-
ここではレート制限をServiceに適用してみる。
以下のPluginを定義したManifestをapplyする。
echo "
apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
name: rate-limit-5-min
annotations:
kubernetes.io/ingress.class: kong
config:
minute: 5
policy: local
plugin: rate-limiting
" | kubectl apply -n dev -f -
KongPlugin
リソースではspec
がなく、config
でPlugin固有の設定を書いていく。
また、plugin
でPluginを指定する。
まだこの段階ではPluginを定義しただけで、実際のサービスとは紐づいていな異状態である。
このPluginを使うようにKubernetesリソースを修正する。
最初Service
のannotation
を変更して適用しようとしたのだが、何故か適用するとroutesが見れなくなってルーティングが出来なくなってしまったので、HTTPRoute
の方を変更する。
kubectl annotate httproute ipcheck konghq.com/plugins=rate-limit-5-min -n dev
設定適用後、6回アクセスすると6回目に失敗することが確認できる。
$ curl $PROXY_IP/new-mock/ip
xxx.xxx.xxx.xxx
$ curl $PROXY_IP/new-mock/ip
{
"message":"API rate limit exceeded",
"request_id":"4d6f884b9495c702d9fc690df40e2323"
Proxy Cache
Proxy CacheもManifestで設定できる。
ここではGet Startedに従い、KongClusterPlugin
リソースで定義してグローバルにキャッシュを効かせるようにする。
早速Manifestを適用する。TTLのところだけ、300秒だと検証しづらくなるので30秒に変更した。
echo '
apiVersion: configuration.konghq.com/v1
kind: KongClusterPlugin
metadata:
name: proxy-cache-all-endpoints
annotations:
kubernetes.io/ingress.class: kong
labels:
global: "true"
plugin: proxy-cache
config:
response_code:
- 200
request_method:
- GET
- HEAD
content_type:
- text/plain; charset=utf-8
cache_ttl: 30
strategy: memory
' | kubectl apply -f -
なお、httpbinにアクセスする場合、どうもBypassされてしまうようなので、ipaddrにのみアクセスするようServiceを削除する。
kubectl delete svc httpbin -n dev
確認する。
$ curl $PROXY_IP/new-mock/ip -i 2>&1 | grep -i cache-status
X-Cache-Status: Miss
$ curl $PROXY_IP/new-mock/ip -i 2>&1 | grep -i cache-status
X-Cache-Status: Hit
初回はMissとなり、2回目はHitとなった。
また、30秒程度放置し再実行するとMiss
となりTTLが効いていることも確認できる。
鍵認証
最後に鍵認証も試してみる。
コンシューマがカスタムリソースKongConsumer
として用意されているので、以下のような流れで試していく。
-
KongPlugin
で鍵認証用のPluginを定義 -
KongConsumer
でユーザと鍵を定義 - 鍵を使ってアクセス
最初に鍵認証のPluginを作成する。ManifestはGet Startから引用した以下となる。
echo "
apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
name: key-auth
plugin: key-auth
config:
key_names:
- apikey
" | kubectl apply -n dev -f -
これをHTTPRoute
に適用する。annotation
にはカンマ区切りで複数のPluginを指定することが出来る。
kubectl annotate httproute ipcheck konghq.com/plugins=rate-limit-5-min,key-auth --overwrite
annotation
を変更するとすぐにアクセスできなくなる。
$ curl $PROXY_IP/new-mock/ip
{
"message":"No API key found in request",
"request_id":"49e03858394ffd7f98f1690bb8354d7f"
}
次に鍵を作成する。Secret
で鍵の種類(kongCredType
)と鍵の内容(key
)を指定する。
kubectl create secret generic alex-key-auth \
--from-literal=kongCredType=key-auth \
--from-literal=key=hello_world
Secret
というのがイケていないが、KongはSecret
のバックエンドにAWS SecretManagerやVaultをサポートしているので、多分置き換えることも出来ると思われる。
鍵を作成したら、KongConsumer
を作成する。
echo "apiVersion: configuration.konghq.com/v1
kind: KongConsumer
metadata:
name: alex
annotations:
kubernetes.io/ingress.class: kong
username: alex
credentials:
- alex-key-auth
" | kubectl apply -f -
credentials
に先ほど作成したSecret
を渡している。
接続してみる。
$ curl $PROXY_IP/new-mock/ip -H 'apikey: hello_world'
xxx.xxx.xxx.xxx
問題なく接続できた。
キーを変更するとエラーになる。
$ curl $PROXY_IP/new-mock/ip -H 'apikey: hello'
{
"message":"Invalid authentication credentials",
"request_id":"7ca9a84eed124b100182f440694a2154"
}
鍵認証も正常に動作した。
まとめ
KICがあればKong Gatewayの構築から管理までManifestで簡単に実現できることが確認できた。
また、よくあるKubernetesリソースになると機能制約が発生する、といったものも見当たらなかったので、普通にKong Gatewayとして使えそうだった。
Manifestで宣言的に用意できるということで、GitOpsでの利用も容易そうで、KubernetesのCloudNativeな作りと上手くマッチしそうなツールだと思う。