Python を使った Kong Gateway のジオコーディング プラグインの構築

最近、あるコミュニティ ユーザーから、いくつかの位置検索キーワードに基づいて住所の詳細を調べることができる Kong プラグインについて問い合わせがありました。ジオコーディングをサポートするライブラリは多数ありますが、ここでは簡単に使える Python Library を選択します。Kong Gateway はバージョン 2.3 以降、Python で記述されたプラグインをサポートしています。

Kong Python PDK の使用が容易であること示すため、GeoPy ライブラリを使って、Kong Plugin 内でジオコーディングを実行できるようにします。

Kong に API リクエストを行う際、呼び出し側はリクエスト ヘッダーに「トラファルガー広場」のような位置検索キーワードを渡します。プラグインは GeoPy ライブラリを使用して、検索キーワードに対応する住所を検索し、住所の緯度と経度を特定して、アップストリームに渡す前に追加のヘッダーとしてリクエストに追加します。

開発環境のブートストラップ

Kong Gateway の Python プラグイン サポートは、Kong Gateway と同じホスト上で Python プラグイン サーバーを別のプロセスとして実行し、Unix Sockets を使ってメッセージをやり取りすることで機能します。このメカニズムは、JS や Go など、Kong の他の外部プラグインにも使用されています。

より簡単に利用できるように、Docker ベースの環境を用意しました。必要なのは、Docker と Docker Compose がインストールされたマシンだけです。イメージのダウンロードと環境の構築に 1 ~ 2 分かかるため、今すぐバックグラウンドで実行を開始してから先に進むことをお勧めします。

$ git clone --recurse-submodules https://github.com/Kong/kong-plugin-py-geocode
$ cd kong-plugin-py-geocode
$ docker compose build

Docker Compose ファイルは、以下を設定します。

  • ベース イメージからカスタム Kong イメージを作成し、Kong Python PDK と GeoPy Python ライブラリをインストールします。
  • DB-less モードで Kong Gateway インスタンスを起動します — プラグインは DB-backed モードでも動作しますが、ここではサンプルを簡潔にするため DB-less モードを使用します。
  • Docker ボリュームを使用して、Kong 宣言型設定と Python プラグイン ファイルを Kong インスタンスにマウントします。

プラグインの準備

Kong Gataway は、Python プラグイン サーバーを起動し、kong-py-plugin ディレクトリにあるすべてのプラグインをロードするように設定されています。現在、このディレクトリには 1 つのプラグイン ファイル「py-geocode.py」が含まれています。必要な Python ライブラリをインポートして、プラグインを開始します。

#!/usr/bin/env python3
import os
import kong_pdk.pdk.kong as kong
from geopy.geocoders import Nominatim

これらの Python ライブラリは、Kong イメージのビルドに使用される Dockerfile で pip を使用してインストールする必要があります。

ほかの優れたプラグインと同様に、py-geocode プラグインは設定ですべてのパラメーターを公開しています。😎

  • 位置検索用語を指定するリクエスト ヘッダー
  • 見つかった住所を含むアップストリーム ヘッダー
  • 位置の緯度と経度を含むアップストリーム ヘッダー
Schema = (
    {"location_search_header": {"type": "string"}},
    {"location_address_header": {"type": "string"}},
    {"location_lat_header": {"type": "string"}},
    {"location_long_header": {"type": "string"}},
)

プラグインのバージョンと優先順位も指定します。最初に、プラグイン オブジェクトとカスタム ロジックを実装します。

Kong Gateway リクエストのライフサイクルには、HTTP リクエストで利用可能な 5 つのフェーズがあります。

  1. certificate – SSL/TLS 接続のプロキシの場合、リクエストごとに 1 回実行されます。
  2. rewrite – API ゲートウェイがアップストリームへのリクエストのルーティングを行う前に実行されます。
  3. access – すべてのルーティングが識別され、プラグインはリクエストがどのサービスにバインドされているかを把握します。これは API ゲートウェイがリクエストをアップストリームに送る前の最後のフェーズです。
  4. response – アップストリームからのレスポンスを処理します。このフェーズを実装すると、リクエストがバッファリングされるため、パフォーマンスが低下します。
  5. log – リクエストが完了した後に実行されます。

ここでは、アップストリームに送る前にリクエストにヘッダーを追加したいので、access フェーズで行います。

class Plugin(object):
    def __init__(self, config):
        self.config = config

    def access(self, kong: kong.kong):
        geolocator = Nominatim(user_agent="kong")

また、これらのヘッダーがプラグイン設定で指定されていない場合は、デフォルト値を指定します。これは、プラグインの回復力を高めるためです。💪

以下にコード スニペットを示します。

# set defaults for header values
        if 'location_search_header' in self.config:
            location_search_header = self.config['location_search_header']

最後に、以下のカスタム ロジックを実行します。

  • リクエスト ヘッダーから位置検索キーワードを抽出します。
  • GeoPy ライブラリを使用して、位置の詳細を取得します。
  • 位置の住所、緯度、経度のアップストリーム ヘッダーを追加します。

すべてのロジックを try-except ブロックでラップし、プラグインでエラーが発生しても、例外が適切に処理されるようにします。👊

try:
location_search = kong.request.get_header(location_search_header)
location = geolocator.geocode(location_search)
      kong.service.request.set_header(location_address_header, location.address)
      kong.service.request.set_header(location_lat_header, location.latitude)
      kong.service.request.set_header(location_long_header, location.longitude)
