Desarrollo de Plugins Personalizados para APISIX con Lua y ChatGPT
June 12, 2023
Una de las características clave de Apache APISIX es su extensibilidad a través de plugins. APISIX te permite construir tu propio plugin personalizado para agregar funcionalidades adicionales y gestionar el tráfico de API de manera más eficiente. A menudo, se utiliza el lenguaje de programación Lua para implementar nuevos plugins o se aprovechan los plugin runners para desarrollar plugins en tu lenguaje de programación favorito. Sin embargo, APISIX tiene el mejor soporte para Lua. Después de escribir un par de plugins para APISIX en Lua, entendí que no necesitas conocer los fundamentos de programación en Lua ni ser un experto en este lenguaje cuando tu amigo ChatGPT siempre está contigo. Por ejemplo, con mi experiencia en Java y C#, puedo entender el código y la lógica escrita en Lua, y creo que tú también puedes hacerlo.
Este artículo te guiará a través del proceso de desarrollo de un nuevo plugin personalizado llamado file-proxy para APISIX utilizando Lua y ChatGPT (lo usamos para escribir algo de código Lua por nosotros). Este plugin se utilizará para exponer archivos estáticos a través de una API y obtener un archivo desde una URL especificada.
APISIX fue construido para mejorar las funcionalidades existentes de Nginx, y Nginx proporciona una colección de módulos Lua reutilizables que APISIX aprovecha.
Objetivos de aprendizaje
A lo largo del artículo, aprenderás lo siguiente:
- Cómo desarrollar un nuevo plugin file-proxy.
- Cómo usar ChatGPT de manera eficiente para generar código Lua.
- Pasos para construir tu propio plugin para APISIX.
Caso de uso del nuevo plugin file-proxy
Antes de saltar a la implementación real del plugin, entendamos primero por qué necesitamos este plugin. Al momento de escribir este post, APISIX podría no proporcionar un plugin integrado para un caso similar. Por eso vamos a construir uno nuevo. A menudo, queremos exponer un archivo estático (Yaml, JSON, JavaScript, CSS o archivos de imagen) a través de una API.
Por ejemplo, el API Gateway de APISIX actúa como una puerta de entrada en tu aplicación para enrutar las solicitudes entrantes a múltiples endpoints de API, es el lugar correcto para definir todas las URLs del servidor, rutas, parámetros, descripciones de cada endpoint de API y sus entradas y salidas. Y construyes especificaciones OpenAPI para documentar la API. El archivo OpenAPI .yaml
es como un mapa que guía a los usuarios de tu API para entender e interactuar con tu API. Al proporcionar la ruta del archivo openapi.yaml
(donde está almacenado en tu servidor) al plugin, puedes obtener y servir el archivo directamente a través de tu API gateway, proporcionando una interfaz consistente para los consumidores de la API. Luego, los usuarios de tu API pueden acceder al archivo .yaml
en la URL especificada (https://example.com/openapi.yaml
).
También hay otros casos de uso, podrías pensar en usar este plugin file-proxy como un reemplazo simple de una Red de Entrega de Contenidos (CDN). Si tienes una aplicación a pequeña escala y no quieres usar una CDN completa, puedes usar el plugin file-proxy para servir archivos estáticos desde una ubicación específica. El plugin file-proxy puede usarse como una capa de caché para archivos. Si tienes archivos que son costosos de obtener o generar, puedes usar el plugin para obtener el archivo una vez y luego servir la versión en caché para solicitudes posteriores.
Pasos para desarrollar el plugin file-proxy
Vamos a ejecutar APISIX localmente y nuestro API Gateway estará alojado en
http://localhost:9080. Cuando el desarrollo esté listo, puedes desplegarlo en tu servidor o en cualquier proveedor de la nube. Básicamente, queremos colocar un archivo openapi.yaml
en la ruta http://localhost:9080/openapi.yaml
. Aprenderás cómo lograr esto.
Prerrequisitos
- Antes de comenzar, es bueno tener un conocimiento básico de APISIX. Familiaridad con el API gateway y sus conceptos clave como rutas, upstream, Admin API, plugins. Un conocimiento básico del protocolo HTTP también es beneficioso.
- Docker se utiliza para instalar etcd y APISIX en contenedores.
- curl se utiliza para enviar solicitudes a la API de administración de APISIX. También puedes usar herramientas como Postman para interactuar con la API.
Entender el proyecto de demostración y los archivos
Aprovecharemos el proyecto de demostración file-proxy demo existente en GitHub. Tiene una estructura bastante similar al repositorio Apisix docker example, solo que eliminamos archivos innecesarios para mantener la demostración simple. El proyecto tiene 3 carpetas, docker-compose.yml, y archivos de muestra openapi.yaml.
- docker-compose.yml define dos contenedores, uno para APISIX y otro para etcd (que es el almacenamiento de configuración para APISIX).
- La carpeta custom-plugins tiene la implementación del plugin file-proxy en Lua. Lo revisaremos en las siguientes secciones.
- openapi.yaml es solo una especificación OpenAPI de muestra que exponemos.
Implementar el plugin file-proxy
Comenzamos preguntando a ChatGPT cómo implementar un plugin file-proxy personalizado para APISIX en Lua. ChatGPT genera una guía casi similar a la implementación real, pero la respuesta es demasiado abstracta y cuando sigues el proceso, terminarás con un plugin que no funciona. Sin embargo, nos ayuda a extraer código Lua útil. Si conocemos el proceso real de desarrollo de plugins, será más fácil combinar ambos conocimientos en la práctica.
1. Crear un archivo Lua
Creamos un nuevo archivo Lua vacío en el directorio /custom-plugins
del proyecto. El nombre del archivo debe ser el nombre de nuestro plugin. Por ejemplo, si tu plugin se llama file-proxy
, debes crear un archivo llamado file-proxy.lua
.
2. Registrar el plugin en APISIX
APISIX necesita saber dónde se encuentra este archivo de plugin y poder ejecutar el plugin en consecuencia. Para hacerlo, primero debemos definir la ruta del archivo donde APISIX encuentra el archivo file-proxy.lua
agregando la ruta del archivo al atributo extra_lua_path
de APISIX en el archivo config.yaml.
apisix:
extra_lua_path: "/opt/?.lua"
node_listen: 9080
Ahora podrías preguntarte por qué la ruta del archivo está configurada como /opt/?.lua
. Porque ejecutamos APISIX usando Docker. Puedes notar esto en el archivo docker-compose.yml, hay 3 volúmenes ./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
Esto monta el directorio local ./custom-plugins
donde nuestro archivo file-proxy.lua
con la implementación del plugin personalizado como un volumen de solo lectura en el contenedor Docker en la ruta /opt/apisix/plugins
. Esto permite que el plugin personalizado se agregue a APISIX en tiempo de ejecución en otra ruta en Docker que está dentro de /opt/?.lua
. De manera similar, copiamos los otros dos archivos a las carpetas de Docker.
El siguiente paso es habilitar el plugin en la lista de plugins de APISIX. Esto se hace agregando el nombre del plugin a la lista plugins
en el archivo de configuración de APISIX (config.yaml):
plugins:
- file-proxy
Nota que esta acción anulará todos los plugins predeterminados existentes especificados en config-default.yaml. Debes agregar manualmente otros plugins por su nombre si deseas usar tu plugin personalizado en combinación con ellos.
3. Desglose del código Lua del plugin file-proxy
Hasta ahora, solo hemos registrado el plugin que simplemente no hace nada. Es hora de implementarlo. La lógica del plugin se implementa como funciones Lua. Puedes ver cómo se hace en file-proxy.lua.
Desglosemos el archivo file-proxy.lua para entender mejor la estructura del código y el flujo que te ayudará a crear nuevos plugins por tu cuenta. Puedes simplemente pedirle a ChatGPT que explique el código Lua:
En realidad, obtuvimos una explicación bastante buena del código (porque fue parcialmente escrito por ChatGPT).
Solo te guiaré a través de las partes importantes de este código para que no te pierdas o dependas completamente de la IA para escribir tus plugins.
4. Estructura del archivo del plugin
Cada archivo Lua de plugin debe tener la siguiente estructura:
1. Módulos: Importas los módulos/bibliotecas necesarios que necesitamos para el plugin
local core = require("apisix.core")
...
2. Nombre del plugin: Cada plugin tiene un nombre único, puede ser el mismo que el nombre de nuestro archivo Lua.
local plugin_name = "file-proxy"
3. Esquema del plugin: Cada plugin tiene un esquema de plugin, donde generalmente especificamos las entradas al plugin. La entrada que pasaremos desde la configuración de la ruta de APISIX, que verás más adelante cuando probemos el plugin. Para el plugin file-proxy
, el plugin necesita una ruta de archivo para leer el archivo y devolver una respuesta, por lo que nuestro parámetro es path
, que es de tipo cadena. Puedes entender el esquema como una declaración de método con parámetros en otros lenguajes de programación.
local plugin_schema = {
type = "object",
properties = {
path = {
type = "string" -- La ruta del archivo que se servirá
},
},
required = {"path"} -- La ruta es un campo obligatorio
}
4. Definición del plugin: Es una parte realmente importante de la implementación del plugin que definimos como una tabla con propiedades para version
, priority
, name
y schema
. El name
y schema
son el nombre y el esquema del plugin definidos anteriormente. La version
y priority
son utilizadas por APISIX para gestionar el plugin. La versión generalmente se refiere a la versión que está en uso actualmente, como la versión de la API. Si publicas y actualizas la lógica de tu plugin, será 1.1
(puedes establecer cualquier versión que desees). Pero debes tener mucho cuidado al elegir la prioridad. El campo priority
define en qué orden y fase debe ejecutarse tu plugin. Por ejemplo, el plugin 'ip-restriction', con una prioridad de 3000, se ejecutará antes que el 'example-plugin', que tiene una prioridad de 0. Esto se debe a la mayor prioridad del plugin 'ip-restriction'. Si estás desarrollando tu propio plugin, asegúrate de seguir el orden de los plugins para no desordenar el orden de los plugins existentes. Puedes verificar el orden de los plugins existentes en el archivo config-default.yaml y abrir la Guía de desarrollo de plugins de Apache APISIX para determinar.
local _M = {
version = 1.0,
priority = 1000,
name = plugin_name,
schema = plugin_schema
}
5. Verificación del esquema: La función Lua check_schema
se utiliza para validar el plugin en una configuración de ruta (lo verás pronto en la sección de prueba) contra el esquema del plugin que definimos anteriormente.
-- Función para verificar si la configuración del plugin es correcta
function _M.check_schema(conf)
-- Validar la configuración contra el esquema
local ok, err = core.schema.check(plugin_schema, conf)
-- Si la validación falla, devolver falso y el error
if not ok then
return false, err
end
-- Si la validación tiene éxito, devolver verdadero
return true
end
6. Lógica del plugin: La función access
es la función principal donde podemos escribir la lógica principal del plugin. Se llama durante la fase de acceso de la tubería de procesamiento de solicitudes de Nginx y controlamos el tráfico y escribimos instrucciones personalizadas. Para file-proxy, necesitamos abrir el archivo especificado en la configuración del plugin, leer su contenido y devolver el contenido como respuesta. Si el archivo no se puede abrir, registra un error y devuelve un estado 404 No Encontrado. Es el lugar exacto donde le damos este trabajo a ChatGPT:
Después de estructurar y refactorizar el código, así es como se ve:
function _M.access(conf, ctx)
-- Abrir el archivo especificado en la configuración
local fd = io.open(conf.path, "rb")
-- Si el archivo se abre correctamente, leer su contenido y devolverlo como respuesta
if fd then
local content = fd:read("*all")
fd:close()
ngx.header.content_length = #content
ngx.say(content)
ngx.exit(ngx.OK)
else
-- Si el archivo no se puede abrir, registrar un error y devolver un estado 404 No Encontrado
ngx.exit(ngx.HTTP_NOT_FOUND)
core.log.error("Archivo no encontrado: ", conf.path, ", información de error: ", err)
end
end
7. Lógica de registro: Siempre es preferible registrar la configuración del plugin para que podamos depurar y verificar si el plugin está funcionando como esperamos. Podemos registrar las solicitudes al plugin y las respuestas.
-- Función para ser llamada durante la fase de registro
function _M.log(conf, ctx)
-- Registrar la configuración del plugin y el contexto de la solicitud
core.log.warn("conf: ", core.json.encode(conf))
core.log.warn("ctx: ", core.json.encode(ctx, true))
end
Instalar Apache APISIX
Una vez que aprendimos cómo desarrollar nuestro plugin personalizado file-proxy, registrado en APISIX. Ahora es el momento de probar el plugin. Puedes instalar fácilmente el proyecto apisix-file-proxy-plugin-demo ejecutando docker compose up
desde la carpeta raíz del proyecto después de bifurcar/clonar el proyecto.
Crear una ruta con el plugin file-proxy
Para usar y probar nuestro nuevo plugin file-proxy, necesitamos crear una ruta en APISIX que use el 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":"Ruta para el archivo de definición OpenAPI",
"uri":"/openapi.yaml",
"plugins":{
"file-proxy":{
"path":"/usr/local/apisix/conf/openapi.yaml"
}
}
}'
Puedes pedirle a ChatGPT que explique la configuración anterior:
Probar el plugin
Luego, puedes enviar una solicitud cURL a la ruta o abrir el enlace http://127.0.0.1:9080/openapi.yaml en tu navegador. La respuesta debería ser el contenido del archivo openapi.yaml
en la URL especificada.
curl -i http://127.0.0.1:9080/openapi.yaml
El plugin funciona como esperábamos. Con esta configuración del plugin, ahora puedes acceder a cualquier archivo utilizando la ruta especificada.
Resumen
Desarrollar plugins personalizados para APISIX con Lua es una forma poderosa de extender la funcionalidad del API gateway. En este post demostramos cómo crear el plugin file-proxy, definimos la definición y el esquema del plugin, validamos la configuración del plugin e implementamos lógica personalizada durante las fases de acceso y registro de la tubería de procesamiento de solicitudes en APISIX. ChatGPT nos ayudó a escribir código Lua para la funcionalidad principal llenando nuestro conocimiento faltante en este lenguaje de programación. ¡Feliz codificación!