Как создать плагин для Apache APISIX с нуля до готовности?

Qi Guo

Qi Guo

February 16, 2022

Ecosystem

За последние несколько месяцев пользователи сообщества добавили множество плагинов в Apache APISIX, что значительно обогатило экосистему Apache APISIX. С точки зрения пользователя, появление более разнообразных плагинов, безусловно, является положительным моментом, так как они удовлетворяют больше ожиданий пользователей от шлюза, который должен быть "универсальным" и "многофункциональным" процессором, помимо обеспечения высокой производительности и низкой задержки Apache APISIX.

Ни одна из статей в блоге Apache APISIX, похоже, не затрагивает подробно процесс разработки плагинов. Поэтому давайте рассмотрим этот процесс с точки зрения разработчика плагина и посмотрим, как рождается плагин!

Эта статья описывает процесс разработки плагина file-logger фронтенд-инженером без опыта работы с бэкендом. Прежде чем углубиться в детали процесса реализации, мы кратко представим функциональность file-logger.

Введение в плагин file-logger

Плагин file-logger поддерживает создание пользовательских форматов логов с использованием метаданных плагина Apache APISIX. Пользователи могут добавлять данные запросов и ответов в формате JSON в файлы логов через плагин file-logger или отправлять поток логов в указанное место.

Представьте себе: при мониторинге логов доступа к маршруту нас интересует не только значение определенных данных запроса и ответа, но и возможность записать данные логов в указанный файл. Именно здесь может помочь плагин file-logger, чтобы достичь этих целей.

как это работает

Мы можем использовать file-logger для записи данных логов в конкретный файл логов, чтобы упростить процесс мониторинга и отладки.

Как реализовать плагин?

После введения в функциональность file-logger, у вас будет лучшее понимание этого плагина. Далее следует подробное объяснение того, как я, фронтенд-разработчик без опыта работы с серверной частью, разработал плагин для Apache APISIX и добавил соответствующие тесты для него.

Определение имени и приоритета плагина

Откройте Руководство по разработке плагинов Apache APISIX и в порядке приоритета вам нужно определить следующие две вещи:

  1. Определить категорию плагина.
  2. Установить приоритет плагинов и обновить файл conf/config-default.yaml.

Поскольку разработка file-logger относится к типу плагинов для логирования, я обратился к именам и порядку существующих плагинов логирования для Apache APISIX и разместил file-logger здесь.

позиция file-logger

После консультации с другими авторами плагинов и активными членами сообщества, наконец, были подтверждены имя плагина file-logger и его приоритет 399.

Обратите внимание, что приоритет плагина связан с порядком выполнения; чем выше значение приоритета, тем раньше выполняется плагин. А порядок имен плагинов не связан с порядком выполнения.

Создание минимального исполняемого файла плагина

После подтверждения имени и приоритета плагина можно создать файл кода плагина в каталоге apisix/plugins/. Здесь есть два важных момента:

  • Если файл кода плагина создается непосредственно в каталоге apisix/plugins/, то нет необходимости изменять файл Makefile.
  • Если ваш плагин имеет собственный каталог с кодом, необходимо обновить файл Makefile, подробные шаги можно найти в Руководстве по разработке плагинов Apache APISIX.
  1. Здесь мы создаем файл file-logger.lua в каталоге apisix/plugins/.
  2. Затем мы завершим инициализированную версию на основе example-plugin.
-- Подключаем необходимые модули в заголовке local log_util = require("apisix.utils.log-util") local core = require("apisix.core") local plugin = require("apisix.plugin") local ngx = ngx -- Объявляем имя плагина local plugin_name = "file-logger" -- Определяем схему формата плагина local schema = { type = "object", properties = { path = { type = "string" }, }, required = {"path"} } -- Схема метаданных плагина local metadata_schema = { type = "object", properties = { log_format = log_util.metadata_schema_log_format } } local _M = { version = 0.1, priority = 399, name = plugin_name, schema = schema, metadata_schema = metadata_schema } -- Проверяем, корректна ли конфигурация плагина function _M.check_schema(conf, schema_type) if schema_type == core.schema.TYPE_METADATA then return core.schema.check(metadata_schema, conf) end return core.schema.check(schema, conf) end -- Фаза логирования function _M.log(conf, ctx) core.log.warn("conf: ", core.json.encode(conf)) core.log.warn("ctx: ", core.json.encode(ctx, true)) end return _M

После создания минимального исполняемого файла плагина, данные конфигурации плагина и данные, связанные с запросом, могут быть выведены в файл error.log с помощью core.log.warn(core.json.encode(conf)) и core.log.warn("ctx: ", core.json.encode(ctx, true)).

Включение и тестирование плагина

Ниже приведены несколько шагов для тестирования. Чтобы проверить, может ли плагин успешно выводить данные плагина и информацию, связанную с запросом, в файл логов ошибок, нам нужно включить плагин и создать тестовый маршрут.

  1. Подготовьте локальный тестовый апстрим (в этой статье используется тестовый апстрим 127.0.0.1:3030/api/hello, который я создал локально).

  2. Создайте маршрут с помощью команды curl и включите наш новый плагин.

    curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' { "plugins": { "file-logger": { "path": "logs/file.log" } }, "upstream": { "type": "roundrobin", "nodes": { "127.0.0.1:3030": 1 } }, "uri": "/api/hello" }'

    Затем вы увидите код состояния 200, что означает успешное создание маршрута.

  3. Запустите команду curl, чтобы отправить запрос на маршрут и проверить, был ли запущен плагин file-logger.

    curl -i http://127.0.0.1:9080/api/hello HTTP/1.1 200 OK ... hello, world
  4. В файле logs/error.log появится запись:

    запись в logs/error.log

    Как видно, path: logs/file.log, который мы настроили для плагина в параметре conf, был успешно сохранен. На этом этапе мы успешно создали минимально используемый плагин, который выводит параметры conf и ctx в фазе логирования.

    После этого мы можем написать основную функциональность для плагина file-logger.lua непосредственно в его файле кода. Здесь мы можем просто выполнить команду apisix reload, чтобы перезагрузить последнюю версию кода плагина без перезапуска Apache APISIX.

Написание основной функциональности для плагина file-logger

Основная функция плагина file-logger заключается в записи данных логов. После консультации с другими участниками сообщества и изучения информации, я узнал о библиотеке IO в Lua и подтвердил, что логика функции плагина примерно следующая.

  1. После каждого принятого запроса выводить данные логов в path, настроенный плагином.

    1. Сначала получить значение path в file-logger через conf в фазе логирования.
    2. Затем использовать библиотеку IO в Lua для создания, открытия, записи, обновления кэша и закрытия файла.
  2. Обработка ошибок, таких как неудачное открытие файла, создание файла и т.д.

    local function write_file_data(conf, log_message) local msg, err = core.json.encode(log_message) if err then return core.log.error("message json serialization failed, error info : ", err) end local file, err = io_open(conf.path, 'a+') if not file then core.log.error("failed to open file: ", conf.path, ", error info: ", err) else local ok, err = file:write(msg, '\n') if not ok then core.log.error("failed to write file: ", conf.path, ", error info: ", err) else file:flush() end file:close() end end
  3. Ссылаясь на исходный код плагина http-logger, я завершил метод передачи данных логов для записи логов и некоторые проверки и обработки метаданных.

    function _M.log(conf, ctx) local metadata = plugin.plugin_metadata(plugin_name) local entry if metadata and metadata.value.log_format and core.table.nkeys(metadata.value.log_format) > 0 then entry = log_util.get_custom_format_log(ctx, metadata.value.log_format) else entry = log_util.get_full_log(ngx, conf) end write_file_data(conf, entry) end