except Exception as ex:
      kong.log.error(ex)

プラグインを有効にする

この実行環境では、Kong の宣言型設定機能を使用しています。つまり、Kong 設定は YAMLファイルとして定義されています。config/kong.yml を開くと、httpbin.org/anything にプロキシし、ジオコーディング プラグインが適用されたパス「/any」のルートを 1 つ公開するサービスが定義されています。

services:
- name: httpbin-svc
  url: http://httpbin.org/anything
  routes:
  - name: example-route
    paths:
    - /any
    plugins:
    - name: py-geocode

Kong Gateway では、セキュリティ上の理由から allowlist に登録されているプラグインのみ使用が許可されます。docker-compose.yml を開くと、今回のプラグイン「py-geocode」が有効になっています。

KONG_PLUGINS=bundled,py-geocode

最後に、ゲートウェイ コンテナーの起動に使用する環境変数を使って、Python プラグイン サーバーを有効にします。

試してみる

API Gateway は Python プラグインを実行する準備ができているので、起動してみましょう。

$ docker compose up

コンテナーが起動すると、Kong Gateway のプロキシ ポートは、ローカル マシンのポート 8000 で利用できるようになります。

プラグインが参照するデフォルトのヘッダーである HTTP ヘッダー「x-location-search」に位置検索のキーワードを指定し、簡単な位置検索を試してみましょう (宣言的設定では、プラグイン設定を一切指定していません)。

curl localhost:8000/any -H 'x-location-search:"Trafalgar Square"'
{
  "args": {},
  "data": "",
  "files": {},
  "form": {},
  "headers": {
    "Accept": "*/*",
    "Host": "httpbin.org",
    "User-Agent": "curl/7.68.0",
    "X-Amzn-Trace-Id": "Root=1-6246cc5b-52a77b5d39a34a9f7d836589",
    "X-Forwarded-Host": "localhost",
    "X-Forwarded-Path": "/any",
    "X-Forwarded-Prefix": "/any",
    "X-Location-Address": "Trafalgar Square, St. James's, Covent Garden, City of Westminster, London, Greater London, England, WC2, United Kingdom",
    "X-Location-Lat": "51.508037",
    "X-Location-Long": "-0.12804941070725",
    "X-Location-Search": "\"Trafalgar Square\""
  },
  "json": null,
  "method": "GET",
  "origin": "192.168.48.1, 116.14.136.90",
  "url": "http://localhost/anything"
}

カスタム プラグインは位置の住所、緯度、経度を調べ、アップストリーム ヘッダーとして追加し、httpbin のレスポンスに反映させていることがわかります。

プラグイン設定の指定

たとえば、検索キーワードを「x-search」ヘッダーで調べ、位置情報を「x-location-addr」ヘッダーで追加したいとします。インテリジェントなプラグインを記述したため、プラグイン設定でこれらを設定できます。dbless_config ディレクトリにある declarative configuration に移動して、kong.yaml を編集してプラグイン設定にこれらを指定します。

services:
- name: httpbin-svc
  url: http://httpbin.org/anything
  routes:
  - name: example-route
    paths:
    - /any
    plugins:
    - name: py-geocode
      config:
        location_search_header: x-search
        location_address_header: x-location-addr

Ctrl + C キーを押して API ゲートウェイを再起動し、再度 docker-compose を実行すると、localhost:8000 に対して「x-search」ヘッダーで検索位置を指定してリクエストできるようになります。

curl localhost:8000/any -H 'x-search:"Trevi Fountain"' 
1.639s
{
  "args": {},
  "data": "",
  "files": {},
  "form": {},
  "headers": {
    "Accept": "*/*",
    "Host": "httpbin.org",
    "User-Agent": "curl/7.68.0",
    "X-Amzn-Trace-Id": "Root=1-6246cd17-0ec71c21628936c5388d9386",
    "X-Forwarded-Host": "localhost",
    "X-Forwarded-Path": "/any",
    "X-Forwarded-Prefix": "/any",
    "X-Location-Addr": "Fontana di Trevi, Piazza di Trevi, Trevi, Municipio Roma I, Roma, Roma Capitale, Lazio, 00187, Italia",
    "X-Location-Lat": "41.9009778",
    "X-Location-Long": "12.48328484234",
    "X-Search": "\"Trevi Fountain\""
  },
  "json": null,
  "method": "GET",
  "origin": "192.168.48.1, 116.14.136.90",
  "url": "http://localhost/anything"
}

まとめ

数行の Python コードで、設定オプションと例外処理を備えた、Kong Gateway プラグインが完成しました。

ここで紹介したプラグインは簡単なものですが、ここで使用した環境と Kong 設定について学んだことを活かせば、Python エコシステムが提供する幅広いライブラリをフル活用して、思いのままにプラグインを構築することができます。

ほかのプラグインの例に興味のある方は、kong-python-pdk の examples ディレクトリを確認してください。

質問のある方は、Kong Nation に投稿してください。Kong Community にもぜひご参加ください。


Kong Enterprise の概要、価格、およびライセンス体系などの詳細は、こちらを参照してください。


参照記事: 2022 年 6 月 3 日
Shrikanth Rajgopalan
© Kong Inc. 2022
Building a geocoding plugin for the Kong Gateway using Python

タイトルとURLをコピーしました