V8 Isolates vs Containers: почему мы выбрали isolates
Когда мы проектировали Edge Functions, главным вопросом была модель изоляции. Как запускать пользовательский код безопасно, быстро и с минимальными накладными расходами? В этой статье разберём два основных подхода и объясним, почему выбрали V8 isolates.
Проблема холодного старта
Традиционные serverless-платформы (AWS Lambda, Google Cloud Functions) используют контейнеры для изоляции. Каждый вызов функции требует:
- Поднять контейнер (50-500 мс)
- Загрузить runtime (Node.js, Python)
- Импортировать зависимости
- Выполнить код
Если контейнер уже «тёплый» — отлично, код выполняется за миллисекунды. Но после периода неактивности контейнер убивается, и следующий запрос получает полный холодный старт.
На практике холодные старты в AWS Lambda занимают 100-500 мс для Node.js и до 1-2 секунд для Java/.NET.
Для edge computing это неприемлемо. Пользователь ожидает, что edge-функция отработает за 10-50 мс, а не за полсекунды.
V8 Isolates: другой подход
V8 — движок JavaScript из Chrome. Помимо JIT-компиляции, V8 предоставляет механизм isolates — изолированные контексты выполнения внутри одного процесса.
Ключевое отличие от контейнеров:
| Характеристика | Контейнеры | V8 Isolates |
|---|---|---|
| Время создания | 50-500 мс | <5 мс |
| Память на инстанс | 50-128 МБ | 2-10 МБ |
| Изоляция | Процесс/namespace | Heap isolation |
| Языки | Любые | JS/TS/WASM |
Isolate — это не отдельный процесс, а изолированный heap внутри V8. Каждый isolate имеет свой набор объектов, и код из одного isolate не может получить доступ к памяти другого.
Как это работает на практике
При деплое функции мы:
- Парсим и компилируем код в байткод V8
- Сохраняем скомпилированный snapshot
- При запросе — создаём isolate и загружаем snapshot
Создание isolate из snapshot занимает микросекунды, а не миллисекунды. Это позволяет держать тысячи функций «горячими» на одном сервере.
// Так выглядит простая edge-функция
export default {
async fetch(request) {
const country = request.headers.get('cf-ipcountry');
return new Response(`Hello from ${country}!`);
}
};
Безопасность
Главный вопрос: достаточно ли изоляции V8 для запуска недоверенного кода?
V8 isolates обеспечивают:
- Memory isolation — каждый isolate имеет отдельный heap, нет shared memory между isolates
- CPU limits — мы ограничиваем CPU time через V8 API
- No filesystem/network — isolate не имеет доступа к syscalls напрямую, только через наш API
Дополнительно мы используем seccomp-bpf для ограничения syscalls на уровне ОС и запускаем worker-процессы от непривилегированного пользователя.
Ограничения
V8 isolates — не серебряная пуля. Ограничения:
- Только JS/TS/WASM — нельзя запустить Python или Go
- Нет native modules — npm-пакеты с C++ биндингами не работают
- CPU time limits — долгие вычисления прерываются (50 мс по умолчанию)
- Memory limits — 128 МБ на isolate
Для большинства edge-сценариев эти ограничения не критичны. Edge-функции должны быть быстрыми и лёгкими — это валидация, роутинг, трансформация, а не тяжёлые вычисления.
Результаты
После перехода на V8 isolates мы получили:
- P50 latency: 8 мс (было 120 мс с контейнерами)
- P99 latency: 25 мс (было 450 мс)
- Memory: 3 МБ/функция (было 64 МБ)
- Density: 10,000 функций/сервер (было 200)
Это позволяет держать все функции «горячими» и обеспечивать стабильно низкую задержку независимо от паттерна нагрузки.
Заключение
V8 isolates — правильный выбор для edge computing, где критичны latency и плотность. Контейнеры остаются лучшим выбором для workloads, требующих произвольных языков, native библиотек или долгих вычислений.
Если вы строите что-то на edge — попробуйте Edge Functions. Первые 100K запросов бесплатно каждый месяц.