WebAssembly-плагины бэкенда
WebAssembly-плагины бэкенда запускают бэкенд плагина, скомпилированный в WebAssembly (WASM), внутри процесса Grafana, в изолированной виртуальной машине, вместо отдельного процесса ОС, с которым Grafana общается по gRPC.
Caution
Драйвер WASM-бэкенда экспериментальный. Он поддерживает сокращённый набор вызовов бэкенда, а описанный на этой странице хостовый ABI ещё не стабилен.
Когда использовать WebAssembly-плагины
Используйте WASM-бэкенд, когда нужен портируемый бэкенд-плагин, который дёшево изолировать:
- Один модуль для всех платформ. WASM-модуль выполняется без изменений на любой ОС и архитектуре CPU, которые поддерживает Grafana, так что не нужно собирать и поставлять бинарник под каждую платформу.
- Без CGO. Рантайм написан на чистом Go, поэтому для сборки и запуска Grafana с этим драйвером не нужны C-тулчейн и нативная компиляция.
- Выполнение внутри процесса. Отдельный процесс не запускается, поэтому нет ни надзора за процессами, ни выделения портов, ни межпроцессного сокета.
- Изоляция по возможностям. Модуль стартует без доступа к сети, файловой системе и переменным окружения и взаимодействует с внешним миром только через хостовые функции
host_http_fetchиhost_logв рамках списка разрешений.
Выбирайте стандартный gRPC-плагин бэкенда, когда нужны потоковые вызовы, сбор метрик, чанковые запросы или обработчики admission и conversion: драйвер WASM их пока не поддерживает.
Как Grafana выбирает драйвер бэкенда
Grafana строит цепочку провайдеров фабрик бэкенда для каждого плагина. Она по очереди запрашивает у каждого провайдера фабрику и использует первую, которая вернула непустой результат. Для WASM-плагина цепочка разрешается так:
- Core-плагины, например встроенные источники данных.
- WASM-провайдер, который заявляет плагин только если тот объявил себя как WASM.
- Провайдер gRPC по умолчанию, который обрабатывает всё остальное.
WASM-провайдер заявляет плагин, когда выполняется любое из условий:
- В
plugin.jsonполеbackendRuntimeустановлено в"wasm". - Путь к исполняемому файлу плагина заканчивается на
.wasm, что покрывает плагины, которые не устанавливаютbackendRuntime, но поставляют WASM-бинарник.
Если ни одно условие не выполняется, WASM-провайдер ничего не возвращает, и цепочка продолжается к провайдеру gRPC.
Настройка WebAssembly-плагина
Объявите плагин как WASM в plugin.json и укажите wasmFilePath с путём к модулю.
{
"id": "myorg-wasm-datasource",
"type": "datasource",
"backend": true,
"executable": "gpx_myorg_wasm_datasource",
"backendRuntime": "wasm",
"wasmFilePath": "module/main.wasm",
"wasmSandbox": {
"allowedOutboundHosts": ["api.example.com", "cache.example.com:8443"],
"allowedEnvVars": ["MYORG_API_TOKEN"],
"allowedReadPaths": ["/etc/myorg/certs:/certs"]
}
}
Бэкендом WASM управляют следующие ключи:
backend: Установитеtrue, чтобы Grafana загрузила бэкенд для плагина.backendRuntime: Установите"wasm", чтобы явно выбрать драйвер WASM.wasmFilePath: Путь к WASM-модулю, разрешается относительно каталога плагина. Если его не указывать, Grafana использует обычный путьexecutable.wasmSandbox: Необязательные списки разрешений песочницы, которые запрашивает плагин. См. раздел Списки разрешений песочницы.
Полный справочник манифеста плагина приведён в схеме плагина.
Песочница и изоляция
WASM-плагин выполняется внутри виртуальной машины wazero. У него нет доступа к файловой системе, сокетам и переменным окружения, пока Grafana явно их не предоставит, и он не может повлиять ни на что за пределами собственной памяти, кроме как через хостовые функции.
Списки разрешений песочницы
Каждая возможность, доступная гостевому модулю, предоставляется через список разрешений. Grafana составляет эти списки из трёх источников и объединяет их:
- Блок
wasmSandboxвplugin.json. Учитывается только при статусе подписи плагинаvalidилиinternal. Манифест и его подпись покрываются одним и тем же подписанным хэшем, поэтому корректная подпись подтверждает и списки разрешений; неподписанный или изменённый манифест не может расширить собственную песочницу. [plugins.wasm_sandbox.<идентификатор плагина>]. Записи для одного плагина, задаваемые администратором.[plugins.wasm_sandbox]. Значения по умолчанию для всех WASM-плагинов.
Списки складываются: источник может расширить то, что разрешил другой, но не сузить. Чтобы списки разрешений задавались исключительно администратором, установите ignore_manifest_grants = true в секции [plugins.wasm_sandbox].
[plugins.wasm_sandbox]
allowed_outbound_hosts = api.example.com
allowed_env_vars = SHARED_TOKEN
allowed_read_paths = /etc/ssl:/certs
[plugins.wasm_sandbox.grafana-glaber-datasource]
allowed_outbound_hosts = glaber:8080
allowed_env_vars = GLABER_TOKEN
Списки записываются в обычном для Grafana синтаксисе: значения через запятую, при необходимости в кавычках, либо массив JSON.
Вывод гостевого модуля в стандартные потоки вывода и ошибок, включая сообщения о паниках, перехватывается и записывается в лог сервера Grafana на уровне debug.
Гостевой ABI
WASM-модуль бэкенда взаимодействует с Grafana через небольшой набор экспортируемых функций и импортируемых хостовых функций. Полезная нагрузка кодируется protobuf-сообщениями pluginv2 — тем же проводным форматом, что использует драйвер gRPC, поэтому гостевой модуль может переиспользовать proto-типы Grafana Plugin SDK.
Экспорт
Модуль экспортирует только те функции, которые поддерживает. Любой отсутствующий экспорт обрабатывается так же, как gRPC-плагин, не зарегистрировавший соответствующую службу: вызов завершается ошибкой plugins.ErrMethodNotImplemented.
alloc(size uint32) uint32иfree(ptr uint32): Выделяют и освобождают память гостя. Хост никогда не пишет по адресу, который выбрал сам: он всегда сначала просит гостя выделить буфер.wasm_query_data: Обрабатываетpluginv2.QueryDataRequestи возвращаетpluginv2.QueryDataResponse.wasm_check_health: Обрабатываетpluginv2.CheckHealthRequestи возвращаетpluginv2.CheckHealthResponse.wasm_call_resource: Обрабатываетpluginv2.CallResourceRequestи возвращаетpluginv2.CallResourceResponse.
Каждая экспортируемая функция принимает единственный аргумент uint64 и возвращает единственный результат uint64. Буферы адресуются упакованным указателем (ptr<<32 | len), что удерживает ABI в пределах одного параметра и одного возвращаемого значения.
Импорт
Хостовый модуль grafana_host предоставляет единственные способы, которыми гость может выйти за пределы собственной памяти:
host_http_fetch: Выполняет исходящий HTTP-запрос. Гость передаёт упакованный указатель на JSON-полезную нагрузку с полямиmethod,url, необязательнымиheadersи необязательнымbody. Хост проверяет назначение по списку разрешений песочницы, выполняет запрос HTTP-клиентом Grafana и записывает обратно JSON-полезную нагрузку с полямиstatus,headers,bodyиerror. Принимаются только URL со схемамиhttpиhttps, а размер тела ответа ограничен.host_log: Передаёт строку лога в лог сервера Grafana на уровне debug.
Сборка модуля
Собирайте гостевой модуль как c-shared под цель wasip1, чтобы рантайм Go экспортировал _initialize, а не _start. Grafana запускает модуль через _initialize и возвращает управление хосту, не завершая гостя; поэтому модуль в стиле исполняемого файла с _start не работает.
GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared -o module/main.wasm ./cmd/plugin
Доступ к внешней сети
По умолчанию песочница отклоняет все исходящие запросы. Вызов host_http_fetch успешен, только если хост назначения совпадает с одним из следующих:
- Явный список разрешений. Хосты из
allowedOutboundHostsманифестаplugin.json(только для подписанных плагинов) и из секции[plugins.wasm_sandbox]конфигурации; сравниваются без учёта регистра. См. раздел Списки разрешений песочницы. - URL источника данных. Для плагина источника данных хост настроенного URL экземпляра источника данных всегда доступен, потому что Grafana берёт его из управляемых администратором настроек экземпляра, а не от гостя. Хост без порта не совпадает с тем же хостом с портом, и наоборот.
Заблокированный запрос завершается ошибкой с указанием хоста, и Grafana записывает предупреждение в лог.
Компиляция модуля и кэш
wazero не интерпретирует байткод на лету, а компилирует WASM-модуль в машинный код перед первым вызовом. Для крупного модуля (бэкенд на Go в WASM занимает порядка 30 МБ и десятки тысяч функций) эта компиляция занимает десятки секунд и происходит при каждом старте процесса Grafana. Размер файла при этом не главный фактор: время определяется количеством функций и объёмом кода, а не байтами на диске.
Чтобы не компилировать один и тот же модуль повторно, Grafana включает дисковый кэш компиляции wazero в каталоге данных (<data>/wasm-compilation-cache, см. [paths] data в конфигурации). Кэш:
- Ключуется по содержимому модуля и версии wazero, поэтому обновление плагина или wazero просто добавляет новую запись, а не переиспользует устаревшую.
- Разделяется между всеми WASM-плагинами: записи разных модулей не конфликтуют.
- Безопасен для нескольких процессов: записи создаются через временный файл и атомарное переименование, так что параллельные запуски Grafana не повредят кэш.
Первый запуск после чистой установки или обновления модуля остаётся медленным — это ожидаемо. Все последующие старты читают готовый результат с диска и занимают меньше секунды. Если каталог кэша недоступен для записи, Grafana пишет предупреждение в лог и продолжает работу, компилируя модуль без кэша.
Кэш можно безопасно удалить в любой момент: Grafana пересоздаст его при следующем старте.
Ограничения
- Только три вызова. Драйвер реализует
QueryData,CheckHealthиCallResource. Все остальные методы бэкенда, включая сбор метрик, чанковые запросы, потоковую передачу, admission и conversion, возвращаютplugins.ErrMethodNotImplemented. - Один ответ для ресурсов.
CallResource— это один обмен запросом и ответом. Гость не может передать несколько чанковCallResourceResponseпотоком. - Сериализованные вызовы. Grafana сериализует каждый вызов в один экземпляр модуля, поэтому параллельные запросы к одному плагину встают в очередь, а не выполняются одновременно.
- Нет горячей перезагрузки. Изменение модуля требует перезапуска плагина, который заново компилирует и создаёт его. Первый старт с новым модулем пройдёт без кэша (см. раздел «Компиляция модуля и кэш»), последующие — из кэша.
Устранение неполадок
Если WASM-плагин не загружается или падает во время работы:
- Проверьте лог сервера Grafana на уровне debug на строки
WASM plugin logиWASM plugin outputот гостя. - Убедитесь, что в
plugin.jsonуказано"backendRuntime": "wasm"и чтоwasmFilePathуказывает на существующий модуль. - Для заблокированных запросов найдите предупреждение
Blocked outbound WASM plugin request to disallowed hostи добавьте хост вallowedOutboundHostsманифестаplugin.jsonлибо вallowed_outbound_hostsсекции[plugins.wasm_sandbox]. Разрешения из манифеста неподписанного плагина игнорируются, поэтому в этом случае добавляйте хост в конфигурацию. - При ошибках
module closed with exit_code(0)проверьте, что модуль собран какc-sharedподwasip1и экспортирует_initialize, а не_start.