カスタマイズされたソリューションの解放:Apache APISIXとNode-Redの調和を活用する

March 6, 2024

Ecosystem

背景

多くのApache APISIXユーザーは、特定のシナリオでの要件を満たすために、エンタープライズ環境でのカスタマイズを必要としています。APISIXには多くの強力な組み込みプラグイン機能がありますが、それでもカスタマイズが必要な場合があります。

ユーザーはしばしばLuaでプラグインを書き、APISIXインスタンスにマウントして使用することを選択します。しかし、Luaの利用者は比較的限られています。Luaは簡単に始められますが、習得は容易ではなく、複雑なデータ変換ロジックをLuaで実装するのは非常に複雑になることがあります。現在、Java Plugin Runnerによって開発者が呼び出すことができるフックの一部しか公開されておらず、直接サポートされていない機能についてはJava Plugin Runnerのソースコードを変更する必要があります。

以下の図は、3つの一般的なプラグイン使用パターンを示しています。LuaプラグインはAPISIXコアに直接組み込まれて実行されます。Plugin RunnerはJavaやGolangなどの言語で書かれたPlugin RunnersとRPCを介して通信します。WASMプラグインはバイトコードに変換され、APISIXコア内部で実行されます。

APISIXのプラグインとプラグインランナー

ユーザーフィードバックから、カスタムプラグインの必要性は、HTTPリクエストパラメータのデータ変換や外部APIを呼び出してデータを処理することに関連していることが多いことがわかっています。

この問題に対処するために、Apache APISIXの機能を強化する新しいアプローチが提案されました。このアプローチでは、Apache APISIXの組み込みプラグインのみを使用して、認証やレート制限などの一般的な機能を設定し、新しくカスタマイズされたロジックを外部サービスに配置します。 外部サービスはバイナリプログラムまたは別のAPIサービスであり、APISIXのアップストリームとして扱われます。この外部サービスは、ミドルウェアのようにリクエストとレスポンスを処理します。このアプローチは、他のAPIゲートウェイやプロキシサービスにも適用できます。

シナリオ説明

