APISIX 用 Lua と ChatGPT によるカスタムプラグイン開発

Bobur Umurzokov

Bobur Umurzokov

June 12, 2023

Technology

Apache APISIXの主要な特徴の1つは、プラグインを通じた拡張性です。APISIXでは、独自のカスタムプラグインを構築して、追加の機能を追加し、APIトラフィックをより効率的に管理することができます。多くの場合、新しいプラグインを実装するためにLuaプログラミング言語を使用したり、プラグインランナーを活用して好きなプログラミング言語でプラグインを開発したりします。しかし、APISIXはLuaに対して最も優れたサポートを提供しています。LuaでAPISIXのプラグインをいくつか書いてみた後、Luaプログラミングの基礎を知る必要がないこと、またはこの言語のエキスパートである必要がないことに気づきました。特に、ChatGPTの友達が常に一緒にいる場合です。例えば、**JavaやC#**のバックグラウンドを持つ私にとって、Luaで書かれたコードやロジックを理解することは可能であり、あなたも同じことができると信じています。

この記事では、LuaとChatGPTを使用してAPISIX用の新しいカスタムプラグインfile-proxyを開発するプロセスをガイドします(ChatGPTを使ってLuaコードを書いてもらいます)。このプラグインは、APIを通じて静的ファイルを公開し、指定されたURLからファイルを取得するために使用されます。

APISIXはNginxの既存の機能を強化するために構築され、NginxはAPISIXが活用する再利用可能なLuaモジュールのコレクションを提供しています。

学習目標

この記事を通じて、以下のことを学びます:

  • 新しいfile-proxyプラグインを開発する方法。
  • ChatGPTを効率的に使用してLuaコードを生成する方法。
  • APISIX用の独自プラグインを構築する手順。

新しいfile-proxyプラグインのユースケース

実際のプラグイン実装に飛び込む前に、なぜこのプラグインが必要なのかを理解しましょう。この記事を書いている時点では、APISIXには同様のケースに対応する組み込みプラグインが提供されていないかもしれません。そのため、新しいプラグインを構築します。多くの場合、静的ファイル(Yaml、JSON、JavaScript、CSS、または画像ファイル)をAPIを通じて公開したいと考えます。

