Desenvolvimento de Plugin Personalizado para APISIX com Lua e ChatGPT

Bobur Umurzokov

Bobur Umurzokov

June 12, 2023

Technology

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.

Implementar o plugin file-proxy com ChatGPT

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:

Explicar o código Lua com ChatGPT

Na verdade, obtivemos uma explicação bastante boa do código (porque ele foi parcialmente escrito pelo ChatGPT).

Descrição da imagem

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:

Código Lua do plugin file-proxy com 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:

ChatGPT para explicar o código Lua

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!

Recursos relacionados

Tags: