Вы написали сервис — теперь его надо где-то запустить, чтобы он работал круглосуточно и был доступен из интернета. В AWS для этого есть не один способ, а целых четыре, и каждый решает задачу «куда поселить код» по-своему. Новичка это сбивает с толку: какой выбрать и почему их так много?
Хорошая новость — выбор подчиняется простой логике. Все четыре варианта — это лестница от «полного ручного управления» до «вообще не думаю о серверах». Чем выше по лестнице, тем меньше у вас забот, но тем больше ограничений и (иногда) выше цена. Разберём каждую ступеньку на понятных аналогиях, а в конце дадим таблицу-шпаргалку и разбор типичных ошибок.
Снизу вверх растёт то, что берёт провайдер, и убывает свобода: у EC2 всё ваше, у Lambda только код и жёсткие лимиты. Ориентир для большинства команд лежит посередине: ECS на Fargate без опыта Kubernetes, EKS с опытом, Lambda точечно.
EC2 — виртуальные машины
EC2 (Elastic Compute Cloud) — это виртуальный компьютер в облаке. Представьте, что вы арендуете пустой системный блок: на нём есть операционная система, а всё остальное вы ставите и настраиваете сами. Установить рантайм, накатить обновления безопасности, поднять ваше приложение, перезапускать его если упало, добавлять новые машины при росте нагрузки — всё это ваша забота.
За это вы получаете максимальный контроль. Нужна конкретная версия ядра Linux, особое железо (GPU для машинного обучения), или вы переносите старое приложение «как есть» — EC2 даёт такую свободу.
Платой за свободу становится объём ручной работы. Запускать новый сервис «прямо на EC2 руками» сегодня почти никто не советует: это всё равно что собирать мебель без инструкции, когда рядом стоит готовый шкаф. Масштабирование настраивается через Auto Scaling Groups — механизм, который сам добавляет и убирает машины по нагрузке, но и его нужно сконфигурировать.
Запустить виртуальную машину можно из консоли в браузере, но в реальных проектах это делают командой или описанием в коде. Минимальный пример через AWS CLI:
aws ec2 run-instances \
--image-id ami-0abcdef1234567890 \
--instance-type t3.micro \
--key-name my-key
Здесь image-id — образ операционной системы, instance-type — размер машины (сколько процессора и памяти), key-name — ключ для входа по SSH. Уже из этого видно: про каждую такую деталь придётся думать вам.
ECS — контейнеры без Kubernetes
Сначала про контейнеры. Контейнер — это коробка, в которую упаковано ваше приложение вместе со всем, что ему нужно для работы (рантайм, библиотеки, настройки). Коробка одинаково запускается на ноутбуке и в облаке — «у меня работало, а на сервере нет» исчезает как класс. Самый популярный формат таких коробок — Docker.
ECS (Elastic Container Service) — это собственный сервис AWS для запуска контейнеров. Он берёт на себя роль управляющего: следит, чтобы нужное число копий приложения было запущено, распределяет к ним трафик, перезапускает упавшие. Сначала описывают, что именно запускать — какой контейнер, сколько ему дать памяти и процессора; этот рецепт называется task definition. Потом говорят, сколько копий держать и как балансировать между ними нагрузку, — это service.
У ECS есть два режима запуска. Можно крутить контейнеры на ваших EC2-машинах (тогда сами машины — снова ваша забота), а можно на Fargate — это режим, где вы вообще не видите серверов: говорите «запусти контейнер с такими-то ресурсами», и AWS сам находит, где его разместить. Fargate сильно упрощает жизнь: нет машин, которые надо патчить.
Плюс ECS — простота. Здесь нет сложных концепций Kubernetes (никаких CRD, Helm, операторов), а интеграция с остальным AWS работает из коробки: права доступа через IAM, балансировщик ALB, логи в CloudWatch. Минус — привязка к одному облаку. Знания и конфиги ECS не переедут в другое облако, а сторонних инструментов вокруг ECS заметно меньше, чем вокруг Kubernetes.
Fargate против машин в ECS
У ECS два режима, и выбор между ними — самое частое решение в этой теме.
Режим с машинами (EC2): узлы ваши, вы их патчите и масштабируете, зато получаете полный контроль — любые типы машин, GPU, привилегированные контейнеры, локальные диски, плотная упаковка нескольких контейнеров на узел (а значит дешевле за единицу ресурса).
Fargate: машин нет вовсе, вы задаёте задаче процессор и память, платите за них по секундам. Ограничения, которые надо знать до выбора:
- Комбинации процессора и памяти фиксированные. Нельзя попросить «0.4 vCPU и 3 ГБ»; выбирают из сетки (0.25, 0.5, 1, 2, 4, 8, 16 vCPU с допустимыми диапазонами памяти для каждого). Значит, задача, которой нужно 2.5 ГБ, оплачивается как ближайший больший вариант.
- Нет GPU и нет специальных типов машин — всё, что требует конкретного оборудования, остаётся на EC2.
- Нет привилегированных контейнеров и ограничен доступ к ядру узла: часть агентов наблюдаемости и сетевых инструментов не работает.
- Место на диске — эфемерное, по умолчанию 20 ГБ (увеличивается до 200 ГБ настройкой), и оно исчезает вместе с задачей.
- Дороже за единицу ресурса — обычно на десятки процентов относительно плотно упакованных машин.
Отсюда правило выбора: Fargate, пока не мешает одно из ограничений выше. Экономия на обслуживании узлов почти всегда перекрывает наценку — особенно в маленькой команде, где патчить узлы всё равно некому. На EC2 переходят, когда появился GPU, когда нужен особый агент, или когда счёт вырос настолько, что разница за единицу ресурса стала заметна в абсолютных деньгах.
EKS — managed Kubernetes
Kubernetes (часто пишут k8s) — это открытый стандарт оркестрации контейнеров: тот же «управляющий», что и ECS, но не привязанный к AWS и с огромной экосистемой вокруг. Его сложность в том, что им надо управлять — а это отдельная работа.
EKS (Elastic Kubernetes Service) снимает часть этой ноши: AWS берёт на себя control plane — мозг кластера, который принимает решения о размещении контейнеров. А вот узлы (ноды, где реально крутятся контейнеры) остаются на вас — либо как managed node groups (AWS помогает их обновлять), либо снова на Fargate.
EKS выбирают, когда в компании уже есть опыт с Kubernetes или нужна переносимость между облаками: всё, что вы знаете про k8s, здесь применимо дословно. Если хотите глубже разобраться в самом Kubernetes — начните с основ, деплоя и конфигурации и эксплуатации.
Честная оговорка: кластер остаётся кластером. Обновления версий, дополнения (аддоны), настройка прав через IRSA, сетевые плагины — эта работа никуда не девается, её становится лишь меньше. Без человека, который этим занимается, кластер за год обрастает непропатченными версиями и заброшенными компонентами.
Считать при выборе надо не только людей, но и фиксированную плату: за управляющий слой EKS берёт 0,10 доллара в час за кластер — около 73 долларов в месяц, независимо от того, крутится в кластере что-то или он пустой. Для одного небольшого сервиса это заметная строчка, для двадцати — уже нет.
Lambda — функции
Lambda — это «код без серверов вообще». Вы загружаете кусок кода (функцию), а AWS сам запускает его в ответ на событие: пришёл файл в хранилище, прилетело сообщение в очередь, наступило время по расписанию. Платите только за миллисекунды, пока код реально работал; в простое — почти ноль.
Это идеально для клея между сервисами: обработать загруженную картинку, среагировать на сообщение, запустить задачу по расписанию, принять редкий веб-хук. Когда событий нет — вы не платите ни за что.
Где проходит граница: Lambda и тяжёлые сервисы
Запустить полноценный backend-сервис целиком в Lambda технически можно, но это плавание против течения. Главная причина — холодный старт (cold start). Когда функцию долго не вызывали, при следующем вызове AWS поднимает её с нуля: для тяжёлого рантайма (например, среды с большим набором зависимостей, которая при запуске поднимает весь контекст приложения) это занимает секунды. Для синхронного API это задержка, которую пользователь видит своими глазами.
Лекарства есть, но каждое возвращает ту сложность, от которой Lambda обещала избавить:
- provisioned concurrency — заранее оплаченные «прогретые» экземпляры, которые всегда наготове;
- минималистичные рантаймы без тяжёлых фреймворков;
- SnapStart — снимок уже прогретого процесса: вместо долгой инициализации функция поднимается из готового снимка за доли секунды (работает для Java, Python и .NET). Вместе с provisioned concurrency его не включить — выбирают что-то одно.
Дальше идут другие ограничения. Максимум 15 минут на один вызов — длинную задачу так не выполнить. Для синхронного API важнее другая пара чисел: ответ функции не может быть больше 6 МБ, а перед ней обычно стоит API Gateway, который по умолчанию ждёт ответа 29 секунд. Модель «один запрос — один экземпляр» ломает привычные пулы соединений к базе данных: каждый экземпляр открывает своё соединение, и база захлёбывается — приходится ставить прослойку вроде RDS Proxy. Отладка и локальный запуск тоже сложнее, чем у контейнеров.
Простое правило: Lambda — для событийного клея и редких задач; постоянный сервис с трафиком — в контейнер. Идея «перепишем все сервисы на функции» почти всегда оказывается дороже и медленнее, чем выглядела на презентации. Подробнее о serverless-подходе — в статье про serverless.
App Runner — контейнер без кластера
Между «поднять контейнер» и «разбираться с ECS или Kubernetes» есть ещё одна ступенька, о которой знают меньше всего: App Runner. Вы даёте ему образ из реестра (или прямо репозиторий с кодом), а он поднимает сервис, выдаёт адрес по HTTPS с сертификатом, сам масштабирует по числу запросов и сам умеет уходить в ноль экземпляров при отсутствии трафика.
Ни кластера, ни балансировщика, ни описания задач и сервисов — одна настройка на сервис:
aws apprunner create-service --service-name orders \
--source-configuration '{"ImageRepository":{"ImageIdentifier":"123456789012.dkr.ecr.eu-central-1.amazonaws.com/orders:1.4.2","ImageRepositoryType":"ECR","ImageConfiguration":{"Port":"8080"}}}'
Когда это правильный выбор: один-два обычных веб-сервиса, образ уже есть, опыта Kubernetes нет и заводить его не хочется, трафик небольшой или неровный.
Чем платят: меньше контроля (нет тонкой настройки сети, ограниченные размеры, свой набор возможностей), цена за единицу ресурса выше, чем у Fargate, и привязка к сервису сильнее — переезд на ECS означает переписывание описания развёртывания. Поэтому App Runner берут как быстрый старт и осознанно, зная, куда с него уходить.
Как трафик попадает в сервис
Во всех четырёх вариантах приложение надо ещё и открыть наружу, и делается это по-разному.
EC2 и ECS — балансировщик приложений (ALB). Он принимает запросы на своём адресе, разбирает HTTP, смотрит на имя хоста и путь и отправляет запрос в целевую группу (target group) — список адресов и портов, куда можно отправлять трафик. Машины ASG или задачи ECS регистрируются в целевой группе сами; проверка работоспособности тоже настраивается на ней. Отсюда практическая схема: балансировщик в публичных подсетях, целевая группа с приложением в закрытых, правило маршрутизации связывает имя и путь с группой.
EKS — тот же балансировщик, но создаётся из кластера. Контроллер балансировщика читает объекты Ingress или Service типа LoadBalancer и создаёт под них ALB или NLB в вашем аккаунте. То есть манифест кластера превращается в настоящий балансировщик AWS — и в счёт за него.
Lambda — три пути. Через шлюз API (когда нужны аутентификация, ограничение частоты, преобразование запросов), через тот же ALB (когда функция живёт рядом с остальными сервисами и правило маршрутизации проще шлюза) и через Function URL — встроенный адрес HTTPS прямо у функции, без всякого шлюза. Последний — самый простой способ дать функции адрес: одна настройка, свой домен *.lambda-url.<регион>.on.aws, встроенная проверка подписи IAM или открытый доступ. У него нет возможностей шлюза (ключи, квоты, кэш, преобразования), зато нет и его цены и задержки.
App Runner адрес выдаёт сам — это часть сервиса, отдельно ничего создавать не нужно.
Сравнение и как выбрать
| EC2 | ECS/Fargate | EKS | Lambda | |
|---|---|---|---|---|
| Профиль | Особые требования | Постоянные сервисы, команда без k8s | Постоянные сервисы, есть опыт k8s | События, клей, расписание |
| Ручной труд | Весь на вас | Минимальный | Нужны люди под платформу | Почти нулевой |
| Переносимость | Высокая | Низкая (только AWS) | Манифесты — да, обвязка (IRSA, сетевой плагин, ALB) — нет | Низкая |
| Дружелюбность к рантайму | Полная | Полная | Полная | С оговорками (cold start) |
| Цена при постоянной нагрузке | Низкая (с резервированием) | Средняя | Средняя | Высокая |
| Цена при редких вызовах | Платите за простой | Платите за простой | Платите за простой | Около нуля |
Ориентир для большинства команд: есть опыт Kubernetes — берите EKS; нет, и второе облако не нужно — ECS/Fargate; для событийного кода рядом — Lambda точечно. EC2 — только когда есть явная причина, которую вы можете записать и обосновать. А если сервис один, образ готов и ни кластера, ни тонкой настройки не нужно — App Runner из раздела выше избавляет от половины вопросов.
Запуск сервиса — это лишь начало. Дальше встаёт вопрос, как доставлять туда новые версии кода без простоя: про это — статьи о принципах пайплайнов и стратегиях релизов.
Где это применяется
Этот выбор делает каждая команда при запуске первого сервиса в AWS, и он определяет всю дальнейшую эксплуатацию — поэтому ошибиться здесь дорого. Вот что чаще всего идёт не так у новичков:
-
Lambda как универсальная архитектура. Сто функций вместо пяти сервисов превращаются в запутанный клубок: один запрос пользователя проходит через десяток функций, каждая со своим холодным стартом, а проследить путь запроса почти невозможно.
-
EKS «потому что модно». Кластер без человека, который им занимается, деградирует за год. Если выделенной команды под платформу нет — ECS честнее и спокойнее.
-
EC2 со «своим Docker». Самодельный оркестратор из bash-скриптов и systemd — это тот же ECS, только написанный плохо и без поддержки.
-
Забыть выключать ненужное. Fargate и Lambda дороже за единицу ресурсов, но дешевле в простое; стейджи и песочницы, которые молотят на EC2/EKS по ночам и выходным, — прямой слив бюджета. Про это подробнее — в статье об оптимизации затрат.
-
Основы AWS — как устроены аккаунты, регионы и базовые сервисы.
-
IAM — права доступа, которые сервис получает в каждом из вариантов запуска.
-
Масштабирование и доступность — как переживать рост нагрузки и отказы.
-
Managed-данные — где будут жить базы данных и очереди, к которым обращается сервис.
-
Интеграция с AWS SDK — код обращения к сервисам AWS почти одинаков во всех четырёх вариантах.
Глубже: три способа платить за машинырасширенное
В таблице ниже стоит «низкая цена с резервированием», и это требует пояснения, потому что за одну и ту же машину AWS берёт очень по-разному.
On-demand — платите по факту, за секунды работы, без обязательств. Самый дорогой вариант за единицу времени и самый гибкий: выключили — платить перестали. Это значение по умолчанию и правильный выбор, пока вы не знаете свой профиль нагрузки.
Spot — та же машина из свободных мощностей, дешевле on-demand в разы (обычно на 60–90 %), но AWS может забрать её обратно, предупредив за две минуты. Подходит всему, что переносит внезапную остановку: сборка, обработка очередей, аналитика, часть узлов кластера. Не подходит тому, что не переживёт потерю экземпляра посреди работы без подготовки.
Резервирование — обязательство пользоваться определённым объёмом год или три в обмен на скидку (обычно 30–60 % в зависимости от срока и предоплаты). Сейчас это обычно оформляют не как «зарезервированную машину», а как обязательство по сумме расходов в час (Savings Plans): оно гибче и распространяется в том числе на Fargate и Lambda.
Практический порядок: запустили на on-demand, месяц-два посмотрели на реальный ровный минимум потребления, на этот минимум взяли обязательство, пики оставили on-demand, а фоновые задачи перевели на spot. Подробный разбор — в статье про затраты.
Глубже: откуда берётся образ: ECRрасширенное
Про контейнеры удобно рассуждать, пока не встаёт вопрос, где живёт образ. В AWS для этого есть свой реестр — ECR (Elastic Container Registry): приватный, в вашем аккаунте, по одному на регион, с правами через IAM.
Путь «собрал — положил — запустил» выглядит так:
aws ecr create-repository --repository-name orders
aws ecr get-login-password | docker login --username AWS --password-stdin \
123456789012.dkr.ecr.eu-central-1.amazonaws.com
docker build -t 123456789012.dkr.ecr.eu-central-1.amazonaws.com/orders:1.4.2 .
docker push 123456789012.dkr.ecr.eu-central-1.amazonaws.com/orders:1.4.2
Четыре вещи, которые полезно знать сразу. Вход в реестр даётся временным паролем на 12 часов — в конвейере это отдельный шаг перед публикацией, а на машине он делается заново каждый день. Права на скачивание образа выдаются роли задачи или роли узла, то есть ECS и EKS тянут образ без всяких секретов — это приятное отличие от внешнего реестра. Сканирование на уязвимости встроено и включается на репозиторий одной настройкой. И реестр надо чистить: правило жизненного цикла удаляет старые образы по возрасту или числу, иначе через год за хранение платят больше, чем за вычисления.
Если образы уже лежат в другом реестре, их можно тянуть и оттуда, но тогда понадобится секрет с учётными данными, а из закрытой подсети — ещё и выход в интернет через преобразование адресов. Отсюда и практика: образы для AWS кладут в ECR, даже если сборка идёт в другом месте.
Глубже: соединения с базой из функциирасширенное
Про Lambda сказано, что каждый экземпляр открывает своё соединение и база захлёбывается, и упомянута прослойка. Вторая половина лечения — в самом коде, и без неё прослойка не спасёт.
Соединение открывают вне обработчика, на уровне инициализации, и переиспользуют между вызовами:
# вне обработчика: выполняется один раз на экземпляр функции
pool = create_pool(size=1)
client = create_service_client()
def handler(event, context): # вызывается много раз на том же экземпляре
with pool.connection() as conn:
...
Почему это работает: экземпляр функции после вызова не уничтожается, а остаётся «тёплым» и обслуживает следующие вызовы — статические поля переживают вызов. То есть соединение открывается один раз на экземпляр, а не один раз на запрос, и число соединений равно числу одновременно работающих экземпляров, а не числу запросов.
Три правила к этому. Пул внутри функции делают маленьким (одно-два соединения): один экземпляр обрабатывает один вызов за раз, больше ему не нужно, а сумма по экземплярам и есть нагрузка на базу. Всё тяжёлое, что можно создать один раз (клиенты сервисов, разборщики, модели), создают там же, вне обработчика — это заодно уменьшает холодный старт. И ограничивают одновременность самой функции (reserved concurrency): это единственный способ поставить потолок числу соединений, потому что иначе всплеск трафика превращается в сотни экземпляров и сотни соединений.
С прослойкой (RDS Proxy) это сочетается, а не заменяется: она держит пул к базе и отдаёт соединения функциям, но функция всё равно должна открывать соединение вне обработчика — иначе каждый вызов будет платить за установку соединения к прослойке.
Коротко
- Четыре варианта это лестница от полного контроля к отсутствию серверов: EC2, ECS на Fargate, EKS и Lambda; чем выше, тем меньше забот и больше ограничений.
- За машины платят тремя способами: on-demand по факту, spot в разы дешевле, но с отзывом за две минуты, обязательство на год или три за скидку на ровный минимум.
- Образы живут в ECR: реестр приватный, по одному на регион, вход временным паролем на 12 часов, права роли задачи, из закрытой подсети через точку доступа.
- Fargate берут, пока не мешает одно из ограничений: фиксированная сетка процессора и памяти, нет GPU и привилегированных контейнеров, эфемерный диск, наценка за единицу ресурса.
- EKS снимает управляющий слой за 0,10 доллара в час, узлы, обновления и IRSA остаются на вас; без человека под платформу кластер деградирует за год.
- Lambda для событийного клея и редких задач: холодный старт, потолок 15 минут, ответ до 6 МБ; постоянный сервис с трафиком живёт в контейнере. App Runner это контейнер без кластера для одного-двух сервисов.
- Трафик приходит через ALB и целевую группу, в EKS балансировщик создаёт контроллер из Ingress, у Lambda три пути: шлюз API, ALB или Function URL.
- Соединение с базой из функции открывают вне обработчика и переиспользуют между вызовами; RDS Proxy дополняет это, а не заменяет.
Что почитать дальше
- Масштабирование и доступность — как копии за балансировщиком переживают рост и отказы.
- Serverless в AWS — события, холодный старт и лимиты Lambda подробно.
- Managed-данные — где будут жить базы и очереди, к которым обращается сервис.
- Интеграция с AWS SDK — код обращения к сервисам одинаков во всех вариантах запуска.