私たちは、上流にデータクエリサービスを提供する一連のサービスを持っています(この記事ではhttps://api-ninjas.com/apiを例として使用します)。例えば、都市名に基づいて最新の天気情報や国の情報(国のGDP、首都名、通貨単位など)を取得できます。

私たちの主な目的は、開発者に汎用的なリクエストインターフェースを提供しながら、都市名とデータ範囲パラメータに基づいて取得したいデータ内容を決定できるようにすることです。さらに、上流サービスを悪用から保護するために、開発者インターフェースに認証サービスを追加し、正しいAPI Keyを持つリクエストのみを上流サービスに転送できるようにする必要があります。

問題分析

Node-Redを導入する前に、上記の要件を満たすために、APISIX開発者はLuaプラグインを使用することを検討します。Apache APISIXは詳細なプラグイン開発ドキュメントを提供していますが、ビジネス開発者はLuaの構文とチューニング技術を学び、APISIXが公開するさまざまなリクエストフックを理解し、パラメータの抽出と検証のロジックを書きながらプラグインを継続的にリロードして検証する必要があります。テストが完了した後、LuaプラグインをAPISIXプログラムにパッケージ化するか、すべてのAPISIXインスタンスに配布してマウントする必要があります。

このブログで提供する例の要件は、クライアントリクエストから特定のパラメータを解析し、異なる上流サービスからデータを取得するためのリクエストを構築することです。しかし、ビジネス記述以外のトランザクションに多くの時間を費やしました。そのため、パラメータ変換、フォーマット変換、または外部呼び出しを含むこのようなロジックに対して、より軽量で直感的なアプローチを採用できます。これはまさにNode-Redが解決できる問題です。

Node-Redの紹介

Node-REDは、さまざまなドメインにわたる自動化およびデータフロー処理タスクに適した、強力で使いやすいフローベースのプログラミングツールです。そのプログラミングインターフェース、豊富なノードライブラリ、柔軟な拡張性により、複雑なフローを迅速に構築し、さまざまなアプリケーションシナリオを実装できます。以下は、Node-REDが提供するいくつかのノードです:

  • HTTP_INノード: 外部サービス呼び出しのためのエンドポイントを公開し、APISIXのアップストリームサービスとして使用します。

  • Functionノード: 開発者がJavaScriptでコード関数を記述し、入力/出力の変更、削除などを行えるようにします。

  • Switchノード: 開発者が一連の条件を設定し、条件が満たされたときに次の指定されたノードに入ることを可能にします。

  • HTTP_Requestノード: URLなどを設定し、ワークフロー全体を実行するときにこのエンドポイントにデータを送信できます。

  • Changeノード: 指定されたオブジェクトの指定された値を追加、変更、または削除できます。

  • HTTP_Responseノード: クライアントにレスポンスを返すために使用されます。

上記のノードに加えて、Node-REDは他にも多くの組み込みノードを提供しています。この記事では、Node-REDを使用して上記の要件を実装する方法を読者に示します。

例のデモンストレーション

環境設定

コンテナ化を通じて必要なコンポーネントをデプロイし、DigitalOcean Dropletをサーバーリソースとして使用します。

$ doctl compute ssh-key list

ID          Name             FingerPrint
25621060    Zhiyuan Ju       2c:84:b7:d8:14:0a:a0:0f:ca:fe:ca:24:06:a4:fe:39

$ doctl compute droplet create \
    --image docker-20-04 \
    --size s-2vcpu-4gb-amd \
    --region sgp1 \
    --vpc-uuid 646cf2b8-03d8-4f48-b7c8-57cdee60ad27 \
    --ssh-keys 25621060 \
    apisix-nodered-docker-ubuntu-s-2vcpu-4gb-amd-sgp1-01
  
$ doctl compute droplet list

ID           Name                                                    Public IPv4       Private IPv4    Public IPv6    Memory    VCPUs    Disk    Region    Image                                   VPC UUID                                Status    Tags    Features                            Volumes
404094941    apisix-nodered-docker-ubuntu-s-2vcpu-4gb-amd-sgp1-01    143.198.192.64    10.104.0.3                     4096      2        80      sgp1      Ubuntu Docker 25.0.3 on Ubuntu 22.04    646cf2b8-03d8-4f48-b7c8-57cdee60ad27    active            droplet_agent,private_networking

Apache APISIXのデプロイ

APISIX Quickstartを使用して新しいAPISIXインスタンスを起動します。詳細なドキュメントはhttps://docs.api7.ai/apisix/getting-started/を参照してください。

$ curl -sL https://run.api7.ai/apisix/quickstart | sh

Node-REDのデプロイ

Node-REDは複数のデプロイ方法を提供しており、Dockerを介して迅速にデプロイし、既存の環境と統合します。詳細なデプロイ方法については、公式ドキュメントを参照してください:https://nodered.org/docs/getting-started/

Node-REDをデプロイする際、コンテナがAPISIXネットワークに追加されていることを確認し、APISIXと通信してリクエストを処理できるようにします。

$ docker run -d -it -p 1880:1880 -v $PWD/configs/nodered/data:/data --network=apisix-quickstart-net --name mynodered -u Node-Red:dialout nodered/Node-Red

Node-REDの設定

  1. APISIXからNode-REDに入るリクエストを処理するために、Node-Redはリクエスト内のパラメータが存在し有効かどうかを確認する必要があります。パラメータが欠落しているか無効な場合、エラーメッセージが返されます。有効な場合、次のノードが実行されます。この特定のシナリオでは、ストックホルム(city=stockholm)とベルリン(city=berlin)の2つの都市のデータクエリのみを許可します。

Node-Redを使用してパラメータを確認

  1. リクエストが次のノードに入ると、Node-REDはリクエストされたデータのタイプを決定する必要があります。このシナリオでは、天気情報(scope=weather)、都市が位置する国の情報(scope=country)、都市が位置する国のGDP(scope=gdp)の3つのタイプがあります。

Node-Redを使用してデータタイプをターゲット

  1. CityScopeパラメータの両方が有効な場合、Node-REDはscopeの値に基づいてデータを取得するAPIを決定します。URL、Method、Payload、X-API-Keyなどを設定した後、Node-REDのノードはトリガーされたときに対応するエンドポイントにアクセスしてデータを取得します。

scopeに応じてデータを取得するAPIを選択

  1. scope=gdpのデータを取得する場合、Node-REDは外部APIのレスポンスボディからGDPの値を抽出する必要があります。これはChangeノードを使用して抽出するか、Functionノードを使用して行うことができます。

外部APIのレスポンスボディからGDPの値を解析

  1. 最後に、Node-REDは処理されたデータをAPISIXに返し、APISIXはそれをクライアントに渡します。最終的なNode-REDの図を以下に示します。

APISIXとNode-Redを組み合わせたフローチャート

APISIXルートの作成

Node-Redサービスをクライアントに利用可能にするために、APISIXをNode-Redが公開するエンドポイントのリバースプロキシとして使用する必要があります。以下はその手順です:

  1. APISIXルートを作成し、mynodered:1880をこのルートのアップストリームとして設定します。これにより、このエンドポイントに送信されたすべてのリクエストがNode-Redサービスに転送されます。

  2. Key Authenticationを有効にして、有効なAPI Keyを持つリクエストのみが認証を通過し、Node-Redサービスにアクセスできるようにします。

上記の手順に従うことで、Node-Redサービスをクライアントに安全に公開し、認証されたユーザーのみがアクセスできるようにすることができます。

$ curl -i "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '
{
  "id": "proxy-global-data-endpoint",
  "uri": "/global-data",
  "upstream": {
    "type": "roundrobin",
    "nodes": {
      "mynodered:1880": 1
    }
  },
  "plugins": {
    "key-auth": {}
  }
}'

$ curl -i "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT -d '
{
  "username": "tom",
  "plugins": {
    "key-auth": {
      "key": "secret-key"
    }
  }
}'

リクエスト検証

APISIXとNode-Redが期待通りに動作するかどうかを確認するために、いくつかのシナリオを個別に試します:

シナリオ1

  • シナリオ説明: 不正なKeyでAPIにアクセスします。

  • 期待される結果: 提供されたAPI Keyが不正であるため、リクエストは拒否され、対応するエラーメッセージが返されます。

$ curl http://143.198.192.64:9080/global-data -H "apikey: invalid-key" -i

HTTP/1.1 401 Unauthorized
Date: Mon, 04 Mar 2024 07:47:24 GMT
Content-Type: text/plain; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Server: APISIX/3.8.0

{"message":"Invalid API key in request"}

シナリオ2

  • シナリオ説明: 正しいKeyでAPIにアクセスしますが、無効なCityフィールドを使用します。

  • 期待される結果: リクエストパラメータが要件を満たしていないため、対応するエラーメッセージが返され、Cityフィールドが無効であることが示されます。

$ curl "http://143.198.192.64:9080/global-data?city=singapore&scope=country" -H "apikey: secret-key" -i

HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
Content-Length: 69
Connection: keep-alive
Access-Control-Allow-Origin: *
X-Content-Type-Options: nosniff
ETag: W/"45-IOhgB2XkDHi2Kt4PP42n1xa8Gys"
Date: Mon, 04 Mar 2024 07:48:02 GMT
Server: APISIX/3.8.0

{"errorCode":400,"message":"Allowed city Options: Stockholm, Berlin"}

シナリオ3

  • シナリオ説明: 正しいKeyでAPIにアクセスし、有効なCityScopeフィールドを使用して国のデータを取得します。

  • 期待される結果: リクエストは成功し、Cityが位置する国の関連情報が返されます。

$ curl "http://143.198.192.64:9080/global-data?city=stockholm&scope=country" -H "apikey: secret-key" -i

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 947
Connection: keep-alive
Access-Control-Allow-Origin: *
X-Content-Type-Options: nosniff
ETag: W/"3b3-XDlm9OHfuUrWH+g42q8L1F2uu/o"
Date: Mon, 04 Mar 2024 07:48:26 GMT
Server: APISIX/3.8.0

[{"gdp":556086,"sex_ratio":100.4,"surface_area":438574,"life_expectancy_male":80.8,"unemployment":6.7,"imports":158710,"homicide_rate":1.1,"currency":{"code":"SEK","name":"Swedish Krona"},"iso2":"SE","employment_services":80.7,"employment_industry":17.7,"urban_population_growth":1.1,"secondary_school_enrollment_female":157.9,"employment_agriculture":1.6,"capital":"Stockholm","co2_emissions":37.6,"forested_area":68.9,"tourists":7440,"exports":160538,"life_expectancy_female":84.4,"post_secondary_enrollment_female":82.1,"post_secondary_enrollment_male":52.7,"primary_school_enrollment_female":127.4,"infant_mortality":2,"gdp_growth":2.2,"threatened_species":98,"population":10099,"urban_population":87.7,"secondary_school_enrollment_male":148.1,"name":"Sweden","pop_growth":0.7,"region":"Northern Europe","pop_density":24.6,"internet_users":92.1,"gdp_per_capita":55766.8,"fertility":1.8,"refugees":310.4,"primary_school_enrollment_male":125.8}]

シナリオ4

  • シナリオ説明: 正しいKeyでAPIにアクセスし、有効なCityScopeフィールドを使用してGDPデータを取得します。

  • 期待される結果: リクエストは成功し、Cityが位置する国のGDPデータが返されます。

$ curl "http://143.198.192.64:9080/global-data?city=stockholm&scope=gdp" -H "apikey: secret-key" -i

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 6
Connection: keep-alive
Access-Control-Allow-Origin: *
ETag: W/"6-j8I5kokycgWjCeKC1c2UfJW7AQY"
Date: Mon, 04 Mar 2024 07:48:48 GMT
Server: APISIX/3.8.0

556086

これらの4つのシナリオを検証することで、APISIXとNode-Redが期待通りに動作し、さまざまなタイプのリクエストを正しく処理できることを確認できます。

まとめ

私たちは、カスタム機能開発の問題をより巧妙に解決するための新しいアプローチを提供し、詳細な例を示しました。

  1. APIリクエストルーティングと認証: まず、Apache APISIXのルーティング機能と認証プラグインを利用し、提供された資格情報が有効な場合、APISIXはクライアントリクエストをNode-Redサービスに転送します。

  2. リクエスト処理と変換: Node-Redでは、受信したAPIリクエストを処理するためのフローを作成します。HTTP入力ノードを使用してAPISIXからのリクエストを受信し、リクエストパラメータを解析して検証し、ビジネス要件を満たしていることを確認します。

  3. ビジネスロジック処理: 有効なリクエストを受信した後、Node-Redでビジネスロジックを実行できます。例えば、パラメータに基づいて異なるビジネスAPIにリクエストを送信してデータを取得し、レスポンスから必要なフィールドを抽出します。これらの操作が完了した後、最終的なデータがAPISIXに返されます。

  4. エラーハンドリングとロギング: 処理中にエラーや例外が発生した場合、Node-Redにエラーハンドリングノードを追加して例外状況をキャプチャし処理できます。さらに、ロギングノードを使用して処理中のキー情報を記録し、後続のトラブルシューティングと分析に役立てることができます。この例ではこれを示していません。

APISIXとNode-Redを組み合わせることで、リクエストルーティング、データ処理、ビジネスロジックなどを含む完全なリクエスト処理プロセスを視覚的に実装できます。複雑なコードやプラグインを書く必要はありません。この柔軟でカスタマイズ可能なソリューションは、システム機能を迅速に構築および調整し、開発効率を向上させ、開発コストを削減し、システムの安定性と拡張性を確保するのに役立ちます。

Tags: