Первый счёт, который удивляет команду, работающую с серверлесс, редко бывает за вычисления. Это счёт за простаивающую базу данных, за приём логов или за передачу данных между регионами — за те части, которые никто не считал «серверлесс», когда рисовал архитектуру на доске. Сами функции почти ничего не стоят. Всё вокруг них — вот где живут деньги и боль.
В этом суть серверлесс-архитектуры: обещание честное — не нужно патчить серверы, масштабирование до нуля, оплата за вызов, — но дисциплина, которую она на самом деле требует, отличается от того, что предполагает обещание. Вы не перестаёте думать об инфраструктуре. Вы начинаете думать о ней в месте, которого не видите. Эта статья именно об этом разрыве: как выглядит работающий серверлесс-дизайн, один полный разбор с реальными цифрами и конкретные способы, которыми модель отказывает на своих собственных условиях.
Что на самом деле меняет серверлесс — механически
Традиционный сервис держит процесс в памяти. У него есть жизненный цикл, которым вы управляете: он запускается, прогревается, держит соединения открытыми и умирает, когда вы скажете. У FaaS-функции (AWS Lambda, Google Cloud Functions, Azure Functions) такого жизненного цикла нет. Платформа запускает экземпляр по событию, выполняет ваш обработчик, а затем замораживает или уничтожает его. Вы не можете гарантированно сохранить состояние между вызовами.
Этот единственный факт каскадом влияет на всё остальное:
- Пулы соединений — это ловушка. Классическое приложение открывает 20 соединений с базой данных при запуске и переиспользует их. Функция Lambda под нагрузкой запускает сотни параллельных экземпляров, каждый из которых открывает своё соединение. Реляционная база данных с несколькими сотнями слотов для соединений быстро исчерпывается, и ошибки выглядят как таймауты, а не как «у вас закончились соединения».
- Холодные старты — это ограничение дизайна, а не баг. Первый вызов на новом экземпляре оплачивает инициализацию. Для небольшого обработчика на Node.js это десятки миллисекунд; для JVM-сервиса, подтягивающего тяжёлый фреймворк, это может быть секунда или больше. Вы чувствуете это на пользовательских API и не чувствуете на фоновых задачах.
- Вы платите за астрономическое время, а не за процессорное. Функция, которая тратит 900 мс на ожидание медленного стороннего API, оплачивается за 900 мс. Это переворачивает старый инстинкт: на сервере ожидание бесплатно, а процессорное время драгоценно; в FaaS ожидание — это счёт.
Один разбор: конвейер загрузки изображений, сделанный правильно
Возьмём конкретную задачу. Пользователи загружают фотографии через веб-приложение; для каждой загрузки нужны миниатюра, проверка содержимого и запись в базе данных. Вот как это строит практик и что ломается на каждом шаге, если делать лениво.
Шаг 1 — Загрузка не идёт через функцию. Клиент запрашивает предварительно подписанный URL у небольшой конечной точки API, затем загружает файл напрямую в объектное хранилище (S3 или эквивалент). Если вместо этого передавать файл через Lambda, вы упрётесь в лимит синхронного запроса Lambda в 6 МБ, а даже ниже этого вы платите за каждую миллисекунду передачи. Предварительно подписанные URL полностью убирают байты с ваших вычислений.
Шаг 2 — Хранилище испускает событие. Событие создания объекта запускает функцию обработки. В этом весь смысл событийно-ориентированной архитектуры: сервис хранения — производитель, ваша функция — потребитель, и нет цикла опроса, сжигающего деньги.
Шаг 3 — Выполняйте работу с дедлайном. Измените размер, запустите проверку содержимого, запишите метаданные. Установите таймаут функции осознанно — скажем, 30 секунд, — а не на платформенный максимум. Функция, зависающая на полный 15-минутный лимит Lambda, пока накапливаются повторные попытки, — это то, как маленький баг превращается в большой счёт.
Шаг 4 — Обращайтесь с базой данных как с дефицитным ресурсом. Записывайте метаданные в DynamoDB (который нормально справляется с соединениями на каждый запрос), а не открывайте новое соединение с реляционной базой данных при каждом вызове. Если вам действительно нужна реляционная семантика, поставьте перед ней RDS Proxy или используйте Aurora Serverless, который масштабирует ёмкость по требованию, — но понимайте, что Aurora Serverless масштабируется шагами, а не мгновенно, и внезапный всплеск может столкнуться с задержкой масштабирования.
Шаг 5 — Сделайте повторные попытки безопасными. Источники событий повторяют попытки при сбое. Если ваш обработчик не идемпотентен, один неудачный вызов с последующими двумя повторами означает три миниатюры, три строки в базе данных или три попытки списания. Используйте детерминированный ключ (ключ объекта плюс версия) и проверку перед записью или условную запись.
Теперь цифры. Предположим, вы обрабатываете 1 миллион изображений в месяц. Каждая функция выполняется 800 мс при 512 МБ памяти. Ценообразование Lambda — за ГБ-секунду, то есть 1 000 000 × 0,8 с × 0,5 ГБ = 400 000 ГБ-секунд. При опубликованной ставке примерно 0,0000166667 доллара за ГБ-секунду это около 6,67 доллара за вычисления в месяц. Хранение, запросы и передача данных, вероятно, будут стоить больше, чем функции. Это честная версия серверлесс-обещания: вычисления почти бесплатны, и именно они не являются источником вашего счёта.
Где ломается сама модель
Это не ошибки новичков. Это отказы схемы в условиях, для которых она не была спроектирована.
Потолок параллелизма наступает без предупреждения
У каждой учётной записи есть региональный лимит параллелизма в Lambda (по умолчанию — низкие тысячи, настраивается по запросу). У нижестоящего сервиса — платёжного API, устаревшей внутренней конечной точки — может быть гораздо более низкий лимит. Когда трафик подскакивает, все ваши функции срабатывают одновременно, обрушиваются на нижестоящий сервис, и он начинает возвращать ошибки. Повторные попытки усиливают нагрузку. Система не деградирует плавно; она отказывает в виде стада громовержцев.
Как заметить это рано: следите за метрикой одновременного выполнения в сравнении с настройкой зарезервированного параллелизма и установите зарезервированный параллелизм для функции, которая вызывает хрупкий сервис, чтобы она не могла превысить то, что этот сервис может выдержать. Если уже слишком поздно: поставьте очередь (SQS) между событием и функцией, чтобы потребитель забирал с контролируемой скоростью, а не получал push.
Граница состояния протекает
Кто-то кэширует сессию пользователя в переменной уровня модуля, предполагая, что она сохраняется. Она сохраняется — на том единственном тёплом экземпляре, в течение непредсказуемого времени, и не на следующем. Тесты проходят локально, а затем производство показывает, что пользователи случайно выходят из системы или, что хуже, видят кэшированные данные другого пользователя. Нет чистого способа обнаружить это по логам; вы узнаёте об этом, когда клиент сообщает.
Что делать: считайте каждую функцию не имеющей состояния по правилу и храните общее состояние во внешнем хранилище (DynamoDB, Redis, сервис сессий). Если вам нужно кэшировать, кэшируйте только данные, которые безопасно устаревают, и никогда не привязывайте их к запросу конкретного пользователя.
Холодный старт попадает на критический путь
Синхронный API за API Gateway имеет бюджет задержки в несколько сотен миллисекунд. Холодный старт на более тяжёлой среде выполнения может съесть большую его долю, и это происходит именно тогда, когда трафик низкий и идёт масштабирование — в момент, когда новый пользователь приходит впервые. При устойчивой нагрузке экземпляры остаются тёплыми, и проблема исчезает, поэтому её легко пропустить при тестировании.
Как заметить это рано: измеряйте p99 задержки, а не среднее. Provisioned Concurrency поддерживает заданное число экземпляров тёплыми за реальную ежемесячную плату — вы платите за простаивающую ёмкость, то есть за то, что серверлесс должен был устранить. Компромисс явный: платите за тепло или принимайте хвостовую задержку.
Источник событий меняет свой контракт
Управляемый источник событий (объектное хранилище, очередь сообщений, сторонний вебхук) обновляет формат полезной нагрузки или семантику повторных попыток. Ваш обработчик был написан под старую форму. Ничего не падает громко — поля приходят пустыми, записи пропускаются, и конвейер молча ничего не обрабатывает в течение дня. Событийно-ориентированные системы по умолчанию отказывают тихо.
Что делать: проверяйте схему события в начале обработчика и громко падайте при несоответствии, держите очередь недоставленных сообщений для всего, что не может быть обработано, и оповещайте о глубине DLQ, а не только об ошибках функции.
Пути обучения и что каждый на самом деле стоит
Есть три реалистичных пути сюда, и они различаются больше в обратной связи, чем в цене.
| Путь | Время до рабочей компетенции | Стоимость | Что вы на самом деле получаете |
|---|---|---|---|
| Самостоятельное изучение по документации и учебникам | Долго и неравномерно | Почти нулевые деньги | Вы хорошо усваиваете счастливый путь и плохо — режимы отказа, потому что учебники редко показывают вам счёт или стадо громовержцев |
| Создание реального побочного проекта | Недели, с обходными путями | Небольшой облачный счёт, иногда неожиданный | Лучший учитель по стоимости и идемпотентности — если вы действительно следите за метриками и счётом |
| Структурированный курс | Недели | Плата за курс | Последовательность, закрывающая пробелы, оставленные самообучением: почему выбор дизайна стоит столько, сколько стоит, и кто-то, кто проверит ваши рассуждения |
Честный компромисс: самообучение дёшево и работает, если вы дисциплинированно ломаете свою собственную систему. Ловушка в том, что вы не видите ошибок, которые не знаете, где искать, — отсутствующий ключ идемпотентности, пул соединений, шторм повторных попыток, — пока они не окажутся в производстве. Чтение об наблюдаемости здесь помогает; статья Наблюдаемость в продакшене — хороший компаньон для мониторинга, а CI/CD как искусство охватывает дисциплину развёртывания, которая серверлессу всё ещё нужна.
Структурированный путь заслуживает своего места, когда он заставляет вас пройти через расчёты и случаи отказа по порядку, с обратной связью по вашим решениям, а не просто с готовым примером. Именно этот пробел призван закрыть курс Серверлесс-архитектура — он проходит через рассуждения о стоимости и дизайне с конкретными примерами, а уроки адаптируются к вашему уровню, а не ведут всех по одному сценарию.
Кому это подходит — и кому нет
Это подходит вам, если вы уже пишете код и что-то развернули, даже небольшой сервис, и хотите понять, почему серверлесс-системы ведут себя так, как ведут, под нагрузкой и в счёте. Это подходит командам, переносящим рабочую нагрузку на Lambda или Cloud Functions, которым нужно рассуждать о параллелизме и стоимости до миграции, а не после.
Это не подходит вам, если вы ищете первый курс по программированию — материал предполагает, что вы умеете читать обработчик и понимать HTTP-запрос. Это также не подходит, если ваша рабочая нагрузка — длительный процесс, ограниченный процессором: серверлесс будет бороться с вами по лимитам таймаута и стоимости, а контейнер на Fargate или Cloud Run — более честный инструмент. Серверлесс не универсально лучше; он лучше для событийно-ориентированной, пиковой, кратковременной работы, и он плохо подходит для стабильных тяжёлых вычислений.
Настоящий навык — не запоминание сервисов. Это способность посмотреть на дизайн и предсказать, где он сломается и сколько будет стоить. Это предсказание отличает того, кто выпускал серверлесс, от того, кто только читал о нём, — и именно для этого нужен структурированный курс с реальной обратной связью. Начните с разбора конвейера выше, постройте его один раз сами, следите за счётом и p99 задержкой, и если пробелы в ваших рассуждениях станут узким местом, курс Серверлесс-архитектура — это место, где их закрыть.
Начать можно здесь: Serverless Architecture.