Оптимизация Cold Start для Serverless Functions

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.
Разработка и обслуживание любых видов сайтов:
Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Оптимизация Cold Start для Serverless Functions
Средняя
~2-3 рабочих дня
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    874
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    851

Оптимизация Cold Start для Serverless Functions

Cold start — это задержка при первом вызове Lambda после периода простоя или при масштабировании на новый экземпляр. Состоит из нескольких фаз: создание контейнера (~100–500ms), инициализация runtime (~50–200ms для Node.js), выполнение init-кода вашей функции (зависит от вас). Первые две фазы почти не поддаются оптимизации — всё, что можно сделать, это работать с третьей.

Реальные цифры для Node.js 20 на AWS Lambda: без оптимизаций init-фаза занимает 300–800ms. После оптимизаций — 50–150ms. На arm64 (Graviton2) runtime-фаза на 10–20% быстрее x86.

Что происходит при cold start

Фазы cold start:
1. Container init    │ ~100-500ms  │ AWS управляет, не оптимизируется
2. Runtime init      │ ~50-200ms   │ зависит от runtime и архитектуры
3. Function init     │ ВАШ КОД    │ require/import, DB-подключения, SDK init
4. Handler execution │ ВАШ КОД    │ собственно работа функции

Профилирование init-фазы через переменную окружения:

# Добавляем в Lambda environment
AWS_LAMBDA_EXEC_WRAPPER=/opt/aws-lambda-exec-wrapper
# или используем встроенное профилирование

Лучший способ замерить — CloudWatch Logs. Ищем строку Init Duration в отчёте:

REPORT RequestId: abc123
Duration: 45.23 ms
Billed Duration: 46 ms
Memory Size: 512 MB
Max Memory Used: 89 MB
Init Duration: 312.45 ms  ← это cold start overhead

Уменьшение размера бандла

Главая причина медленного cold start — большой бандл с ненужными модулями.

До оптимизации:

// Плохо — импортируем весь AWS SDK
import AWS from 'aws-sdk';
const s3 = new AWS.S3();
const dynamo = new AWS.DynamoDB.DocumentClient();

После:

// Хорошо — только нужные клиенты, AWS SDK v3 модульный
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3';
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
import { DynamoDBDocumentClient, GetCommand } from '@aws-sdk/lib-dynamodb';

// Инициализируем один раз вне handler
const s3 = new S3Client({ region: process.env.AWS_REGION });
const dynamo = DynamoDBDocumentClient.from(new DynamoDBClient({}));

Вынесение AWS SDK из бандла (он уже есть в Lambda runtime для Node.js 18+):

// esbuild конфиг
{
  "external": ["@aws-sdk/*"],
  "bundle": true,
  "minify": true,
  "target": "node20",
  "platform": "node"
}

Размеры до/после. Типичный Express-приложение с aws-sdk v2:

  • До: 8–15 MB zip
  • После: 500KB–2MB zip

Lazy loading для редко используемых модулей

// Плохо — всё загружается при init
import { createCanvas } from 'canvas';
import sharp from 'sharp';
import { PDFDocument } from 'pdf-lib';

export const handler = async (event) => {
  if (event.type === 'generate-pdf') {
    // используется 5% вызовов
    const pdf = await PDFDocument.create();
  }
};

// Хорошо — тяжёлые модули только когда нужны
export const handler = async (event) => {
  if (event.type === 'generate-pdf') {
    const { PDFDocument } = await import('pdf-lib');
    const pdf = await PDFDocument.create();
  }
};

Инициализация вне handler

Код вне handler выполняется один раз при cold start и переиспользуется между горячими вызовами:

import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';

// INIT PHASE — один раз
const client = new DynamoDBClient({
  // Переиспользуем TCP-соединения
  requestHandler: {
    requestTimeout: 3000,
    httpsAgent: { keepAlive: true, maxSockets: 50 },
  },
});
const dynamo = DynamoDBDocumentClient.from(client);

// Переменные окружения читаем один раз
const TABLE_NAME = process.env.TABLE_NAME!;
const STAGE = process.env.STAGE ?? 'dev';

// HANDLER — вызывается каждый раз
export const handler = async (event) => {
  // dynamo уже инициализирован, соединение переиспользуется
  const result = await dynamo.send(new GetCommand({
    TableName: TABLE_NAME,
    Key: { pk: event.userId, sk: 'profile' },
  }));

  return result.Item;
};

Оптимизация подключений к базе данных

Обычный пул соединений (pg Pool, Sequelize) не работает в serverless — каждый экземпляр Lambda создаёт своё подключение, при 1000 concurrent executions получаем 1000 подключений к PostgreSQL.

Решение 1: RDS Proxy (AWS-managed connection pooler):

// Подключаемся к RDS Proxy, не напрямую к RDS
import { Pool } from 'pg';

const pool = new Pool({
  host: process.env.RDS_PROXY_ENDPOINT,  // xxx.proxy-xxx.rds.amazonaws.com
  database: process.env.DB_NAME,
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  max: 1,          // 1 соединение на экземпляр Lambda
  idleTimeoutMillis: 0,
  connectionTimeoutMillis: 5000,
});

// Переиспользуем между горячими вызовами
let cachedClient: pg.PoolClient | null = null;

export const getDbClient = async () => {
  if (!cachedClient) {
    cachedClient = await pool.connect();
  }
  return cachedClient;
};

Решение 2: PlanetScale или Neon — HTTP-based serverless databases без постоянного соединения:

import { neon } from '@neondatabase/serverless';

// Каждый запрос — HTTP, не TCP
const sql = neon(process.env.DATABASE_URL!);

export const handler = async (event) => {
  const users = await sql`SELECT id, email FROM users WHERE active = true LIMIT 10`;
  return users;
};

Provisioned Concurrency

Provisioned Concurrency — AWS держит N экземпляров Lambda всегда прогретыми. Init-фаза выполняется заранее, пользователь получает ответ без задержки.

# serverless.yml или SAM template
Resources:
  ApiFunction:
    Type: AWS::Serverless::Function
    Properties:
      # ...
      AutoPublishAlias: live

  ApiProvisionedConcurrency:
    Type: AWS::Lambda::ProvisionedConcurrencyConfig
    Properties:
      FunctionName: !Ref ApiFunction
      Qualifier: live
      ProvisionedConcurrentExecutions: 5  # держим 5 горячих экземпляров

Через Serverless Framework:

functions:
  api:
    handler: src/handler.main
    provisionedConcurrency: 5
    # Автоматическое масштабирование через Application Auto Scaling
    # настраивается отдельно через AWS Console или CDK

Provisioned Concurrency стоит денег (оплачивается даже в idle), поэтому используется только для критических эндпоинтов.

arm64 vs x86_64

Переключение на Graviton2 (arm64) — простейшая оптимизация с нулевыми изменениями кода:

# SAM template
Globals:
  Function:
    Architectures: [arm64]  # было x86_64

Выигрыш: ~10–20% меньше init duration, ~20% дешевле по тарифам AWS. Единственное ограничение: нативные Node.js модули (.node файлы) нужно пересобирать под arm64. Обычные JS/TS модули работают без изменений.

Сроки

Аудит и базовая оптимизация бандла (esbuild, tree shaking, вынос AWS SDK) — 1 день. Переработка инициализации с вынесением клиентов из handler, настройка RDS Proxy — 2–3 дня. Настройка Provisioned Concurrency с мониторингом и автоскейлингом — 1–2 дня. Полный цикл: от аудита до production с измеримым результатом — 1 неделя.