Слово serverless («бессерверный») звучит так, будто серверов нет вообще. Это не так — серверы есть, просто вы их не видите и не управляете ими. AWS сам поднимает машину, когда нужно выполнить ваш код, и гасит её, когда работа закончена. Вы не патчите операционную систему, не настраиваете масштабирование, не платите за простаивающие инстансы — платите только за фактическое время, пока код работал.
Главный кирпич serverless в AWS — это Lambda: сервис, в который вы загружаете функцию (кусок кода), а AWS запускает её в ответ на событие. Такой подход часто называют FaaS — Function as a Service, «функция как сервис». Если вы ещё не разбирались с тем, какие вообще бывают варианты запуска кода в облаке, полезно сперва заглянуть в обзор вычислений AWS — там Lambda стоит рядом с виртуальными машинами и контейнерами. Здесь же разберём, как serverless устроен изнутри и когда его стоит брать.
Событийная модель
Обычный сервер — это программа, которая постоянно запущена и ждёт запросов. Lambda устроена наоборот: код «спит», пока не случилось событие (event) — что-то, на что функция должна отреагировать. Произошло событие — AWS запускает функцию, она отрабатывает и завершается. Нет событий — нет запущенного кода и нет счёта за работу.
Источников событий много, и в этом сила модели. Несколько типичных:
- HTTP-запрос от пользователя — приходит через API Gateway (о нём ниже);
- новое сообщение в очереди SQS;
- загрузка файла в хранилище S3 (например, картинку залили — функция сделала превью);
- запись изменилась в базе DynamoDB (через DynamoDB Streams);
- наступило время по расписанию (раз в час, каждую ночь);
- событие пришло на шину EventBridge от другого сервиса.
Из этой модели следует ключевое свойство — автоматическое масштабирование. Пришёл один запрос — AWS запустил одну копию функции. Пришла тысяча запросов одновременно — он запустит тысячу копий параллельно. Вам не нужно настраивать группы автомасштабирования и балансировщики, как для обычных серверов (это подробно разобрано в статье про масштабирование и доступность). Поэтому неровная, «скачущая» нагрузка — когда то пусто, то густо — это родная стихия Lambda.
Cold start
У событийной модели есть обратная сторона, и про неё спрашивают чаще всего — это cold start («холодный старт»).
Разберём механику. Когда функция отработала, AWS не убивает её мгновенно — он держит «тёплую» среду выполнения какое-то время, чтобы следующий вызов был быстрым (это называется warm start, «тёплый старт»). Но если вызовов долго нет, среда «остывает» и удаляется. И тогда следующий вызов требует поднять всё заново: выделить машину, скачать ваш код, запустить рантайм (среду исполнения языка), выполнить инициализацию — подключения к базе, чтение настроек. Вся эта подготовка добавляется к времени ответа первого запроса. Вот эта добавка и есть cold start.
Аналогия: тёплый старт — это машина, которая уже заведена и едет. Холодный старт — это когда машина стояла всю ночь на морозе, и сперва её нужно завести и прогреть. Едет она потом одинаково, но первая поездка после простоя дольше.
Как с этим борются:
- Provisioned concurrency — заранее держать заданное число «тёплых» функций наготове. Они не остывают, поэтому cold start пропадает — но за эту готовность приходится платить, даже когда вызовов нет.
- Меньше зависимостей и легче пакет — чем меньше кода и библиотек нужно загрузить и проинициализировать, тем быстрее старт.
- SnapStart — функция инициализируется один раз, AWS делает «снимок» (snapshot) готовой среды и потом запускает копии из него, минуя долгую инициализацию. Эта возможность доступна для нескольких рантаймов (Java, Python, .NET) и для них заметно сокращает cold start без доплаты за provisioned concurrency.
- Лёгкий рантайм — у одних языков среда исполнения стартует за миллисекунды, у других «тяжёлых» рантаймов старт самого окружения заметно дольше. Для синхронных API с жёсткими требованиями к скорости ответа это учитывают при выборе языка.
Важный практический вывод: cold start вполне терпим для фоновой и событийной обработки (никто не заметит, если разбор файла начнётся на полсекунды позже), но неприятен для синхронного API, где пользователь ждёт ответа прямо сейчас. Это один из главных критериев, брать Lambda под задачу или нет.
Лимиты Lambda
У функции есть жёсткие ограничения, которые важно знать заранее, чтобы не упереться в них в самый неподходящий момент:
- Время выполнения — максимум 15 минут (900 секунд) на один вызов. Это потолок, его нельзя поднять. Долгие задачи на Lambda просто не помещаются.
- Память — до 10 240 МБ на функцию. Причём процессорная мощность привязана к памяти: чем больше памяти выделили, тем больше vCPU функция получит. Иногда добавляют памяти не ради неё самой, а чтобы ускорить вычисления.
Эти лимиты — ещё одна причина не пытаться засунуть в Lambda всё подряд. Если задача идёт дольше 15 минут или требует постоянной тяжёлой работы — это сигнал смотреть на контейнеры или виртуальные машины.
API Gateway и Step Functions
Lambda редко живёт в одиночку. Два сервиса дополняют её чаще всего.
API Gateway — это управляемый «вход» для HTTP-запросов перед вашими функциями. Сама по себе Lambda не умеет принимать запросы из интернета по адресу вроде https://api.example.com/orders — у неё нет своего веб-сервера. API Gateway берёт это на себя: принимает HTTP-запрос, проверяет авторизацию, ограничивает частоту обращений (rate limiting), направляет запрос нужной функции и отдаёт ответ обратно клиенту. Так из набора функций получается полноценный REST API.
Простейший вызов функции через AWS CLI выглядит так:
aws lambda invoke --function-name create-order --cli-binary-format raw-in-base64-out --payload '{"item":"book"}' response.json
Флаг --cli-binary-format raw-in-base64-out здесь обязателен. В AWS CLI версии 2 (она ставится по умолчанию) параметр --payload ожидает данные в base64, и если передать сырой JSON без этого флага, команда упадёт с ошибкой Invalid base64. Флаг говорит CLI принять JSON как есть. Чтобы не дописывать его каждый раз, можно один раз выполнить aws configure set cli-binary-format raw-in-base64-out. В старой версии 1 команда работала и без флага — отсюда и старые примеры, которые на CLI 2 уже не запускаются.
Step Functions — это оркестратор многошаговых процессов. Представьте сценарий: «принять заказ → списать деньги → зарезервировать товар → отправить письмо, а если на каком-то шаге ошибка — откатить назад». Можно было бы написать это как цепочку функций, вызывающих друг друга, но такая самодельная логика быстро превращается в запутанный клубок, где непонятно, на каком шаге всё застряло.
Step Functions описывают процесс как машину состояний (state machine): явная последовательность шагов, ветвления («если успех — туда, если ошибка — сюда»), повторные попытки и паузы между шагами. Логика становится наглядной, а в консоли AWS видно, какой шаг сейчас выполняется и где что пошло не так. Описывается машина состояний декларативно, в формате JSON:
{
"StartAt": "ChargePayment",
"States": {
"ChargePayment": {
"Type": "Task",
"Resource": "arn:aws:lambda:eu-west-1:123456789012:function:charge",
"Next": "ReserveItem"
},
"ReserveItem": {
"Type": "Task",
"Resource": "arn:aws:lambda:eu-west-1:123456789012:function:reserve",
"End": true
}
}
}
Когда serverless оправдан, а когда нет
Главный навык — не «уметь писать Lambda», а понимать, где она к месту, а где навредит.
Serverless хорош, когда:
- нагрузка событийная или «скачущая» — то пусто, то наплыв; платить за простой обидно, а пиков не предугадать;
- задачи нерегулярные — ночная выгрузка отчёта, реакция на загрузку файла, обработка сообщений из очереди;
- нужно склеить несколько сервисов между собой небольшим кусочком кода;
- работа фоновая, и доли секунды задержки на cold start никого не смущают.
Serverless проигрывает, когда:
- нагрузка постоянная и высокая — тогда предсказуемее и дешевле держать контейнеры или инстансы, которые работают всё время;
- задачи долгие — упираются в лимит 15 минут;
- нужен очень быстрый и стабильный ответ синхронного API, а cold start этому мешает;
- у задачи сложное состояние, которое неудобно держать в эфемерной функции, живущей лишь время вызова.
Главная мысль: serverless — инструмент под определённый класс задач, а не замена всему остальному. «Всё на Lambda» — такая же крайность, как «ничего на Lambda». Выбирают осознанно, по характеру нагрузки.
Где это применяется
Serverless встречается повсюду, где есть событийная или нерегулярная работа: обработка загруженных пользователями файлов, реакция на сообщения в очередях, ночные задания по расписанию, склейка микросервисов, чат-боты, webhook-приёмники. Многие проекты используют Lambda не как фундамент всей системы, а точечно — там, где постоянный сервер был бы пустой тратой денег.
Типичные ошибки новичков:
- Игнорировать cold start до того, как пользователи пожалуются на медленный первый ответ. Если строите синхронный API с требованием к скорости — закладывайте provisioned concurrency или SnapStart с самого начала, а не как пожарную меру.
- Пытаться засунуть в Lambda долгую задачу и упереться в лимит 15 минут на середине обработки. Длинные процессы — это Step Functions, контейнеры или специальные сервисы пакетной обработки.
- Складывать всю логику в одну гигантскую функцию. Lambda хороша маленькими, сфокусированными функциями — это и стартует быстрее, и проще тестировать.
- Забывать про оплату за provisioned concurrency — её держат запущенной всегда, и счёт идёт даже без вызовов. Это бьёт по стоимости, если включить «на всякий случай» и забыть.
Что учить дальше. Чтобы видеть полную карту вариантов запуска кода, пройдите обзор вычислений и масштабирование и доступность — поймёте, чем Lambda отличается от серверов и контейнеров. Для хранения данных под serverless-функции естественная пара — DynamoDB, база, которая так же масштабируется без управления серверами. Полезно посмотреть управление доступом через IAM: функции нужны права, чтобы читать очередь или писать в базу. А чтобы не описывать функции, шлюзы и машины состояний кликами в консоли, осваивайте инфраструктуру как код — начните с основ IaC и Terraform или CloudFormation.