Перейти к содержимому
Документация

inquir.yaml: конфигурация сборки функций

inquir.yaml — единый файл сборки функций. У него одинаковая структура для Node.js, Python и Go, и браузерный редактор, inquir deploy и деплой из Git читают его одинаково. В нём объявляются пакеты ОС, нужные функции (build.system), и её языковые пакеты (build.dependencies). Файл читается при деплое; отдельных настроек в консоли, флагов CLI или полей API для этого нет.

Где лежит файл#

Положите inquir.yaml в корень файлов функции: в папку функции в дереве файлов редактора или в каталог функции, который вы деплоите через CLI или из Git. Файл уезжает вместе с кодом, поэтому все способы деплоя читают один и тот же файл. Имя inquir.yml тоже подходит; если есть оба, выигрывает inquir.yaml. Размер файла — до 64 КиБ.

Проект может держать и один общий inquir.yaml в своём корне — для всех функций. Когда вы деплоите SDK-функцию из подкаталога (inquir deploy --asset <name>), корневого файла в её архиве нет, поэтому CLI (версии после 0.4.1) отправляет его вместе с архивом, и платформа читает его как файл проекта. Деплой из Git читает только файл в том каталоге, который он деплоит.

Контейнерные приложения используют то же имя файла: version и services — это манифест приложения (см. Контейнерные приложения). Функции читают только build и functions.

Полный пример#

Блок build в начале файла действует для всех функций, которые деплоятся из этого каталога или проекта. Запись functions.<name> заменяет его для одной функции. У функции одна среда выполнения, поэтому каждый список зависимостей пишите в синтаксисе её среды.

inquir.yamlbash
# inquir.yaml: the same file for Node.js, Python and Go functions
build:                          # defaults for every function deployed from here
  system:
    presets: [ffmpeg]

functions:
  thumbnails:                   # replaces the defaults for this function (no merge)
    build:
      system:
        presets: [imagemagick, fonts]
        packages: [poppler-utils]          # Debian names; pin with name=version
      dependencies: [sharp@0.33.5]         # Node.js: name@version
  ocr:
    build:
      system:
        presets:
          - name: tesseract
            langs: [deu, rus]
      dependencies: ["pytesseract==0.3.13"] # Python: a pip requirement
  ids:
    build:
      system: null                         # no system packages for this function
      dependencies: [github.com/google/uuid@v1.6.0]   # Go: module@version

Справочник по ключам#

Все ключи необязательны. Неизвестные ключи отклоняются на верхнем уровне, в build, в build.system и в functions.<name>: опечатка вроде build.sytem проваливает деплой с подсказкой «возможно, вы имели в виду», а не теряет ваши пакеты молча. Браузерный редактор дополняет и проверяет файл по мере ввода.

КлючТипНазначение
buildсловарьНастройки сборки для всех функций, которые деплоятся из этого каталога или проекта.
build.systemсловарь или nullПакеты ОС, устанавливаемые в образ функции (Debian bookworm). null или пустой блок означают «без пакетов».
build.system.presetsсписок, до 8Пресеты платформы по имени. Параметры принимает только tesseract: { name: tesseract, langs: [deu, rus] }.
build.system.packagesсписок, до 10Дополнительные пакеты Debian по имени в apt, при желании с версией: name=version.
build.dependenciesсписок, до 200Языковые пакеты, которые читаются по правилам среды выполнения функции: name@version для Node.js, требование pip для Python, module@version для Go. Каждая запись — не длиннее 512 символов.
functions.<name>.buildсловарьТот же блок build для одной функции, по её имени. Он заменяет build файла поключево; списки никогда не объединяются.
version, services—Манифест контейнерного приложения. Функции эти ключи не читают.

Системные пакеты: build.system#

При деплое блок превращается в образ функции: компактная база Debian bookworm её среды выполнения плюс один слой с вашими пакетами, которые apt ставит из официальных репозиториев Debian bookworm. Ключ образа вычисляется по его содержимому, поэтому набор пакетов собирается один раз и используется всеми функциями, которые просят такой же набор, в любом рабочем пространстве, а деплой, меняющий только код, образ не пересобирает.

Пакеты ставятся только при сборке. Функции работают от непривилегированного пользователя и не могут выполнить apt-get install во время работы. Если убрать блок или сам файл, пакеты исчезнут при следующем деплое.

Системные пакеты доступны функциям на Node.js и Python. Для Go они скоро появятся: до тех пор деплой отклоняет build.system у Go-функции и сообщает об этом. Когда поддержка Go появится, менять файл и код не придётся.

Пресеты#