例えば、APISIX API Gatewayは、アプリケーションのフロントドアとして機能し、着信リクエストを複数のAPIエンドポイントにルーティングします。これは、すべてのサーバーURL、パス、パラメータ、各APIエンドポイントの説明、およびその入力と出力を定義する適切な場所です。そして、APIをドキュメント化するためにOpenAPI仕様を作成します。OpenAPI .yamlファイルは、APIユーザーがAPIを理解し、操作するための地図のようなものです。プラグインにopenapi.yamlファイルのパス(サーバー内の保存場所)を提供することで、APIゲートウェイを通じてファイルを直接取得し、API利用者に一貫したインターフェースを提供できます。これにより、APIユーザーは指定されたURL(https://example.com/openapi.yaml)で.yamlファイルにアクセスできます。

他にも、このfile-proxyプラグインをシンプルなコンテンツデリバリーネットワーク(CDN)の代替として使用することを考えるかもしれません。小規模なアプリケーションがあり、本格的なCDNを使用したくない場合、特定の場所から静的ファイルを提供するためにfile-proxyプラグインを使用できます。file-proxyプラグインは、ファイルのキャッシュレイヤーとしても使用できます。取得や生成にコストがかかるファイルがある場合、プラグインを使用してファイルを一度取得し、その後のリクエストに対してキャッシュされたバージョンを提供できます。

file-proxyプラグインを開発する手順

APISIXをローカルで実行し、API Gatewayをhttp://localhost:9080でホストします。開発が完了したら、サーバーや任意のクラウドプロバイダーにデプロイできます。基本的に、openapi.yamlファイルをhttp://localhost:9080/openapi.yamlパスに配置したいと考えます。これを実現する方法を学びます。

前提条件

  • 開始する前に、APISIXの基本的な理解があると良いでしょう。APIゲートウェイとその主要な概念(ルートアップストリームAdmin APIプラグイン)に慣れていることが望ましいです。HTTPプロトコルの基本的な理解も役立ちます。
  • Dockerを使用して、コンテナ化されたetcdとAPISIXをインストールします。
  • curlを使用して、APISIX Admin APIにリクエストを送信します。Postmanなどのツールを使用してAPIとやり取りすることもできます。

デモプロジェクトとファイルの理解

GitHub上の既存のfile-proxyデモプロジェクトを活用します。これは、既存のApisix docker exampleリポジトリと非常に似た構造を持っていますが、デモをシンプルにするために不要なファイルを削除しています。プロジェクトには3つのフォルダ、docker-compose.yml、およびサンプルのopenapi.yamlファイルがあります。

  • docker-compose.ymlは、APISIX用とetcd用(APISIXの設定ストレージ)の2つのコンテナを定義します。
  • custom-pluginsフォルダには、Luaで実装されたfile-proxyプラグインがあります。次のセクションでこれを確認します。
  • openapi.yamlは、公開するサンプルのOpenAPI仕様です。

file-proxyプラグインの実装

まず、ChatGPTにAPISIX用のカスタムfile-proxyプラグインをLuaで実装する方法を尋ねます。ChatGPTは、実際の実装とほぼ同じガイドを生成しますが、回答は抽象的すぎるため、プロセスに従うと動作しないプラグインになってしまいます。しかし、有用なLuaコードを抽出するのに役立ちます。プラグイン開発の実際のプロセスを知っていれば、両方の知識を実践で組み合わせることが容易になります。

ChatGPTを使用してfile-proxyプラグインを実装

1. Luaファイルの作成

プロジェクトの/custom-pluginsディレクトリに新しい空のLuaファイルを作成します。ファイル名はプラグインの名前と同じにする必要があります。例えば、プラグインがfile-proxyという名前の場合、file-proxy.luaというファイルを作成します。

2. APISIXにプラグインを登録

APISIXはこのプラグインファイルがどこにあるかを知り、それに応じてプラグインを実行できる必要があります。そのため、まずfile-proxy.luaファイルのパスをAPISIXのextra_lua_path属性に追加して、APISIXがファイルを見つけられるようにします。これはconfig.yamlで行います。

apisix:
  extra_lua_path: "/opt/?.lua"
  node_listen: 9080

なぜファイルパスが/opt/?.luaに設定されているのか疑問に思うかもしれません。これは、dockerを使用してAPISIXを実行しているためです。docker-compose.ymlファイルに3つのボリュームがあることに気づくでしょう。./custom-plugins:/opt/apisix/plugins:ro

volumes:
      - ./apisix_conf/config.yaml:/usr/local/apisix/conf/config.yaml:ro
      - ./openapi.yaml:/usr/local/apisix/conf/openapi.yaml:ro
      - ./custom-plugins:/opt/apisix/plugins:ro

これにより、ローカルディレクトリ./custom-plugins(カスタムプラグインの実装を含むfile-proxy.luaファイルがある場所)が、dockerコンテナ内の/opt/apisix/pluginsパスに読み取り専用ボリュームとしてマウントされます。これにより、カスタムプラグインがAPISIXに追加され、Docker内の別のパス(/opt/?.lua内)で実行できるようになります。同様に、他の2つのファイルもDockerフォルダにコピーされます。

次のステップとして、プラグインをAPISIXのプラグインリストで有効にします。これは、APISIX設定ファイル(config.yaml)のpluginsリストにプラグイン名を追加することで行います:

plugins:
  - file-proxy

このアクションは、config-default.yamlで指定されているすべてのデフォルトプラグインを上書きすることに注意してください。カスタムプラグインを他のプラグインと組み合わせて使用したい場合は、手動で他のプラグインを名前で追加する必要があります。

3. file-proxyプラグインのLuaコードの解説

これまで、単に何もしないプラグインを登録しました。次に、それを実装します。プラグインのロジックはLua関数として実装されます。**file-proxy.lua**でどのように行われているかを確認できます。

**file-proxy.lua**ファイルを分解して、コードの構造とフローを理解し、自分で新しいプラグインを作成するのに役立てましょう。ChatGPTにLuaコードを説明するように依頼できます:

ChatGPTを使用してLuaコードを説明

実際、コードの説明はかなり良いものでした(部分的にChatGPTによって書かれたためです)。

画像の説明

このコードの重要な部分を説明しますので、迷子になったり、AIに完全に依存したりしないようにします。

4. プラグインファイルの構造

すべてのプラグインLuaファイルは以下の構造を持つ必要があります:

1. モジュール: プラグインに必要なモジュール/ライブラリをインポートします。

local core = require("apisix.core")
...

2. プラグイン名: すべてのプラグインには一意の名前があり、Luaファイル名と同じにすることができます。

local plugin_name = "file-proxy"

3. プラグインスキーマ: すべてのプラグインにはプラグインスキーマがあり、通常はプラグインへの入力を指定します。入力はAPISIXルート設定から渡されますが、これは後でプラグインをテストする際に確認できます。file-proxyプラグインの場合、プラグインはファイルを読み取り、レスポンスを返すためにファイルパスが必要なので、パラメータはpathで、文字列型です。スキーマは、他のプログラミング言語でのメソッド宣言とパラメータのように理解できます。

local plugin_schema = {
    type = "object",
    properties = {
        path = {
            type = "string" -- 提供するファイルのパス
        },
    },
    required = {"path"} -- pathは必須フィールド
}

4. プラグイン定義: プラグイン実装の非常に重要な部分で、versionprioritynameschemaのプロパティを持つテーブルとして定義します。nameschemaは、先に定義したプラグインの名前とスキーマです。versionpriorityは、APISIXがプラグインを管理するために使用されます。バージョンは通常、現在使用されているバージョンを指します(APIのバージョニングのようなものです)。プラグインロジックを公開して更新する場合、1.1になるかもしれません(任意のバージョンを設定できます)。ただし、優先度を選択する際には非常に注意が必要です。priorityフィールドは、プラグインが実行される順序とフェーズを定義します。例えば、優先度が3000の'ip-restriction'プラグインは、優先度が0の'example-plugin'よりも前に実行されます。これは、'ip-restriction'プラグインの優先度が高いためです。独自のプラグインを開発する場合、既存のプラグインの順序を乱さないように注意してください。既存のプラグインの順序はconfig-default.yamlファイルで確認でき、Apache APISIX Plugin Development Guideを開いて決定できます。

local _M = {
    version = 1.0,
    priority = 1000,
    name = plugin_name,
    schema = plugin_schema
}

5. スキーマチェック: check_schema Lua関数は、ルート設定(後でテストセクションで確認します)のプラグインを、先に定義したプラグインスキーマに対して検証するために使用されます。

-- プラグイン設定が正しいかどうかをチェックする関数
function _M.check_schema(conf)
  -- 設定をスキーマに対して検証
  local ok, err = core.schema.check(plugin_schema, conf)
  -- 検証が失敗した場合、falseとエラーを返す
  if not ok then
      return false, err
  end
  -- 検証が成功した場合、trueを返す
  return true
end

6. プラグインロジック: **access**関数は、主要なプラグインロジックを記述するコア関数です。これはNginxリクエスト処理パイプラインのアクセスフェーズ中に呼び出され、トラフィックを制御し、カスタム命令を記述します。file-proxyの場合、プラグイン設定で指定されたファイルを開き、その内容を読み取り、レスポンスとして内容を返す必要があります。ファイルを開けない場合、エラーをログに記録し、404 Not Foundステータスを返します。これは、ChatGPTにこの作業を任せる正確な場所です:

ChatGPTを使用してfile-proxyプラグインのLuaコードを実装

コードを構造化してリファクタリングした後、以下のようになります:

function _M.access(conf, ctx)
  -- 設定で指定されたファイルを開く
  local fd = io.open(conf.path, "rb")
  -- ファイルが正常に開かれた場合、その内容を読み取り、レスポンスとして返す
  if fd then
    local content = fd:read("*all")
    fd:close()
    ngx.header.content_length = #content
    ngx.say(content)
    ngx.exit(ngx.OK)
  else
    -- ファイルを開けない場合、エラーをログに記録し、404 Not Foundステータスを返す
    ngx.exit(ngx.HTTP_NOT_FOUND)
    core.log.error("File is not found: ", conf.path, ", error info: ", err)
  end
end

7. ロギングロジック: プラグイン設定をログに記録することは常に望ましいです。これにより、プラグインが期待通りに動作しているかどうかをデバッグして確認できます。プラグインへのリクエストとレスポンスをログに記録できます。

-- ログフェーズ中に呼び出される関数
function _M.log(conf, ctx)
    -- プラグイン設定とリクエストコンテキストをログに記録
    core.log.warn("conf: ", core.json.encode(conf))
    core.log.warn("ctx: ", core.json.encode(ctx, true))
end

Apache APISIXのインストール

カスタムfile-proxyプラグインを開発し、APISIXに登録する方法を学びました。次に、プラグインをテストします。apisix-file-proxy-plugin-demoプロジェクトをフォーク/クローンした後、プロジェクトのルートフォルダからdocker compose upを実行して簡単にインストールできます。

file-proxyプラグインを使用したルートの作成

新しいfile-proxyプラグインを使用してテストするために、APISIXでプラグインを使用するルートを作成する必要があります:

curl "http://127.0.0.1:9180/apisix/admin/routes/open-api-definition" -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
   "name":"OpenAPI Definition",
   "desc":"Route for OpenAPI Definition file",
   "uri":"/openapi.yaml",
   "plugins":{
      "file-proxy":{
         "path":"/usr/local/apisix/conf/openapi.yaml"
      }
   }
}'

ChatGPTに上記の設定を説明するように依頼できます:

ChatGPTを使用してLuaコードを説明

プラグインのテスト

次に、ルートにcURLリクエストを送信するか、ブラウザでリンクhttp://127.0.0.1:9080/openapi.yamlを開きます。レスポンスは、指定されたURLにある`openapi.yaml`ファイルの内容であるはずです。

curl -i http://127.0.0.1:9080/openapi.yaml

プラグインは期待通りに動作します。このプラグイン設定により、指定されたルートを使用して任意のファイルにアクセスできるようになります。

まとめ

Luaを使用してAPISIX用のカスタムプラグインを開発することは、APIゲートウェイの機能を拡張する強力な方法です。この記事では、file-proxyプラグインを作成する方法をデモンストレーションし、プラグイン定義とスキーマを定義し、プラグイン設定を検証し、APISIXのリクエスト処理パイプラインのアクセスフェーズとログフェーズ中にカスタムロジックを実装しました。ChatGPTは、このプログラミング言語の知識が不足している部分を補うためにLuaコードを書くのに役立ちました。楽しいコーディングを!

関連リソース

Tags: