Desenvolvimento de Plugin Personalizado para APISIX com Lua e ChatGPT
June 12, 2023
Uma das principais características do Apache APISIX é sua extensibilidade por meio de plugins. O APISIX permite que você crie seu próprio plugin personalizado para adicionar funcionalidades extras e gerenciar o tráfego de API de forma mais eficiente. Frequentemente, você usa a linguagem de programação Lua para implementar novos plugins ou aproveita plugin runners para desenvolver plugins na sua linguagem de programação favorita. No entanto, o APISIX tem o melhor suporte para Lua. Depois de escrever alguns plugins para o APISIX em Lua, entendi que você não precisa conhecer os fundamentos da programação em Lua ou ser um especialista nessa linguagem quando seu amigo ChatGPT está sempre com você. Por exemplo, com meu background em Java e C#, consigo entender o código e a lógica escritos em Lua e acredito que você também pode.
Este artigo guiará você pelo processo de desenvolvimento de um novo plugin personalizado chamado file-proxy para o APISIX usando Lua e ChatGPT (usamos ele para escrever alguns códigos em Lua para nós). Este plugin será usado para expor arquivos estáticos por meio de API e buscar um arquivo de uma URL especificada.
O APISIX foi construído para aprimorar as funcionalidades existentes do Nginx, e o Nginx fornece uma coleção de módulos Lua reutilizáveis que o APISIX aproveita.
Objetivos de aprendizagem
Você aprenderá o seguinte ao longo do artigo:
- Como desenvolver um novo plugin file-proxy.
- Como usar o ChatGPT de forma eficiente para gerar código Lua.
- Passos para construir seu próprio plugin para o APISIX.
Caso de uso do novo plugin file-proxy
Antes de mergulhar na implementação real do plugin, vamos entender primeiro por que precisamos desse plugin. No momento da escrita deste post, o APISIX pode não fornecer um plugin embutido para um caso semelhante. É por isso que vamos construir um novo. Frequentemente, queremos expor um arquivo estático (Yaml, JSON, JavaScript, CSS ou arquivos de imagem) por meio de API.
Por exemplo, o API Gateway do APISIX age como uma porta de entrada em sua aplicação para rotear solicitações recebidas para vários endpoints de API, é o lugar certo para definir todas as URLs do servidor, caminhos, parâmetros, descrições de cada endpoint de API e suas entradas e saídas. E você constrói especificações OpenAPI para documentar a API. O arquivo OpenAPI .yaml
é como um mapa que orienta o usuário da API a entender e interagir com sua API. Ao fornecer o caminho do arquivo openapi.yaml
(onde ele está armazenado no seu servidor) para o plugin, você pode buscar e servir o arquivo diretamente por meio do seu gateway de API, fornecendo uma interface consistente para os consumidores da API. Então, seus usuários de API podem acessar o arquivo .yaml
na URL especificada (https://example.com/openapi.yaml
).
Há outros casos de uso também, você pode pensar em usar este plugin file-proxy como uma substituição simples de uma Rede de Entrega de Conteúdo (CDN). Se você tem uma aplicação de pequena escala e não quer usar uma CDN completa, pode usar o plugin file-proxy para servir arquivos estáticos de um local específico. O plugin file-proxy pode ser usado como uma camada de cache para arquivos. Se você tem arquivos que são caros para buscar ou gerar, pode usar o plugin para buscar o arquivo uma vez e, em seguida, servir a versão em cache para solicitações subsequentes.
Passos para desenvolver o plugin file-proxy
Vamos executar o APISIX localmente e nosso API Gateway será hospedado em
http://localhost:9080. Quando o desenvolvimento estiver pronto, você pode implantá-lo em seu servidor ou em qualquer provedor de nuvem. Basicamente, queremos colocar um arquivo openapi.yaml
no caminho http://localhost:9080/openapi.yaml
. Você aprenderá como alcançar isso.
Pré-requisitos
- Antes de começar, é bom ter um entendimento básico do APISIX. Familiaridade com gateway de API e seus conceitos-chave, como rotas, upstream, Admin API, plugins. Um entendimento básico do protocolo HTTP também é benéfico.
- Docker é usado para instalar o etcd e o APISIX em contêineres.
- curl é usado para enviar solicitações à API Admin do APISIX. Você também pode usar ferramentas como Postman para interagir com a API.
Entenda o projeto de demonstração e os arquivos
Vamos aproveitar o projeto de demonstração file-proxy demo existente no GitHub. Ele tem uma estrutura bastante semelhante ao repositório Apisix docker example, apenas removemos arquivos desnecessários para manter a demonstração simples. O projeto tem 3 pastas, docker-compose.yml e arquivos de exemplo openapi.yaml.
- docker-compose.yml define dois contêineres, um para o APISIX e outro para o etcd (que é o armazenamento de configuração do APISIX).
- A pasta custom-plugins contém a implementação do plugin file-proxy em Lua. Vamos revisá-la nas seções seguintes.
- openapi.yaml é apenas uma especificação OpenAPI de exemplo que expomos.
Implementar o plugin file-proxy
Começamos perguntando ao ChatGPT como implementar um plugin file-proxy personalizado para o APISIX em Lua. O ChatGPT gera um guia quase semelhante à implementação real, mas a resposta é muito abstrata e, quando você segue o processo, acaba com um plugin que não funciona. No entanto, ele nos ajuda a extrair códigos Lua úteis. Se conhecermos o processo real de desenvolvimento de plugins, será mais fácil combinar ambos os conhecimentos na prática.
1. Criar um arquivo Lua
Criamos um novo arquivo Lua vazio no diretório /custom-plugins
do projeto. O nome do arquivo deve ser o nome do nosso plugin. Por exemplo, se seu plugin se chama file-proxy
, você deve criar um arquivo chamado file-proxy.lua
.
2. Registrar o plugin no APISIX
O APISIX precisa saber onde este arquivo de plugin está localizado e é capaz de executar o plugin de acordo. Para isso, devemos primeiro definir o caminho do arquivo onde o APISIX encontra o arquivo file-proxy.lua
adicionando o caminho do arquivo ao atributo extra_lua_path
do APISIX no arquivo config.yaml.
apisix:
extra_lua_path: "/opt/?.lua"
node_listen: 9080
Agora você pode perguntar por que o caminho do arquivo está definido como /opt/?.lua
. Porque executamos o APISIX usando docker. Você pode notar isso no arquivo docker-compose.yml, há 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
Isso monta o diretório local ./custom-plugins
, onde nosso arquivo file-proxy.lua
com a implementação do plugin personalizado está localizado, como um volume somente leitura no contêiner docker no caminho /opt/apisix/plugins
. Isso permite que o plugin personalizado seja adicionado ao APISIX em tempo de execução para outro caminho no Docker, que está dentro de /opt/?.lua
. Da mesma forma, os outros dois arquivos que copiamos para as pastas do Docker.
Próximo passo, habilitamos o plugin na lista de plugins do APISIX. Isso é feito adicionando o nome do plugin à lista plugins
no arquivo de configuração do APISIX (config.yaml):
plugins:
- file-proxy
Observe que essa ação substituirá todos os plugins padrão existentes especificados em config-default.yaml. Você precisa adicionar manualmente outros plugins pelo nome se quiser usar seu plugin personalizado em combinação.
3. Análise do código Lua do plugin file-proxy
Até agora, apenas registramos o plugin que simplesmente não faz nada. É hora de implementá-lo. A lógica do plugin é implementada como funções Lua. Você pode verificar como isso é feito em file-proxy.lua.
Vamos analisar o arquivo file-proxy.lua para entender melhor a estrutura do código e o fluxo que ajuda você a criar novos plugins por conta própria. Você pode simplesmente pedir ao ChatGPT para explicar o código Lua:
Na verdade, obtivemos uma explicação bastante boa do código (porque ele foi parcialmente escrito pelo ChatGPT).
Vou apenas guiá-lo pelas partes importantes deste código para que você não se perca ou dependa totalmente da IA para escrever seus plugins.
4. Estrutura do arquivo do plugin
Todo arquivo Lua de plugin deve ter a seguinte estrutura:
1. Módulos: Você importa os módulos/bibliotecas necessários que precisamos para o plugin
local core = require("apisix.core")
...
2. Nome do plugin: Todo plugin tem um nome único, pode ser o mesmo que o nome do nosso arquivo Lua.
local plugin_name = "file-proxy"
3. Esquema do plugin: Todo plugin tem um esquema de plugin, onde geralmente especificamos as entradas para o plugin. A entrada que passaremos da configuração da rota do APISIX, que você verá mais tarde quando testarmos o plugin. Para o plugin file-proxy
, o plugin precisa de um caminho de arquivo para ler o arquivo e retornar uma resposta, então nosso parâmetro é o path
, que é do tipo string. Você pode entender o esquema como uma declaração de método com parâmetros em outras linguagens de programação.
local plugin_schema = {
type = "object",
properties = {
path = {
type = "string" -- O caminho do arquivo a ser servido
},
},
required = {"path"} -- O caminho é um campo obrigatório
}
4. Definição do plugin: É uma parte realmente importante da implementação do plugin que definimos como uma tabela com propriedades para version
, priority
, name
e schema
. O name
e schema
são o nome e o esquema do plugin definidos anteriormente. O version
e priority
são usados pelo APISIX para gerenciar o plugin. A versão geralmente se refere à versão que está atualmente em uso, como a versão da API. Se você publicar e atualizar a lógica do seu plugin, ela será 1.1
(você pode definir qualquer versão que desejar). Mas você precisa ter muito cuidado ao escolher a prioridade. O campo priority
define em qual ordem e fase seu plugin deve ser executado. Por exemplo, o plugin 'ip-restriction', com uma prioridade de 3000, será executado antes do 'example-plugin', que tem uma prioridade de 0. Isso se deve ao valor de prioridade mais alto do plugin 'ip-restriction'. Se você estiver desenvolvendo seu próprio plugin, certifique-se de seguir a ordem dos plugins para não bagunçar a ordem dos plugins existentes. Você pode verificar a ordem dos plugins existentes no arquivo config-default.yaml e abrir o Guia de Desenvolvimento de Plugins do Apache APISIX para determinar.
local _M = {
version = 1.0,
priority = 1000,
name = plugin_name,
schema = plugin_schema
}
5. Verificação de esquema: A função Lua check_schema
é usada para validar o plugin em uma configuração de rota (você verá isso em breve na seção de teste) em relação ao esquema do plugin que definimos anteriormente.
-- Função para verificar se a configuração do plugin está correta
function _M.check_schema(conf)
-- Valida a configuração em relação ao esquema
local ok, err = core.schema.check(plugin_schema, conf)
-- Se a validação falhar, retorna falso e o erro
if not ok then
return false, err
end
-- Se a validação for bem-sucedida, retorna verdadeiro
return true
end
6. Lógica do plugin: A função access
é a função principal onde podemos escrever a lógica principal do plugin. Ela é chamada durante a fase de acesso do pipeline de processamento de solicitações do Nginx e controlamos o tráfego e escrevemos instruções personalizadas. Para o file-proxy, precisamos abrir o arquivo especificado na configuração do plugin, ler seu conteúdo e retornar o conteúdo como resposta. Se o arquivo não puder ser aberto, ele registra um erro e retorna um status 404 Not Found. É exatamente o lugar onde damos esse trabalho ao ChatGPT:
Depois de estruturar e refatorar o código, ele ficou assim:
function _M.access(conf, ctx)
-- Abre o arquivo especificado na configuração
local fd = io.open(conf.path, "rb")
-- Se o arquivo for aberto com sucesso, lê seu conteúdo e o retorna como resposta
if fd then
local content = fd:read("*all")
fd:close()
ngx.header.content_length = #content
ngx.say(content)
ngx.exit(ngx.OK)
else
-- Se o arquivo não puder ser aberto, registra um erro e retorna um status 404 Not Found
ngx.exit(ngx.HTTP_NOT_FOUND)
core.log.error("Arquivo não encontrado: ", conf.path, ", informações de erro: ", err)
end
end
7. Lógica de registro: É sempre preferível registrar a configuração do plugin para que possamos depurar e verificar se o plugin está funcionando como esperado. Podemos registrar solicitações ao plugin e respostas.
-- Função a ser chamada durante a fase de log
function _M.log(conf, ctx)
-- Registra a configuração do plugin e o contexto da solicitação
core.log.warn("conf: ", core.json.encode(conf))
core.log.warn("ctx: ", core.json.encode(ctx, true))
end
Instalar o Apache APISIX
Depois de aprendermos como desenvolver nosso plugin personalizado file-proxy, registrado no APISIX. Agora é hora de testar o plugin. Você pode facilmente instalar o projeto apisix-file-proxy-plugin-demo executando docker compose up
na pasta raiz do projeto após fazer um fork/clone do projeto.
Criar uma rota com o plugin file-proxy
Para usar e testar nosso novo plugin file-proxy, precisamos criar uma rota no APISIX que use o 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":"Rota para o arquivo de definição OpenAPI",
"uri":"/openapi.yaml",
"plugins":{
"file-proxy":{
"path":"/usr/local/apisix/conf/openapi.yaml"
}
}
}'
Você pode pedir ao ChatGPT para explicar a configuração acima:
Testar o plugin
Então, você pode enviar uma solicitação cURL para a rota ou abrir o link http://127.0.0.1:9080/openapi.yaml no seu navegador. A resposta deve ser o conteúdo do arquivo openapi.yaml
na URL especificada.
curl -i http://127.0.0.1:9080/openapi.yaml
O plugin funciona como esperado. Com essa configuração de plugin, você agora pode acessar qualquer arquivo usando a rota especificada.
Resumo
Desenvolver plugins personalizados para o APISIX com Lua é uma maneira poderosa de estender a funcionalidade do gateway de API. Demonstramos como criar o plugin file-proxy neste post, definimos a definição e o esquema do plugin, validamos a configuração do plugin e implementamos a lógica personalizada durante as fases de acesso e log do pipeline de processamento de solicitações no APISIX. O ChatGPT nos ajudou a escrever o código Lua para a funcionalidade principal, preenchendo nossa falta de conhecimento nessa linguagem de programação. Feliz codificação!