Пресет — это подобранный платформой версионированный набор пакетов Debian плюс переменные окружения, которых ждут его инструменты. Переменные зашиваются в образ; переменная окружения функции с тем же именем их переопределяет.

ПресетЧто ставитОкружение
ffmpegffmpeg: ffmpeg и ffprobe (FFmpeg 5.1)FFMPEG_PATH=/usr/bin/ffmpeg
FFPROBE_PATH=/usr/bin/ffprobe
imagemagickimagemagick (ImageMagick 6: convert, identify), ghostscript, fonts-dejavu-core. Политика ImageMagick в Debian оставляет PDF и PostScript выключенными.—
libvipslibvips-tools, libvips42: команда vips и библиотека для pyvips (sharp в Node поставляет свою libvips)—
popplerpoppler-utils: pdftotext, pdftoppm, pdfinfo—
tesseracttesseract-ocr 5 и tesseract-ocr-eng, плюс tesseract-ocr-<lang> для каждого элемента langsTESSDATA_PREFIX=/usr/share/tesseract-ocr/5/tessdata
ghostscriptghostscript (gs), интерпретатор PostScript и PDF—
fontsfonts-dejavu-core, fonts-liberation, fonts-noto-core, fonts-noto-cjk, fonts-noto-color-emoji — для рендеринга без экрана—

langs принимает до 8 кодов из списка: ara, bul, ces, chi-sim, chi-tra, dan, deu, ell, eng, est, fin, fra, heb, hin, hrv, hun, ind, ita, jpn, kat, kaz, kor, lav, lit, nld, nor, pol, por, ron, rus, slk, slv, spa, srp, swe, tha, tur, ukr, vie. Текущий каталог выводит inquir functions system presets.

Пакеты Debian: правила и ограничения#

  • Пишите имена пакетов bookworm так, как их знает apt: строчные буквы, цифры и + . -, первый и последний символ — буква или цифра, без ... Шаблоны apt и удаление пакетов так не записать; для компилятора C++ указывайте build-essential, а не g++. Каждое имя должно быть настоящим пакетом: до установки сборка проверяет все имена через apt-cache, а виртуальный пакет (тот, который только предоставляют другие пакеты) проваливает её с сообщением, что нужно указать пакет, который его предоставляет.
  • Версию фиксируют синтаксисом apt: name=version (например, tesseract-ocr-deu=5.3.0-2). Если Debian больше не поставляет зафиксированную версию, сборка образа провалится; обновите или уберите версию.
  • Не больше 10 пакетов на функцию (пресеты в лимит не входят) и не больше 8 пресетов. Каждый указывается один раз.
  • Демоны, инструменты повышения привилегий, ядра и пакетный инструментарий запрещены: systemd*, init, sudo, sudo-ldap, doas, openssh-server, docker*, containerd*, podman*, runc, crun, linux-image-*, linux-headers-*, *-dkms, apt-transport-*, apt, dpkg, cron, anacron, rsyslog, udev, dbus. Сборка также проверяет, что установлены ровно запрошенные пакеты и что ни инструмент повышения привилегий, ни SSH-сервер, ни контейнерный движок, ни пакет ядра (sudo, doas, openssh-server, docker*, containerd*, podman*, runc, crun, linux-image-*, linux-headers-*, *-dkms) не пришёл как зависимость, и снимает с файлов образа биты setuid и setgid.
  • Добавленный слой — до 1 ГБ в сжатом виде, сборка образа — до 15 минут.
  • Рабочее пространство может запросить не больше 10 новых наборов пакетов за 24 часа. Набор, который у платформы уже есть, потому что его раньше запрашивало любое рабочее пространство, не считается.
  • Собственные шаги сборки (system.run) недоступны.

Chromium без экрана для Puppeteer или Playwright#

Пресета для браузера нет: Chromium — это список пакетов, а его библиотеки apt подтянет сам. Запускайте /usr/bin/chromium с --no-sandbox (функции работают без привилегий), используйте puppeteer-core или playwright-core, чтобы браузер не скачивался, и дайте функции не меньше 1 ГБ памяти. Если библиотека читает PUPPETEER_EXECUTABLE_PATH, задайте его как переменную окружения функции со значением /usr/bin/chromium. Чтобы загружать внешние страницы, включите функции Сеть.

inquir.yamlbash
# inquir.yaml
build:
  system:
    packages: [chromium, fonts-liberation, fonts-noto-color-emoji]
  dependencies: [puppeteer-core@^23.0.0]   # no browser download; or list it in package.json
index.jsjs
const puppeteer = require('puppeteer-core');

// Give the function at least 1 GB of memory and turn on Network.
exports.handler = async (event) => {
  const browser = await puppeteer.launch({
    executablePath: '/usr/bin/chromium',
    args: ['--no-sandbox', '--disable-dev-shm-usage'],
  });
  try {
    const page = await browser.newPage();
    await page.goto(event.url || 'https://example.com', { waitUntil: 'networkidle2' });
    const pdf = await page.pdf({ format: 'A4' });
    return { statusCode: 200, body: JSON.stringify({ pdfBase64: Buffer.from(pdf).toString('base64') }) };
  } finally {
    await browser.close();
  }
};

Нативным модулям нужны -dev-пакеты#

Если у функции есть системные пакеты, установка её зависимостей (npm ci, npm install, pip install) идёт на её собственном образе, и нативные аддоны и исходные дистрибутивы компилируются с установленными библиотеками. Пресеты ставят только runtime-библиотеки, поэтому заголовочные файлы добавляйте сами: pycairo нужен packages: [libcairo2-dev, pkg-config], psycopg2 — libpq-dev. Аддону Node, который собирается через node-gyp, нужны ещё build-essential и python3.

Языковые пакеты: build.dependencies#

Один список и один ключ для всех сред выполнения. Функция читает его по правилам своей среды:

  • Node.js: name@version — имя npm-пакета с версией, диапазоном или dist-tag (sharp@0.33.5, zod@^3.23.0). Спецификации git, file, URL и алиасы оставьте в package.json.
  • Python: требование pip с ограничением версии (requests==2.32.3, uvicorn[standard]>=0.30). Маркеры окружения и прямые ссылки по URL оставьте в requirements.txt.
  • Go: module@version с точной версией или псевдоверсией (github.com/google/uuid@v1.6.0). Модулям версии v2 и выше нужен суффикс /vN в пути.

Деплой вливает список в сборочную копию исходников и никогда — в ваши файлы: редактор показывает ровно то, что вы написали. Записи Node.js попадают в dependencies файла package.json, записи Python — в requirements.txt, записи Go — в блок require в go.mod; недостающий файл создаётся. Затем платформа устанавливает пакеты — одинаково в handler- и web-режиме: npm ci --omit=dev при наличии lock-файла или npm install --omit=dev без него; pip install в .python_packages; для Go — go mod tidy и go mod vendor, после чего обработчик компилируется офлайн из vendor/. Обработчик на Node.js поставляет рядом с бандлом весь production-каталог node_modules и не включает установленные пакеты в бандл, поэтому нативные аддоны и пакеты, которые сами загружают свои файлы во время работы, работают ровно так, как установлены.

Задаче установки выделяется 1 ГБ памяти, 512 МиБ временного диска и 5 минут. Она обращается только к реестрам пакетов: npm (а также nodejs.org и GitHub, откуда нативные пакеты скачивают готовые бинарники), PyPI, прокси модулей Go и база контрольных сумм (proxy.golang.org, sum.golang.org; прямых загрузок из VCS нет). package.json, requirements.txt и go.mod продолжают работать, но объявлять пакеты стоит в inquir.yaml. Если пакет объявлен и там, и там с разными версиями, деплой проваливается, а не объединяет их молча; одинаковое объявление в обоих местах допустимо. pnpm-lock.yaml и yarn.lock не читаются: платформа ставит пакеты через npm и пишет об этом в логе деплоя.

Ещё деплой отклоняет — с сообщением, где названа запись:

  • один и тот же пакет дважды в списке;
  • добавленный пакет Node.js, который package-lock.json или npm-shrinkwrap.json не фиксирует с той же спецификацией (обновите lock-файл или используйте точные версии без него);
  • пакет Node.js, который package.json перечисляет в devDependencies: установка пропускает dev-зависимости;
  • requirements.txt с опциями pip (-r, -c, -e, --index-url, --hash…); go.mod, где перечисленный модуль заменён через replace или исключён через exclude либо строка go или toolchain требует Go новее 1.22, версии среды выполнения;
  • исходники, в которых уже лежат node_modules, .python_packages или vendor/.

Какое объявление действует#

build.system и build.dependencies ищутся по отдельности, в таком порядке. Действует первое найденное объявление; уровни и файлы никогда не объединяются.

  1. functions.<name>.build в собственном inquir.yaml функции
  2. build в собственном inquir.yaml функции
  3. functions.<name>.build в inquir.yaml в корне проекта
  4. build в inquir.yaml в корне проекта
  5. Ничего не найдено: у функции нет системных пакетов и дополнительных языковых пакетов.

