Разработка Serverless Functions для сайта (AWS Lambda)
AWS Lambda — функции, которые запускаются по событию и не требуют постоянно работающего сервера. Вы платите только за фактическое время выполнения: первые 1M запросов в месяц бесплатно, далее $0.20 за 1M запросов. Холодный старт — единственный серьёзный недостаток, решаемый несколькими способами.
Когда Lambda подходит для сайта
Подходит: обработка форм обратной связи, генерация PDF/изображений по запросу, вебхуки от платёжных систем и CRM, ресайз изображений при загрузке, планировщики задач (cron через EventBridge), API-прокси для сторонних сервисов.
Не подходит: долгоживущие соединения (WebSocket нужен отдельный сервис), задачи дольше 15 минут, высокочастотные операции с БД без connection pooling.
Структура проекта
my-lambda/
├── src/
│ ├── handlers/
│ │ ├── contact-form.ts
│ │ ├── image-resize.ts
│ │ └── webhook.ts
│ ├── services/
│ │ ├── email.ts
│ │ └── storage.ts
│ └── utils/
├── template.yaml # SAM или serverless.yml
├── package.json
└── tsconfig.json
Пример функции: обработка контактной формы
import { APIGatewayProxyHandler } from "aws-lambda";
import { SESClient, SendEmailCommand } from "@aws-sdk/client-ses";
import { z } from "zod";
const ses = new SESClient({ region: "eu-west-1" });
const ContactSchema = z.object({
name: z.string().min(2).max(100),
email: z.string().email(),
message: z.string().min(10).max(2000),
});
export const handler: APIGatewayProxyHandler = async (event) => {
const headers = {
"Access-Control-Allow-Origin": "https://your-site.com",
"Content-Type": "application/json",
};
try {
const body = JSON.parse(event.body || "{}");
const data = ContactSchema.parse(body);
await ses.send(new SendEmailCommand({
Source: "[email protected]",
Destination: { ToAddresses: ["[email protected]"] },
Message: {
Subject: { Data: `Новое сообщение от ${data.name}` },
Body: {
Text: { Data: `От: ${data.name} <${data.email}>\n\n${data.message}` }
}
}
}));
return { statusCode: 200, headers, body: JSON.stringify({ ok: true }) };
} catch (error) {
if (error instanceof z.ZodError) {
return { statusCode: 400, headers, body: JSON.stringify({ errors: error.errors }) };
}
console.error(error);
return { statusCode: 500, headers, body: JSON.stringify({ error: "Internal error" }) };
}
};
Деплой через AWS SAM
template.yaml:
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Globals:
Function:
Runtime: nodejs20.x
Timeout: 10
MemorySize: 256
Environment:
Variables:
NODE_ENV: production
Resources:
ContactFormFunction:
Type: AWS::Serverless::Function
Properties:
Handler: dist/handlers/contact-form.handler
Events:
Api:
Type: HttpApi
Properties:
Path: /contact
Method: POST
Policies:
- SESCrudPolicy:
IdentityName: your-site.com
npm run build
sam build
sam deploy --guided # первый деплой
sam deploy # последующие
Холодный старт и его устранение
Холодный старт Lambda на Node.js 20 — 200–500 мс. На Python — 100–300 мс. После первого вызова функция остаётся «тёплой» несколько минут.
Способы снижения:
- Provisioned Concurrency — Lambda держит N экземпляров всегда прогретыми. Стоит ~$0.015 за час на один экземпляр. Для критичных эндпоинтов с SLA.
-
Уменьшение размера бандла — используйте esbuild вместо webpack, не включайте
aws-sdkв бандл (он уже есть в среде), tree-shaking. - SnapStart (Java) — снимок состояния после инициализации.
- Пингер — EventBridge раз в 5 минут вызывает функцию с warmup-payload.
// Инициализация вне handler — выполняется один раз при холодном старте
const dbClient = new DynamoDBClient({ region: "eu-west-1" });
const sesClient = new SESClient({ region: "eu-west-1" });
export const handler = async (event) => {
// handler использует уже инициализированные клиенты
};
Работа с базой данных
Стандартное TCP-соединение к PostgreSQL/MySQL не подходит для Lambda — каждый вызов создаёт новое соединение, при 1000 RPS база захлёбывается.
Решения:
- RDS Proxy — пул соединений перед RDS, Lambda подключается к прокси. +$0.015 за vCPU/час RDS.
- DynamoDB — нет соединений, нативный serverless. Для простых структур данных.
- PlanetScale / Neon — serverless-совместимые базы данных с HTTP API.
Логирование и трейсинг
import { Logger } from "@aws-lambda-powertools/logger";
import { Tracer } from "@aws-lambda-powertools/tracer";
const logger = new Logger({ serviceName: "contact-form" });
const tracer = new Tracer({ serviceName: "contact-form" });
export const handler = tracer.captureLambdaHandler(async (event) => {
logger.addContext(context);
logger.info("Processing contact form", { email: event.body?.email });
// ...
});
AWS Lambda Powertools — официальная библиотека для структурированного логирования, трейсинга через X-Ray и метрик через CloudWatch EMF.
Сроки
Одна Lambda-функция с деплоем через SAM — 1–2 дня. Набор из 5–7 функций с CI/CD через GitHub Actions и мониторингом — 5–7 дней.