Проверка и добавление тестов

Проверка записей логов

Поскольку плагин file-logger был включен при создании тестового маршрута и путь был настроен как logs/file.log, мы можем просто отправить запрос на тестовый маршрут, чтобы проверить результаты сбора логов.

curl -i http://127.0.0.1:9080/api/hello

В соответствующем файле logs/file.log мы можем видеть, что каждая запись сохраняется в формате JSON. После форматирования одной из данных, это выглядит следующим образом.

{ "server": { "hostname": "....", "version": "2.11.0" }, "client_ip": "127.0.0.1", "upstream": "127.0.0.1:3030", "route_id": "1", "start_time": 1641285122961, "latency": 13.999938964844, "response": { "status": 200, "size": 252, "headers": { "server": "APISIX/2.11.0", "content-type": "application/json; charset=utf-8", "date": "Tue, 04 Jan 2022 08:32:02 GMT", "vary": "Accept-Encoding", "content-length": "19", "connection": "close", "etag": "\"13-5j0ZZR0tI549fSRsYxl8c9vAU78\"" } }, "service_id": "", "request": { "querystring": {}, "size": 87, "method": "GET", "headers": { "host": "127.0.0.1:9080", "accept": "*/*", "user-agent": "curl/7.77.0" }, "url": "http://127.0.0.1:9080/api/hello", "uri": "/api/hello" } }

На этом проверка сбора записей логов завершена. Результаты проверки показывают, что плагин был успешно запущен и вернул соответствующие данные.

Добавление дополнительных тестов для плагина

Для части кода add_block_preprocessor я был в замешательстве, когда впервые начал писать его, так как у меня не было предыдущего опыта работы с Perl. После исследования я понял правильный способ его использования: если мы не пишем утверждения request и no_error_log в разделе данных, то по умолчанию утверждение выглядит следующим образом.

--- request GET /t --- no_error_log [error]

После рассмотрения других файлов тестов логирования, я создал файл file-logger.t в каталоге t/plugin/.

Каждый тестовый файл разделен на раздел преамбулы и раздел данных с помощью **DATA**. Поскольку на официальном сайте нет четкой классификации документов, связанных с тестированием, вы можете обратиться к материалам в конце статьи для получения более подробной информации. Вот один из тестовых случаев, который я завершил после изучения соответствующих материалов.

use t::APISIX 'no_plan'; no_long_string(); no_root_location(); add_block_preprocessor(sub { my ($block) = @_; if (! $block->request) { $block->set_value("request", "GET /t"); } if (! $block->no_error_log && ! $block->error_log) { $block->set_value("no_error_log", "[error]"); } }); run_tests; __DATA__ === TEST 1: sanity --- config location /t { content_by_lua_block { local configs = { -- полная конфигурация { path = "file.log" }, -- свойство "path" обязательно { path = nil } } local plugin = require("apisix.plugins.file-logger") for i = 1, #configs do ok, err = plugin.check_schema(configs[i]) if err then ngx.say(err) else ngx.say("done") end end } } --- response_body_like done property "path" is required

На этом завершается сессия добавления тестов для плагина.

Заключение

Выше описан весь процесс реализации плагина Apache APISIX с нуля для новичка в бэкенде. В процессе разработки плагина я действительно столкнулся с множеством трудностей, но, к счастью, в сообществе Apache APISIX есть много активных участников, которые помогли мне решить проблемы, что сделало разработку и тестирование плагина file-logger относительно гладким на протяжении всего процесса. Если вас интересует этот плагин или вы хотите увидеть детали плагина, вы можете обратиться к официальной документации Apache APISIX.

Apache APISIX также активно работает над другими плагинами для поддержки большего количества интеграционных сервисов, поэтому, если вы заинтересованы, не стесняйтесь начать обсуждение в GitHub Discussion или через список рассылки.

Ссылки

Tags: