Développement de plugins personnalisés pour APISIX avec Lua et ChatGPT

Bobur Umurzokov

Bobur Umurzokov

June 12, 2023

Technology

L'une des caractéristiques clés d'Apache APISIX est son extensibilité via les plugins. APISIX vous permet de créer votre propre plugin personnalisé pour ajouter des fonctionnalités supplémentaires et gérer le trafic API de manière plus efficace. Souvent, vous utilisez le langage de programmation Lua pour implémenter de nouveaux plugins ou vous pouvez exploiter les plugin runners pour développer des plugins dans votre langage de programmation préféré. Cependant, APISIX offre le meilleur support pour Lua. Après avoir écrit quelques plugins pour APISIX en Lua, j'ai compris que vous n'avez pas besoin de connaître les bases de la programmation Lua ou d'être un expert dans ce langage lorsque votre ami ChatGPT est toujours avec vous. Par exemple, avec mon expérience en Java et C#, je peux comprendre le code et la logique écrits en Lua, et je crois que vous pouvez en faire de même.

Cet article vous guidera à travers le processus de développement d'un nouveau plugin personnalisé appelé file-proxy pour APISIX en utilisant Lua et ChatGPT (nous l'utilisons pour écrire du code Lua pour nous). Ce plugin sera utilisé pour exposer des fichiers statiques via une API et récupérer un fichier à partir d'une URL spécifiée.

APISIX a été conçu pour améliorer les fonctionnalités existantes de Nginx, et Nginx fournit une collection de modules Lua réutilisables qu'APISIX exploite.

Objectifs d'apprentissage

Vous apprendrez les éléments suivants tout au long de cet article :

  • Comment développer un nouveau plugin file-proxy.
  • Comment utiliser ChatGPT efficacement pour générer du code Lua.
  • Les étapes pour créer votre propre plugin pour APISIX.

Cas d'utilisation du nouveau plugin file-proxy

Avant de plonger dans l'implémentation réelle du plugin, comprenons d'abord pourquoi nous avons besoin de ce plugin. Au moment de la rédaction de cet article, APISIX ne fournit peut-être pas de plugin intégré pour un cas similaire. C'est pourquoi nous allons en créer un nouveau. Souvent, nous voulons exposer un fichier statique (Yaml, JSON, JavaScript, CSS ou des fichiers image) via une API.

Par exemple, la passerelle API APISIX agit comme une porte d'entrée dans votre application pour router les requêtes entrantes vers plusieurs points de terminaison d'API. C'est l'endroit idéal pour définir toutes les URL des serveurs, les chemins, les paramètres, les descriptions de chaque point de terminaison d'API ainsi que leurs entrées et sorties. Et vous construisez des spécifications OpenAPI pour documenter l'API. Le fichier OpenAPI .yaml est comme une carte qui guide les utilisateurs de votre API pour comprendre et interagir avec votre API. En fournissant le chemin du fichier openapi.yaml (où il est stocké sur votre serveur) au plugin, vous pouvez récupérer et servir le fichier directement via votre passerelle API, offrant ainsi une interface cohérente pour les consommateurs de l'API. Ensuite, vos utilisateurs d'API peuvent accéder au fichier .yaml à l'URL spécifiée (https://example.com/openapi.yaml).

Il existe également d'autres cas d'utilisation, vous pourriez envisager d'utiliser ce plugin file-proxy comme un remplacement simple d'un réseau de diffusion de contenu (CDN). Si vous avez une application à petite échelle et que vous ne souhaitez pas utiliser un CDN complet, vous pouvez utiliser le plugin file-proxy pour servir des fichiers statiques à partir d'un emplacement spécifique. Le plugin file-proxy peut également être utilisé comme une couche de cache pour les fichiers. Si vous avez des fichiers coûteux à récupérer ou à générer, vous pouvez utiliser le plugin pour récupérer le fichier une fois, puis servir la version mise en cache pour les requêtes suivantes.

Étapes pour développer le plugin file-proxy

Nous allons exécuter APISIX localement et notre passerelle API sera hébergée sur http://localhost:9080. Une fois le développement terminé, vous pourrez le déployer sur votre serveur ou sur n'importe quel fournisseur de cloud. En gros, nous voulons placer un fichier openapi.yaml à l'emplacement http://localhost:9080/openapi.yaml. Vous apprendrez comment y parvenir.

Prérequis

  • Avant de commencer, il est bon d'avoir une compréhension de base d'APISIX. Une familiarité avec les concepts clés d'une passerelle API, tels que les routes, les upstreams, l'API Admin, et les plugins. Une compréhension de base du protocole HTTP est également bénéfique.
  • Docker est utilisé pour installer etcd et APISIX conteneurisés.
  • curl est utilisé pour envoyer des requêtes à l'API Admin d'APISIX. Vous pouvez également utiliser des outils comme Postman pour interagir avec l'API.

Comprendre le projet de démonstration et les fichiers

Nous allons exploiter le projet de démonstration existant file-proxy demo sur GitHub. Il a une structure assez similaire au dépôt Apisix docker example, mais nous avons supprimé les fichiers inutiles pour garder la démonstration simple. Le projet contient 3 dossiers, un fichier docker-compose.yml, et un exemple de fichier openapi.yaml.

  • docker-compose.yml définit deux conteneurs, un pour APISIX et un autre pour etcd (qui est le stockage de configuration pour APISIX).
  • Le dossier custom-plugins contient l'implémentation du plugin file-proxy en Lua. Nous l'examinerons dans les sections suivantes.
  • openapi.yaml est simplement un exemple de spécification OpenAPI que nous exposons.

Implémenter le plugin file-proxy

Nous commençons par demander à ChatGPT comment implémenter un plugin file-proxy personnalisé pour APISIX en Lua. ChatGPT génère un guide presque similaire à l'implémentation réelle, mais la réponse est trop abstraite et lorsque vous suivez le processus, vous vous retrouvez avec un plugin qui ne fonctionne pas. Cependant, cela nous aide à extraire du code Lua utile. Si nous connaissons le véritable processus de développement de plugins, il sera plus facile de combiner les deux connaissances en pratique.

Implémenter le plugin file-proxy avec ChatGPT

1. Créer un fichier Lua

Nous créons un nouveau fichier Lua vide dans le répertoire /custom-plugins du projet. Le nom du fichier doit être le nom de notre plugin. Par exemple, si votre plugin s'appelle file-proxy, vous devez créer un fichier nommé file-proxy.lua.

2. Enregistrer le plugin dans APISIX

APISIX doit savoir où se trouve ce fichier de plugin et être capable d'exécuter le plugin en conséquence. Pour ce faire, nous devons d'abord définir le chemin du fichier où APISIX trouve le fichier file-proxy.lua en ajoutant le chemin du fichier à l'attribut extra_lua_path d'APISIX dans le fichier config.yaml.

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

Maintenant, vous pouvez vous demander pourquoi le chemin du fichier est défini sur /opt/?.lua. Parce que nous exécutons APISIX en utilisant Docker. Vous pouvez le remarquer dans le fichier docker-compose.yml, il y a 3 volumes ./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

Cela monte le répertoire local ./custom-plugins où se trouve notre fichier file-proxy.lua avec l'implémentation du plugin personnalisé en tant que volume en lecture seule dans le conteneur Docker au chemin /opt/apisix/plugins. Cela permet d'ajouter le plugin personnalisé à APISIX au runtime à un autre chemin dans Docker qui est /opt/?.lua. De même, les deux autres fichiers sont copiés dans les dossiers Docker.

L'étape suivante consiste à activer le plugin dans la liste des plugins d'APISIX. Cela se fait en ajoutant le nom du plugin à la liste plugins dans le fichier de configuration d'APISIX (config.yaml) :

plugins:
  - file-proxy

Notez que cette action remplacera tous les plugins par défaut spécifiés dans config-default.yaml. Vous devez ajouter manuellement d'autres plugins par leur nom si vous souhaitez utiliser votre plugin personnalisé en combinaison.

3. Analyse du code Lua du plugin file-proxy

Jusqu'à présent, nous avons seulement enregistré le plugin qui ne fait rien. Il est temps de l'implémenter. La logique du plugin est implémentée sous forme de fonctions Lua. Vous pouvez vérifier comment cela est fait dans le fichier file-proxy.lua.

Décomposons le fichier file-proxy.lua pour mieux comprendre la structure du code et le flux qui vous aide à créer de nouveaux plugins par vous-même. Vous pouvez simplement demander à ChatGPT d'expliquer le code Lua :

Expliquer le code Lua avec ChatGPT

En fait, nous avons obtenu une assez bonne explication du code (parce qu'il a été partiellement écrit par ChatGPT).

Description de l'image

Je vais seulement vous guider à travers les parties importantes de ce code afin que vous ne soyez pas perdus ou ne dépendiez entièrement de l'IA pour écrire vos plugins.

4. Structure du fichier de plugin

Chaque fichier de plugin Lua doit avoir la structure suivante :

1. Modules : Vous importez les modules/bibliothèques nécessaires pour le plugin

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

2. Nom du plugin : Chaque plugin a un nom unique, il peut être le même que le nom de notre fichier Lua.

local plugin_name = "file-proxy"

3. Schéma du plugin : Chaque plugin a un schéma de plugin, où nous spécifions généralement les entrées du plugin. L'entrée que nous passerons à partir de la configuration de la route APISIX, que vous verrez plus tard lorsque nous testerons le plugin. Pour le plugin file-proxy, le plugin a besoin d'un chemin de fichier pour lire le fichier et renvoyer une réponse, donc notre paramètre est le path qui est de type chaîne. Vous pouvez comprendre le schéma comme une déclaration de méthode avec des paramètres dans d'autres langages de programmation.

local plugin_schema = {
    type = "object",
    properties = {
        path = {
            type = "string" -- Le chemin du fichier à servir
        },
    },
    required = {"path"} -- Le chemin est un champ obligatoire
}

4. Définition du plugin : C'est une partie très importante de l'implémentation du plugin que nous définissons comme une table avec des propriétés pour la version, la priorité, le name, et le schema. Le name et le schema sont le nom et le schéma du plugin définis précédemment. La version et la priorité sont utilisées par APISIX pour gérer le plugin. La version fait généralement référence à la version actuellement utilisée, comme le versionnage d'API. Si vous publiez et mettez à jour la logique de votre plugin, elle passera à 1.1 (vous pouvez définir n'importe quelle version que vous souhaitez). Mais vous devez être très prudent dans le choix de la priorité. Le champ priority définit dans quel ordre et phase votre plugin doit être exécuté. Par exemple, le plugin 'ip-restriction', avec une priorité de 3000, sera exécuté avant le plugin 'example-plugin', qui a une priorité de 0. Cela est dû à la valeur de priorité plus élevée du plugin 'ip-restriction'. Si vous développez votre propre plugin, assurez-vous de suivre l'ordre des plugins pour ne pas perturber l'ordre des plugins existants. Vous pouvez vérifier l'ordre des plugins existants dans le fichier config-default.yaml et consulter le Guide de développement de plugins Apache APISIX pour déterminer.

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

5. Vérification du schéma : La fonction Lua check_schema est utilisée pour valider le plugin dans une configuration de route (vous le verrez bientôt dans la section de test) par rapport au schéma de plugin que nous avons défini précédemment.

-- Fonction pour vérifier si la configuration du plugin est correcte
function _M.check_schema(conf)
  -- Valider la configuration par rapport au schéma
  local ok, err = core.schema.check(plugin_schema, conf)
  -- Si la validation échoue, retourner false et l'erreur
  if not ok then
      return false, err
  end
  -- Si la validation réussit, retourner true
  return true
end

6. Logique du plugin : La fonction access est la fonction principale où nous pouvons écrire la logique principale du plugin. Elle est appelée pendant la phase d'accès du pipeline de traitement des requêtes Nginx et nous contrôlons le trafic et écrivons des instructions personnalisées. Pour file-proxy, nous devons ouvrir le fichier spécifié dans la configuration du plugin, lire son contenu et renvoyer le contenu comme réponse. Si le fichier ne peut pas être ouvert, il enregistre une erreur et renvoie un statut 404 Not Found. C'est exactement l'endroit où nous confions ce travail à ChatGPT :

Code Lua du plugin file-proxy avec ChatGPT

Après avoir structuré et refactorisé le code, voici à quoi il ressemble :

function _M.access(conf, ctx)
  -- Ouvrir le fichier spécifié dans la configuration
  local fd = io.open(conf.path, "rb")
  -- Si le fichier est ouvert avec succès, lire son contenu et le renvoyer comme réponse
  if fd then
    local content = fd:read("*all")
    fd:close()
    ngx.header.content_length = #content
    ngx.say(content)
    ngx.exit(ngx.OK)
  else
    -- Si le fichier ne peut pas être ouvert, enregistrer une erreur et renvoyer un statut 404 Not Found
    ngx.exit(ngx.HTTP_NOT_FOUND)
    core.log.error("Fichier non trouvé : ", conf.path, ", info d'erreur : ", err)
  end
end

7. Logique de journalisation : Il est toujours préférable de journaliser la configuration du plugin afin que nous puissions déboguer et vérifier si le plugin fonctionne comme prévu. Nous pouvons journaliser les requêtes au plugin et les réponses.

-- Fonction à appeler pendant la phase de journalisation
function _M.log(conf, ctx)
    -- Journaliser la configuration du plugin et le contexte de la requête
    core.log.warn("conf : ", core.json.encode(conf))
    core.log.warn("ctx : ", core.json.encode(ctx, true))
end

Installer Apache APISIX

Une fois que nous avons appris à développer notre plugin personnalisé file-proxy et à l'enregistrer dans APISIX, il est temps de tester le plugin. Vous pouvez facilement installer le projet apisix-file-proxy-plugin-demo en exécutant docker compose up depuis le dossier racine du projet après avoir fork/cloné le projet.

Créer une route avec le plugin file-proxy

Pour utiliser et tester notre nouveau plugin file-proxy, nous devons créer une route dans APISIX qui utilise le plugin :

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 pour le fichier de définition OpenAPI",
   "uri":"/openapi.yaml",
   "plugins":{
      "file-proxy":{
         "path":"/usr/local/apisix/conf/openapi.yaml"
      }
   }
}'

Vous pouvez demander à ChatGPT d'expliquer la configuration ci-dessus :

ChatGPT pour expliquer le code Lua

Tester le plugin

Ensuite, vous pouvez envoyer une requête cURL à la route ou ouvrir le lien http://127.0.0.1:9080/openapi.yaml dans votre navigateur. La réponse devrait être le contenu du fichier openapi.yaml à l'URL spécifiée.

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

Le plugin fonctionne comme prévu. Avec cette configuration de plugin, vous pouvez maintenant accéder à n'importe quel fichier en utilisant la route spécifiée.

Résumé

Développer des plugins personnalisés pour APISIX avec Lua est un moyen puissant d'étendre les fonctionnalités de la passerelle API. Nous avons démontré comment créer le plugin file-proxy dans cet article, défini la définition et le schéma du plugin, validé la configuration du plugin et implémenté une logique personnalisée pendant les phases d'accès et de journalisation du pipeline de traitement des requêtes dans APISIX. ChatGPT nous a aidés à écrire le code Lua pour la fonctionnalité principale en comblant nos lacunes dans ce langage de programmation. Bon codage !

Ressources connexes

Tags: