Kong Gatewayでは既存のプラグインで機能が足りない場合、自作したプラグイン(カスタムプラグイン)を導入して必要な機能を追加実装することが出来る。
今回はこのカスタムプラグインのインストール方法を確認する。
Kong Gatewayの展開方法には大きく3種類ある。
- Linux上にパッケージマネージャーを使ってインストールする
- Dockerコンテナを立てる
- Kubernetes上に構築する
カスタムプラグインのインストール方法はKong Gatewayの展開方法によって異なるので、それぞれの展開方法でどうなっているかを確認する。
なおカスタムプラグインのインストール方法について参考となる公式ドキュメントは以下あたりとなる。
- Installation and Distribution
- Custom Plugins
- Kong custom plugin distribution with KongPluginInstallation
また、インストールの検証を行うにあたりサンプルとなるカスタムプラグインを以下に用意した。
https://github.com/imurata/kong-plugin-echo
これはパラメータmessage
に指定した文字列をKong Gateway側のログに出力するだけのプラグインであり、これを使って動作確認を行っていく。
なお各検証時、事前に以下のコマンドを実行した状態で始めるものとする。
git clone https://github.com/imurata/kong-plugin-echo
cd kong-plugin-echo
またKong Gatewayは構築済みの状態から始めるものとする。
Linuxのパッケージマネージャーで展開した場合
カスタムプラグインのインストール
Kong GatewayをLinuxのパッケージマネージャーで展開した場合、公式ドキュメントではLuaRocksというLua向けのパッケージ管理ツールを使ってプラグインをインストールすることが推奨されているので、こちらを使ってインストールする。
LuaRocksでインストールする場合、rockspecと呼ばれるインストールの仕様書みたいなものを作ってあげる必要がある。
Kongが配布しているテンプレートをベースに以下のような感じで作成する。
cat <<EOF > ./kong-plugin-echo-1.0.0-1.rockspec
local plugin_name = "echo"
local package_name = "kong-plugin-" .. plugin_name
local package_version = "1.0.0"
local rockspec_revision = "1"
package = package_name
version = package_version .. "-" .. rockspec_revision
description = {
summary = "Write the specified string to log.",
}
source = {
url = "https://example.com",
}
build = {
type = "builtin",
modules = {
["kong.plugins."..plugin_name..".handler"] = "kong/plugins/"..plugin_name.."/handler.lua",
["kong.plugins."..plugin_name..".schema"] = "kong/plugins/"..plugin_name.."/schema.lua",
}
}
EOF
なんとなく見れば分かると思うので説明は割愛するが、source.url
については必須パラメータとなっているので仕方なく書いている。
一旦ローカルにインストールする。
$ luarocks make --local
No existing manifest. Attempting to rebuild...
kong-plugin-echo 1.0.0-1 is now installed in /home/ubuntu/.luarocks
インストールされたものをパッケージングする。なおzipコマンドが無いと失敗するので注意。
$ luarocks pack kong-plugin-echo 1.0.0-1
Packed: /home/ubuntu/CustomPlugin/kong-plugin-echo/kong-plugin-echo-1.0.0-1.all.rock
カレントディレクトリにkong-plugin-echo-1.0.0-1.all.rock
が出来ている。これをKong Gateway環境に持ち込んでインストールする。
$ sudo luarocks install ./kong-plugin-echo-1.0.0-1.all.rock
kong-plugin-echo 1.0.0-1 is now installed in /usr/local
インストールされたLuaRocksパッケージは以下のような感じで確認できる。
$ luarocks list kong-plugin-echo
Rocks installed for Lua 5.1
---------------------------
kong-plugin-echo
1.0.0-1 (installed) - /usr/local/lib/luarocks/rocks-5.1
動作確認
Kongの設定を変更し、カスタムプラグインを認識させる。
sudo vi /etc/kong/kong.conf
エディタを開いた後、plugins
の項目に作成したプラグインを追加する。
plugins = bundled,echo
bundled
の指定はなくてもいいが、これを外すと元々バンドルされているプラグインが使えなくなるので、ただカスタムプラグインを追加するだけであれば指定が必要となる(参考:plugins)
また、プラグインのログ出力はセベリティをinfo
にしているので、log_level
も変更しておく。
log_level = info
Kong Gatewayを再起動する。
sudo kong restart
Service、Routeを作成し、PluginをRouteに割り当てる。
curl -i -X POST http://localhost:8001/services \
-d "name=httpbin-svc" \
-d "url=https://httpbin.org"
curl -i -X POST http://localhost:8001/services/httpbin-svc/routes \
-d "name=httpbin-rt" \
-d "paths[]=/echo"
curl -i -X POST http://localhost:8001/routes/httpbin-rt/plugins \
--data "name=echo" \
--data "config.message=Hello world."
カスタムプラグインの設定でconfig.message=Hello world.
を指定することで、Kong GatewayのログにHello world.
が出てくるようになる。
Routeにアクセスしてログ出力されるか確認する。
curl localhost:8000/echo
ログの内容はこちら。
$ grep echo /usr/local/kong/logs/error.log
2024/12/15 12:26:35 [info] 10630#0: *1294 [kong] handler.lua:7 [echo] [echo Plugin] Message: Hello world., client: 127.0.0.1, server: kong, request: "GET /echo HTTP/1.1", host: "localhost:8000", request_id: "26929a39f5780062415033374ae07970"
上手く動いているようだ。
Dockerコンテナで展開した場合
カスタムプラグインのインストール
Kong GatewayをDockerコンテナで展開した場合、カスタムプラグインを使うには2つのやり方がある。
- コンテナイメージ内に取り込んで、独自のイメージを作ってしまうケース
- ローカルのカスタムプラグインがあるディレクトリをコンテナ内にマウントして利用するケース
1のアプローチは複数環境で構築する時は楽が出来るが、Kong Gatewayのベースイメージに変更が入った場合にリビルドが必要となる。
また、カスタムプラグインを開発している最中だとちょっと面倒くさい。
なので今回は2の方法で検証する。
意図したディレクトリを取り込むにはこちらのドキュメントにも記載があるように、lua_package_path
というパラメータを利用する。
またパッケージマネージャーで展開した時と同様にplugins
とlog_level
も指定する。
dockerでkong.confのパラメータを渡すにはコンテナ起動時に環境変数にKONG_
をつけて設定すればいいので、起動コマンドを以下のような感じにして起動しなおす。
docker run -d --name kong-gateway \
--network=kong-net \
-e "KONG_DATABASE=postgres" -e "KONG_PG_HOST=kong-database" \
-e "KONG_PG_USER=kong" -e "KONG_PG_PASSWORD=kongpass" \
-e "KONG_ADMIN_LISTEN=0.0.0.0:8001" \
-e "KONG_ADMIN_GUI_URL=http://localhost:8002" \
-p 8000:8000 \
-p 8001:8001 \
-p 8002:8002 \
-v "${PWD}:/tmp/custom_plugins" \
-e "KONG_LUA_PACKAGE_PATH=/tmp/custom_plugins/?.lua;;" \
-e "KONG_PLUGINS=bundled,echo" \
-e "KONG_LOG_LEVEL=info" \
kong/kong-gateway:3.9.0.0
なお、lua_package_path
にはkong
という名前のディレクトリがある場所を指定するので、パスはカレントディレクトリまでで問題ない。
コマンド実行後、コンテナが無事立ち上がっていればOK。
パスの指定が誤ってたりしたら、以下のようなエラーが出る。
2024/12/15 12:58:35 [error] 1#0: init_by_lua error: /usr/local/share/lua/5.1/kong/init.lua:812: error loading plugin schemas: on plugin 'echo': echo plugin is enabled but not installed;
no plugin found
stack traceback:
[C]: in function 'assert'
/usr/local/share/lua/5.1/kong/init.lua:812: in function 'init'
init_by_lua(nginx-kong.conf:58):3: in main chunk
動作確認
パッケージマネージャーで展開した時と同様にService、Routeを作成し、カスタムプラグインをRouteにあてて動作確認する。
curl -i -X POST http://localhost:8001/services \
-d "name=httpbin-svc" \
-d "url=https://httpbin.org"
curl -i -X POST http://localhost:8001/services/httpbin-svc/routes \
-d "name=httpbin-rt" \
-d "paths[]=/echo"
curl -i -X POST http://localhost:8001/routes/httpbin-rt/plugins \
--data "name=echo" \
--data "config.message=Hello world."
プラグインにログを吐かせるためにRouteにアクセスする。
curl localhost:8000/echo
ログを確認する。
$ docker logs kong-gateway 2>&1 |grep "echo Plugin"
2024/12/15 14:05:46 [info] 2551#0: *4675 [kong] handler.lua:7 [echo] [echo Plugin] Message: Hello world., client: 172.18.0.1, server: kong, request: "GET /echo HTTP/1.1", host: "localhost:8000", request_id: "8d688c4a81956808d563f568fcdf955b"
問題なさそうだ。
Kubernetes上に展開した場合
カスタムプラグインのインストール
Kong GatewayをKubernetes上に展開した場合、一番簡単なのはConfigMap
にプラグインを設定して読み込ませる方法である。
これに関しては以前「docker buildなしでKong Gatewayのカスタムプラグインを追加する」で取り上げたのでこちらを参照して欲しい。
今回はKong Gateway Operatorで提供されるカスタムリソースKongPluginInstallation
を使ってインストールしてみる。
Operatorを使ったKong Gatewayの構築方法については「Kong Gateway Operatorを使ってKong KonnectをKubernetesのリソースとして管理する」で触れてるのでこちらを参考にして欲しい。
ただし、KongPluginInstallation
を使うにはOperatorインストール時にENABLE_CONTROLLER_KONGPLUGININSTALLATION
をtrueに設定する必要があるので注意。
以下のような感じで指定する。
helm upgrade --install kgo kong/gateway-operator -n kong-system --create-namespace --set image.tag=1.4 \
--set kubernetes-configuration-crds.enabled=true \
--set env.ENABLE_CONTROLLER_KONNECT=false \
--set env.ENABLE_CONTROLLER_KONGPLUGININSTALLATION=true
あと、KongPluginInstallation
を使う手順ではカスタムプラグインをコンテナイメージから引っ張ってくるため、コンテナレジストリが必要となる。
ここではDockerHubを利用する。
最初にディレクトリを移動し、カスタムプラグインのディレクトリを取り込んだコンテナイメージを作成する。
cd kong/plugins/
cat <<EOF > ./Dockerfile
FROM scratch
COPY echo /
EOF
docker build -t imuratashared/echo:1.0.0 .
ベースイメージのscratch
はDockerHubで提供されている最小コンテナのためのイメージである(参考)。
イメージをPushする。
docker push imuratashared/echo:1.0.0
次にカスタムリソースのManifestを作成して適用する。
cat <<EOF > ./plugin-install-echo.yaml
kind: KongPluginInstallation
apiVersion: gateway-operator.konghq.com/v1alpha1
metadata:
name: custom-plugin-echo
namespace: default
spec:
image: imuratashared/echo:1.0.0
EOF
kubectl apply -f ./plugin-install-echo.yaml
spec.image
には先程Pushしたイメージを指定する。
上手く適用できたか確認する。
$ kubectl get kongplugininstallation
NAME ACCEPTED
custom-plugin-echo True
Operatorのインストール時にENABLE_CONTROLLER_KONGPLUGININSTALLATION
を指定していなかったらACCEPTED
の欄が空欄になりTrueにならないので、空欄の場合はOperatorが適切にインストールできているか見直すとよい。
またカスタムプラグインをConfigMap
として自動生成するので、そちらも合わせて確認するとよい。
$ kubectl tree kongplugininstallation custom-plugin-echo
NAMESPACE NAME READY REASON AGE
default KongPluginInstallation/custom-plugin-echo - 71m
default └─ConfigMap/custom-plugin-echo-fpmzh - 65m
次にData Planeにプラグインを読み込ませる。
以下のようにkind: GatewayConfiguration
のspec.dataPlaneOptions.pluginsToInstall
に作成したKongPluginInstallation
のリソース名を指定する。
cat <<EOF > ./gw-config.yaml
kind: GatewayConfiguration
apiVersion: gateway-operator.konghq.com/v1beta1
metadata:
name: kong
namespace: default
spec:
dataPlaneOptions:
deployment:
podTemplateSpec:
spec:
containers:
- name: proxy
image: kong/kong-gateway:3.9.0.0
readinessProbe:
initialDelaySeconds: 1
periodSeconds: 1
env:
- name: KONG_LOG_LEVEL
value: "info"
pluginsToInstall:
- name: custom-plugin-echo
controlPlaneOptions:
deployment:
podTemplateSpec:
spec:
containers:
- name: controller
image: kong/kubernetes-ingress-controller:3.4.0
env:
- name: CONTROLLER_LOG_LEVEL
value: debug
EOF
kubectl apply -f ./gw-config.yaml
ポイントとしてはData PlaneのenvにKONG_LUA_PACKAGE_PATH
やKONG_PLUGINS
は書かない。
これはカスタムリソース側で吸収してくれてるからである。
次にkind: Gateway
を作成するが、その前にGatewayClass
がない場合は以下で作成する。
cat <<EOF > ./gwclass.yaml
kind: GatewayClass
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: kong
spec:
controllerName: konghq.com/gateway-operator
parametersRef:
group: gateway-operator.konghq.com
kind: GatewayConfiguration
name: kong
namespace: default
EOF
kubectl apply -f ./gwclass.yaml
Gateway
を作成する。
cat <<EOF > ./gw.yaml
kind: Gateway
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: kong
namespace: default
spec:
gatewayClassName: kong
listeners:
- name: http
protocol: HTTP
port: 80
EOF
kubectl apply -f ./gw.yaml
Gatewayを作成すると自動的にControl PlaneとData Planeのカスタムリソースも作成される。
$ kubectl tree gateway kong
NAMESPACE NAME READY REASON AGE
default Gateway/kong - 10m
default ├─ControlPlane/kong-zzj5q True Ready 10m
default │ ├─Deployment/controlplane-kong-zzj5q-pddhq - 10m
default │ │ └─ReplicaSet/controlplane-kong-zzj5q-pddhq-6556d4b775 - 38s
default │ │ └─Pod/controlplane-kong-zzj5q-pddhq-6556d4b775-hsr7x True 38s
default │ ├─Secret/controlplane-kong-zzj5q-6h9nv - 10m
default │ ├─Secret/controlplane-kong-zzj5q-vbcxc - 10m
default │ ├─Service/controlplane-webhook-kong-zzj5q-xwftw - 10m
default │ │ └─EndpointSlice/controlplane-webhook-kong-zzj5q-xwftw-dljxw - 10m
default │ └─ServiceAccount/controlplane-kong-zzj5q-tr5jh - 10m
default ├─DataPlane/kong-5bz8r True Ready 10m
default │ ├─ConfigMap/kong-5bz8r-msvbv - 10m
default │ ├─Deployment/dataplane-kong-5bz8r-6sz4m - 10m
default │ │ └─ReplicaSet/dataplane-kong-5bz8r-6sz4m-7768bd9857 - 2m23s
default │ │ └─Pod/dataplane-kong-5bz8r-6sz4m-7768bd9857-kp9f9 True 2m23s
default │ ├─Secret/dataplane-kong-5bz8r-pzhnp - 10m
default │ ├─Service/dataplane-admin-kong-5bz8r-clcx5 - 10m
default │ │ └─EndpointSlice/dataplane-admin-kong-5bz8r-clcx5-s96pq - 10m
default │ └─Service/dataplane-ingress-kong-5bz8r-k4jmf - 10m
default │ └─EndpointSlice/dataplane-ingress-kong-5bz8r-k4jmf-5lxrx - 10m
default └─NetworkPolicy/kong-5bz8r-limit-admin-api-dvz5c - 23s
動作確認
Kong Ingress Controller(KIC)が稼働しているので、IngressかHTTPRouteを作るとKong側でService、Routeが作成される。
ここでは公式ドキュメントで使われているDeployment
とService
を使って宛先となるアプリケーションをデプロイする。
kubectl apply -f https://docs.konghq.com/assets/kubernetes-ingress-controller/examples/echo-service.yaml
次に対応するHTTPRoute
を作成する。
cat << EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: httproute-echo
namespace: default
annotations:
konghq.com/strip-path: "true"
konghq.com/plugins: echo
spec:
parentRefs:
- name: kong
rules:
- matches:
- path:
type: PathPrefix
value: /echo
backendRefs:
- name: echo
kind: Service
port: 1027
EOF
アノテーションにkonghq.com/plugins: echo
を指定しカスタムプラグインを適用するようにしている。
この時点でServiceとRouteは作成され、Gatewayのアドレスを使えばアプリケーションにアクセスできる。
$ HOST=$(kubectl get gateway kong -o jsonpath={.status.addresses[].value})
$ curl $HOST/echo
Welcome, you are connected to node ip-192-168-2-182.ec2.internal.
Running on Pod echo-57ffc6dfcf-p8khz.
In namespace default.
With IP address 192.168.19.228.
ただし、この時点ではカスタムプラグインは適用していないのでカスタムプラグインのログは出ない。
$ kubectl logs dataplane-kong-5bz8r-6sz4m-7768bd9857-kp9f9 | grep "echo Plugin"
$
次にカスタムプラグインを適用し、再度アクセスする。
cat <<EOF | kubectl apply -f -
apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
name: echo
plugin: custom-plugin-echo
config:
message: "Hello world."
EOF
curl $HOST/echo
ログを確認する。
$ kubectl logs dataplane-kong-5bz8r-6sz4m-7768bd9857-kp9f9 | grep "echo Plugin"
2024/12/15 18:01:33 [info] 2551#0: *3235 [kong] handler.lua:7 [custom-plugin-echo] [echo Plugin] Message: Hello world., client: 192.168.32.243, server: kong, request: "GET /echo HTTP/1.1", host: "ab3d9d6a4fa7749d68f111aca5a4c0e5-1300302864.us-east-1.elb.amazonaws.com", request_id: "b97736531d284f116caa344e6c6ec562"
カスタムプラグインのログ出力が確認できた。