Явные system: null, пустой блок или dependencies: [] — тоже объявление. Оно означает «ничего» и перекрывает значение по умолчанию из проекта.

Один файл, три среды выполнения: перекодирование через ffmpeg#

Один и тот же inquir.yaml подходит функциям на Node.js, Python и Go. Каждый обработчик берёт FFMPEG_PATH, который задаёт пресет, и уменьшает видео до 480p во временном каталоге.

inquir.yamlbash
# inquir.yaml (identical for Node.js, Python and Go)
build:
  system:
    presets: [ffmpeg]   # installs ffmpeg + ffprobe, sets FFMPEG_PATH and FFPROBE_PATH

Node.js#

index.jsjs
const { execFile } = require('node:child_process');
const { promisify } = require('node:util');
const fs = require('node:fs/promises');
const os = require('node:os');
const path = require('node:path');

const run = promisify(execFile);

exports.handler = async (event) => {
  const input = typeof event.body === 'string' ? JSON.parse(event.body || '{}') : event;
  const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'transcode-'));
  try {
    const src = path.join(dir, 'in.mp4');
    const out = path.join(dir, 'out.mp4');
    await fs.writeFile(src, Buffer.from(input.videoBase64, 'base64'));
    await run(process.env.FFMPEG_PATH || 'ffmpeg', [
      '-y', '-i', src, '-vf', 'scale=-2:480', '-c:v', 'libx264', '-preset', 'veryfast', out,
    ]);
    const mp4 = await fs.readFile(out);
    return { statusCode: 200, body: JSON.stringify({ mp4Base64: mp4.toString('base64') }) };
  } finally {
    await fs.rm(dir, { recursive: true, force: true });
  }
};

Python#

handler.pypython
import base64
import json
import os
import subprocess
import tempfile


def handler(event, context):
    raw = event.get("body")
    payload = json.loads(raw) if isinstance(raw, str) else event
    with tempfile.TemporaryDirectory(prefix="transcode-") as tmp:
        src = os.path.join(tmp, "in.mp4")
        out = os.path.join(tmp, "out.mp4")
        with open(src, "wb") as f:
            f.write(base64.b64decode(payload["videoBase64"]))
        subprocess.run(
            [os.environ.get("FFMPEG_PATH", "ffmpeg"), "-y", "-i", src,
             "-vf", "scale=-2:480", "-c:v", "libx264", "-preset", "veryfast", out],
            check=True, capture_output=True,
        )
        with open(out, "rb") as f:
            mp4 = base64.b64encode(f.read()).decode()
    return {"statusCode": 200, "body": json.dumps({"mp4Base64": mp4})}

Go#

Системные пакеты для Go скоро появятся; сейчас деплой отклоняет этот файл у Go-функции. Среда выполнения Go уже работает на той же базе Debian bookworm, и этот обработчик задеплоится без изменений, когда поддержка Go появится.

main.gogo
package main

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
)

func Handler(event map[string]interface{}, ctx map[string]interface{}) (interface{}, error) {
	payload := event
	if s, ok := event["body"].(string); ok && s != "" {
		payload = map[string]interface{}{}
		if err := json.Unmarshal([]byte(s), &payload); err != nil {
			return nil, err
		}
	}
	video, _ := payload["videoBase64"].(string)
	in, err := base64.StdEncoding.DecodeString(video)
	if err != nil {
		return nil, err
	}

	dir, err := os.MkdirTemp("", "transcode-")
	if err != nil {
		return nil, err
	}
	defer os.RemoveAll(dir)
	src, out := filepath.Join(dir, "in.mp4"), filepath.Join(dir, "out.mp4")
	if err := os.WriteFile(src, in, 0o600); err != nil {
		return nil, err
	}

	ffmpeg := os.Getenv("FFMPEG_PATH")
	if ffmpeg == "" {
		ffmpeg = "ffmpeg"
	}
	cmd := exec.Command(ffmpeg, "-y", "-i", src, "-vf", "scale=-2:480",
		"-c:v", "libx264", "-preset", "veryfast", out)
	if log, err := cmd.CombinedOutput(); err != nil {
		return nil, fmt.Errorf("ffmpeg: %v: %s", err, log)
	}

	mp4, err := os.ReadFile(out)
	if err != nil {
		return nil, err
	}
	body, _ := json.Marshal(map[string]string{"mp4Base64": base64.StdEncoding.EncodeToString(mp4)})
	return map[string]interface{}{"statusCode": 200, "body": string(body)}, nil
}

Сборка, статус и лог деплоя#

Лог деплоя показывает, что выбрала платформа, — в строках, которые начинаются с [system]. Если образ уже есть в кэше, деплой идёт дальше сразу. Новый набор пакетов сначала собирается: деплой ждёт в очереди, не задерживая другие деплои, и продолжается, когда образ готов. Первая сборка занимает минуты (пресет ffmpeg собирается примерно за 2,5 минуты); все следующие деплои, которые просят тот же набор, из любой функции, используют готовый образ. Если есть более старая сборка тех же пакетов (например, после обновления базового образа платформы), деплой идёт на ней, а функция переходит на новый образ, когда тот будет готов. Пока образ собирается или если его сборка провалилась, текущий релиз продолжает работать.

terminalbash
$ inquir deploy --asset transcode
[system] presets [ffmpeg] + packages [] from inquir.yaml (nodejs22, bookworm)
[system] system image: cache hit registry.inquir.org/inquir-sys/nodejs22@sha256:… +118 MB (shared)
…

# Declared block, image state, size, serving image and package lock
$ inquir functions system transcode

# The preset catalog (and the package limit for your workspace)
$ inquir functions system presets

inquir functions system <function> (в версии CLI после 0.4.1) показывает объявленный блок и файл, из которого он взят, состояние образа (queued, building, ready или failed), его размер, какой образ обслуживает функцию и lock-список пакетов — точную версию каждого пакета, добавленного сборкой. API отдаёт тот же статус по GET /functions/<id>/system-image, а каталог пресетов — по GET /functions/system-presets.

Чтобы повторить проваленную сборку образа, задеплойте ещё раз. Повторные попытки для одного набора пакетов идут с паузами 1, 5 и затем 30 минут.

Ошибки и как их исправить#

ОшибкаПричинаЧто сделать
400 invalid_inquir_yamlФайл — невалидный YAML, больше 64 КиБ, содержит неизвестный ключ или значение не той формы, либо запись build.dependencies некорректна или конфликтует с package.json, requirements.txt или go.mod.Сообщение называет ключ или запись. Исправьте и задеплойте снова.
400 invalid_system_packagesНеизвестный пресет или язык OCR, неизвестный ключ или значение не той формы внутри build.system, имя пакета не по правилам, запрещённый пакет, слишком много пакетов или повтор.Исправьте имя, на которое указывает сообщение; для пресета с опечаткой оно предлагает ближайший.
400 invalid_system_packages (Go)build.system у Go-функции: системные пакеты для Go скоро появятся.Пока уберите блок у Go-функции.
400 system_packages_deploy_onlyЗапрос пытался задать системные пакеты через API функции, а не деплоем.Объявите их в inquir.yaml и задеплойте.
429 system_image_quotaРабочее пространство запросило больше 10 новых наборов пакетов за 24 часа.Используйте уже собранный набор пакетов или задеплойте позже.
503 system_image_buildingВызван код, который ещё не деплоился (запуск из редактора), пока его образ собирается.Повторите после интервала из Retry-After.
409 system_image_failedТо же, но сборка образа провалилась.Исправьте inquir.yaml и задеплойте снова; ошибку сборки показывает inquir functions system <function>.
Сборка образа провалиласьПакета нет в bookworm или он виртуальный, зафиксированная версия пропала или добавленный слой больше 1 ГБ.Проверьте имя для bookworm на packages.debian.org, обновите или уберите версию либо разнесите тяжёлые инструменты по разным функциям. Ошибку сборки показывает inquir functions system <function>.
Установка падает: «pkg-config not found», «pg_config executable not found»Нативный пакет компилируется с библиотекой, заголовочные файлы которой не установлены.Добавьте -dev-пакет: libcairo2-dev и pkg-config для pycairo, libpq-dev для psycopg2.
runtimeVariant: fullВариант образа Full (Chromium) выведен из употребления.Уберите runtimeVariant и добавьте в inquir.yaml packages: [chromium, fonts-liberation, fonts-noto-color-emoji].

Варианты образов и слои#

У каждой среды выполнения теперь один компактный образ на Debian bookworm. Вариант Node.js Full (Chromium) выведен из употребления, а его переключатель убран из консоли. Запрос к API, который создаёт функцию с runtimeVariant: full или переводит функцию на него, отклоняется с 400 и подсказкой про inquir.yaml; если отправить то значение, которое у функции уже есть, запрос пройдёт. Chromium ставится списком пакетов, как показано выше.

Слои продолжают работать у функций, которые их используют, но это больше не рекомендуемый способ добавлять зависимости: языковые пакеты объявляйте в build.dependencies, пакеты ОС — в build.system.