Benutzerdefinierte Plugin-Entwicklung für APISIX mit Lua und ChatGPT

Bobur Umurzokov

Bobur Umurzokov

June 12, 2023

Technology

Eine der Schlüsselfunktionen von Apache APISIX ist seine Erweiterbarkeit durch Plugins. APISIX ermöglicht es Ihnen, Ihr eigenes benutzerdefiniertes Plugin zu erstellen, um zusätzliche Funktionen hinzuzufügen und den API-Verkehr effizienter zu verwalten. Oft verwenden Sie die Programmiersprache Lua, um neue Plugins zu implementieren, oder nutzen Plugin-Runner, um Plugins in Ihrer bevorzugten Programmiersprache zu entwickeln. Allerdings bietet APISIX die beste Unterstützung für Lua. Nachdem ich ein paar Plugins für APISIX in Lua geschrieben hatte, wurde mir klar, dass man die Grundlagen der Lua-Programmierung nicht unbedingt kennen oder ein Experte in dieser Sprache sein muss, wenn Ihr ChatGPT-Freund immer an Ihrer Seite ist. Mit meinem Hintergrund in Java und C# kann ich den in Lua geschriebenen Code und die Logik verstehen, und ich bin überzeugt, dass Sie das auch können.

Dieser Artikel führt Sie durch den Prozess der Entwicklung eines neuen benutzerdefinierten Plugins namens file-proxy für APISIX mit Lua und ChatGPT (wir verwenden es, um etwas Lua-Code für uns zu schreiben). Dieses Plugin wird verwendet, um statische Dateien über eine API bereitzustellen und eine Datei von einer bestimmten URL abzurufen.

APISIX wurde entwickelt, um die bestehenden Funktionen von Nginx zu erweitern, und Nginx bietet eine Sammlung wiederverwendbarer Lua-Module, die APISIX nutzt.

Lernziele

Im Laufe des Artikels werden Sie Folgendes lernen:

  • Wie man ein neues file-proxy-Plugin entwickelt.
  • Wie man ChatGPT effizient nutzt, um Lua-Code zu generieren.
  • Schritte zum Erstellen Ihres eigenen Plugins für APISIX.

Anwendungsfall für das neue file-proxy-Plugin

Bevor wir uns mit der eigentlichen Implementierung des Plugins befassen, wollen wir zunächst verstehen, warum wir dieses Plugin benötigen. Zum Zeitpunkt der Erstellung dieses Beitrags bietet APISIX möglicherweise kein integriertes Plugin für einen ähnlichen Anwendungsfall. Deshalb werden wir ein neues erstellen. Oft möchten wir eine statische Datei (Yaml, JSON, JavaScript, CSS oder Bilddateien) über eine API bereitstellen.

Zum Beispiel fungiert das APISIX API Gateway als Eingangstür in Ihrer Anwendung, um eingehende Anfragen an mehrere API-Endpunkte zu routen. Es ist der richtige Ort, um alle Server-URLs, Pfade, Parameter, Beschreibungen der einzelnen API-Endpunkte sowie deren Eingaben und Ausgaben zu definieren. Und Sie erstellen OpenAPI-Spezifikationen, um die API zu dokumentieren. Die OpenAPI .yaml-Datei ist wie eine Karte, die Ihre API-Nutzer dabei unterstützt, Ihre API zu verstehen und mit ihr zu interagieren. Indem Sie den Pfad der openapi.yaml-Datei (wo sie auf Ihrem Server gespeichert ist) an das Plugin übergeben, können Sie die Datei direkt über Ihr API-Gateway abrufen und bereitstellen, wodurch eine konsistente Schnittstelle für API-Nutzer geschaffen wird. Dann können Ihre API-Nutzer auf die .yaml-Datei unter der angegebenen URL (https://example.com/openapi.yaml) zugreifen.

Es gibt auch andere Anwendungsfälle, bei denen Sie dieses file-proxy-Plugin als einfachen Ersatz für ein Content Delivery Network (CDN) verwenden könnten. Wenn Sie eine Anwendung im kleinen Maßstab haben und kein vollwertiges CDN verwenden möchten, können Sie das file-proxy-Plugin verwenden, um statische Dateien von einem bestimmten Ort bereitzustellen. Das file-proxy-Plugin kann auch als Caching-Schicht für Dateien verwendet werden. Wenn Sie Dateien haben, deren Abruf oder Generierung aufwendig ist, können Sie das Plugin verwenden, um die Datei einmal abzurufen und dann die zwischengespeicherte Version für nachfolgende Anfragen bereitzustellen.

Schritte zur Entwicklung des file-proxy-Plugins

Wir werden APISIX lokal ausführen, und unser API-Gateway wird auf http://localhost:9080 gehostet. Wenn die Entwicklung abgeschlossen ist, können Sie es auf Ihrem Server oder bei einem Cloud-Anbieter bereitstellen. Im Grunde möchten wir eine Datei openapi.yaml unter dem Pfad http://localhost:9080/openapi.yaml platzieren. Sie werden lernen, wie Sie dies erreichen können.

Voraussetzungen

  • Bevor Sie beginnen, ist es gut, ein grundlegendes Verständnis von APISIX zu haben. Vertrautheit mit API-Gateways und deren Schlüsselkonzepten wie Routes, Upstreams, Admin API und Plugins. Ein grundlegendes Verständnis des HTTP-Protokolls ist ebenfalls von Vorteil.
  • Docker wird verwendet, um den containerisierten etcd und APISIX zu installieren.
  • curl wird verwendet, um Anfragen an die APISIX Admin API zu senden. Sie können auch Tools wie Postman verwenden, um mit der API zu interagieren.

Verstehen des Demo-Projekts und der Dateien

Wir werden das bestehende file-proxy-Demo-Projekt auf GitHub nutzen. Es hat eine ähnliche Struktur wie das bestehende Apisix Docker-Beispiel-Repo, nur dass wir unnötige Dateien entfernt haben, um das Demo einfach zu halten. Das Projekt hat 3 Ordner, docker-compose.yml und Beispiel-openapi.yaml-Dateien.

  • docker-compose.yml definiert zwei Container, einen für APISIX und einen für etcd (das Konfigurationsspeicher für APISIX).
  • Der Ordner custom-plugins enthält die Implementierung des file-proxy-Plugins in Lua. Wir werden es in den folgenden Abschnitten überprüfen.
  • openapi.yaml ist nur eine Beispiel-OpenAPI-Spezifikation, die wir bereitstellen.

Implementierung des file-proxy-Plugins

Wir beginnen damit, ChatGPT zu fragen, wie man ein benutzerdefiniertes file-proxy-Plugin für APISIX in Lua implementiert. ChatGPT generiert eine Anleitung, die der tatsächlichen Implementierung fast ähnlich ist, aber die Antwort ist zu abstrakt, und wenn Sie dem Prozess folgen, werden Sie am Ende mit einem nicht funktionierenden Plugin dastehen. Es hilft uns jedoch, nützlichen Lua-Code zu extrahieren. Wenn wir den tatsächlichen Prozess der Plugin-Entwicklung kennen, wird es einfacher, beide Wissensbereiche in der Praxis zu kombinieren.

Implementierung des file-proxy-Plugins mit ChatGPT

1. Erstellen einer Lua-Datei

Wir erstellen eine neue leere Lua-Datei im Verzeichnis /custom-plugins des Projekts. Der Name der Datei sollte der Name unseres Plugins sein. Wenn Ihr Plugin beispielsweise file-proxy heißt, sollten Sie eine Datei namens file-proxy.lua erstellen.

2. Registrierung des Plugins in APISIX

APISIX muss wissen, wo sich diese Plugin-Datei befindet, und in der Lage sein, das Plugin entsprechend auszuführen. Dazu sollten wir zunächst den Dateipfad definieren, unter dem APISIX die file-proxy.lua-Datei findet, indem wir den Dateipfad zum Attribut extra_lua_path von APISIX in der config.yaml** hinzufügen.

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

Jetzt fragen Sie sich vielleicht, warum der Dateipfad auf /opt/?.lua gesetzt ist. Weil wir APISIX mit Docker ausführen. Sie werden dies in der docker-compose.yml-Datei bemerken, wo es 3 Volumes gibt: ./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

Dies bindet das lokale Verzeichnis ./custom-plugins, in dem sich unsere file-proxy.lua-Datei mit der benutzerdefinierten Plugin-Implementierung befindet, als schreibgeschütztes Volume im Docker-Container unter dem Pfad /opt/apisix/plugins ein. Dies ermöglicht es, das benutzerdefinierte Plugin zur Laufzeit zu APISIX hinzuzufügen, indem es in Docker unter einem anderen Pfad, nämlich /opt/?.lua, platziert wird. Ebenso haben wir die anderen beiden Dateien in Docker-Ordner kopiert.

Der nächste Schritt besteht darin, das Plugin in der Liste der APISIX-Plugins zu aktivieren. Dies geschieht, indem der Plugin-Name zur plugins-Liste in der APISIX-Konfigurationsdatei (config.yaml) hinzugefügt wird:

plugins:
  - file-proxy

Beachten Sie, dass diese Aktion alle vorhandenen Standard-Plugins, die in config-default.yaml angegeben sind, überschreibt. Sie müssen andere Plugins manuell nach ihrem Namen hinzufügen, wenn Sie Ihr benutzerdefiniertes Plugin in Kombination mit ihnen verwenden möchten.

3. Lua-Code des file-proxy-Plugins im Detail

Bisher haben wir nur das Plugin registriert, das einfach nichts tut. Es ist an der Zeit, es zu implementieren. Die Plugin-Logik wird als Lua-Funktionen implementiert. Sie können überprüfen, wie dies in file-proxy.lua geschieht.

Lassen Sie uns die file-proxy.lua-Datei aufschlüsseln, um die Struktur des Codes und den Ablauf besser zu verstehen, was Ihnen hilft, eigene Plugins zu erstellen. Sie können ChatGPT einfach bitten, den Lua-Code zu erklären:

Erklärung des Lua-Codes mit ChatGPT

Tatsächlich haben wir eine ziemlich gute Erklärung des Codes erhalten (weil er teilweise von ChatGPT geschrieben wurde).

Bildbeschreibung

Ich werde Sie nur durch die wichtigen Teile dieses Codes führen, damit Sie nicht verloren gehen oder sich vollständig auf KI verlassen, um Ihre Plugins zu schreiben.

4. Struktur der Plugin-Datei

Jede Plugin-Lua-Datei sollte die folgende Struktur haben:

1. Module: Sie importieren die notwendigen Module/Bibliotheken, die wir für das Plugin benötigen.

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

2. Plugin-Name: Jedes Plugin hat einen eindeutigen Namen, der mit dem Namen unserer Lua-Datei übereinstimmen kann.

local plugin_name = "file-proxy"

3. Plugin-Schema: Jedes Plugin hat ein Plugin-Schema, in dem wir normalerweise die Eingaben für das Plugin angeben. Die Eingabe, die wir von der APISIX-Routenkonfiguration übergeben, werden Sie später sehen, wenn wir das Plugin testen. Für das file-proxy-Plugin benötigt das Plugin einen Dateipfad, um die Datei zu lesen und eine Antwort zurückzugeben, daher ist unser Parameter der path, der vom Typ String ist. Sie können das Schema wie eine Methodendeklaration mit Parametern in anderen Programmiersprachen verstehen.

local plugin_schema = {
    type = "object",
    properties = {
        path = {
            type = "string" -- Der Pfad der Datei, die bereitgestellt werden soll
        },
    },
    required = {"path"} -- Der Pfad ist ein erforderliches Feld
}

4. Plugin-Definition: Es ist ein wirklich wichtiger Teil der Plugin-Implementierung, den wir als Tabelle mit Eigenschaften für die version, priority, name und schema definieren. Der name und das schema sind der Name und das Schema des Plugins, die wir zuvor definiert haben. Die version und priority werden von APISIX verwendet, um das Plugin zu verwalten. Die Version bezieht sich normalerweise auf die Version, die derzeit verwendet wird, wie die API-Versionierung. Wenn Sie Ihr Plugin veröffentlichen und die Logik aktualisieren, wird es 1.1 sein (Sie können jede gewünschte Version festlegen). Aber Sie müssen sehr vorsichtig bei der Wahl der Priorität sein. Das Feld priority definiert, in welcher Reihenfolge und Phase Ihr Plugin ausgeführt werden soll. Zum Beispiel wird das 'ip-restriction'-Plugin mit einer Priorität von 3000 vor dem 'example-plugin', das eine Priorität von 0 hat, ausgeführt. Dies liegt an der höheren Priorität des 'ip-restriction'-Plugins. Wenn Sie Ihr eigenes Plugin entwickeln, stellen Sie sicher, dass Sie die Reihenfolge der Plugins befolgen, um die Reihenfolge der vorhandenen Plugins nicht durcheinander zu bringen. Sie können die Reihenfolge der vorhandenen Plugins in der config-default.yaml-Datei überprüfen und den Apache APISIX Plugin Development Guide öffnen, um dies zu bestimmen.

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

5. Schema-Überprüfung: Die check_schema Lua-Funktion wird verwendet, um das Plugin in einer Routenkonfiguration (Sie werden dies bald im Testabschnitt sehen) gegen das zuvor definierte Plugin-Schema zu validieren.

-- Funktion zur Überprüfung, ob die Plugin-Konfiguration korrekt ist
function _M.check_schema(conf)
  -- Validieren Sie die Konfiguration gegen das Schema
  local ok, err = core.schema.check(plugin_schema, conf)
  -- Wenn die Validierung fehlschlägt, geben Sie false und den Fehler zurück
  if not ok then
      return false, err
  end
  -- Wenn die Validierung erfolgreich ist, geben Sie true zurück
  return true
end

6. Plugin-Logik: Die access-Funktion ist die Kernfunktion, in der wir die Hauptlogik des Plugins schreiben können. Sie wird während der Zugriffsphase der Nginx-Anfrageverarbeitungspipeline aufgerufen, und wir kontrollieren den Verkehr und schreiben benutzerdefinierte Anweisungen. Für file-proxy müssen wir die im Plugin angegebene Datei öffnen, ihren Inhalt lesen und den Inhalt als Antwort zurückgeben. Wenn die Datei nicht geöffnet werden kann, wird ein Fehler protokolliert und ein 404 Not Found-Status zurückgegeben. Es ist der genaue Ort, an dem wir diese Arbeit an ChatGPT übergeben:

Lua-Code des file-proxy-Plugins mit ChatGPT

Nachdem wir den Code strukturiert und refaktoriert haben, sieht er so aus:

function _M.access(conf, ctx)
  -- Öffnen Sie die im Plugin angegebene Datei
  local fd = io.open(conf.path, "rb")
  -- Wenn die Datei erfolgreich geöffnet wurde, lesen Sie ihren Inhalt und geben Sie ihn als Antwort zurück
  if fd then
    local content = fd:read("*all")
    fd:close()
    ngx.header.content_length = #content
    ngx.say(content)
    ngx.exit(ngx.OK)
  else
    -- Wenn die Datei nicht geöffnet werden kann, protokollieren Sie einen Fehler und geben Sie einen 404 Not Found-Status zurück
    ngx.exit(ngx.HTTP_NOT_FOUND)
    core.log.error("Datei nicht gefunden: ", conf.path, ", Fehlerinfo: ", err)
  end
end

7. Logging-Logik: Es ist immer vorzuziehen, die Plugin-Konfiguration zu protokollieren, damit wir debuggen und überprüfen können, ob das Plugin wie erwartet funktioniert. Wir können Anfragen an das Plugin und Antworten protokollieren.

-- Funktion, die während der Log-Phase aufgerufen wird
function _M.log(conf, ctx)
    -- Protokollieren Sie die Plugin-Konfiguration und den Anfragekontext
    core.log.warn("conf: ", core.json.encode(conf))
    core.log.warn("ctx: ", core.json.encode(ctx, true))
end

Installation von Apache APISIX

Nachdem wir gelernt haben, wie man unser benutzerdefiniertes file-proxy-Plugin entwickelt und in APISIX registriert, ist es an der Zeit, das Plugin zu testen. Sie können das apisix-file-proxy-plugin-demo-Projekt einfach installieren, indem Sie docker compose up aus dem Stammverzeichnis des Projekts ausführen, nachdem Sie das Projekt geforkt/geklont haben.

Erstellen einer Route mit dem file-proxy-Plugin

Um unser neues file-proxy-Plugin zu verwenden und zu testen, müssen wir eine Route in APISIX erstellen, die das Plugin verwendet:

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"
      }
   }
}'

Sie können ChatGPT bitten, die obige Konfiguration zu erklären:

ChatGPT erklärt Lua-Code

Testen des Plugins

Dann können Sie eine cURL-Anfrage an die Route senden oder den Link http://127.0.0.1:9080/openapi.yaml in Ihrem Browser öffnen. Die Antwort sollte der Inhalt der Datei openapi.yaml unter der angegebenen URL sein.

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

Das Plugin funktioniert wie erwartet. Mit dieser Plugin-Konfiguration können Sie jetzt auf beliebige Dateien über die angegebene Route zugreifen.

Zusammenfassung

Die Entwicklung benutzerdefinierter Plugins für APISIX mit Lua ist eine leistungsstarke Möglichkeit, die Funktionalität des API-Gateways zu erweitern. Wir haben in diesem Beitrag gezeigt, wie man das file-proxy-Plugin erstellt, die Plugin-Definition und das Schema definiert, die Plugin-Konfiguration validiert und benutzerdefinierte Logik während der Zugriffs- und Log-Phasen der Anfrageverarbeitungspipeline in APISIX implementiert. ChatGPT hat uns geholfen, Lua-Code für die Hauptfunktionalität zu schreiben, indem es unser fehlendes Wissen über diese Programmiersprache ergänzt hat. Viel Spaß beim Programmieren!

Verwandte Ressourcen

Tags: