Первый сервис команда подняла на виртуальной машине: поставили среду выполнения, скопировали сборку, запустили. Через год сервисов двадцать, на каждой машине свой способ запуска, обновления накатывают по ночам руками, а половина машин простаивает. Другая команда с первого дня взяла Kubernetes «потому что так делают все» и полгода чинила кластер вместо продукта.
Обе ошибки — про выбор уровня вычислений. У любого облака их три, и различаются они тем, что провайдер берёт на себя, а что оставляет вам. В этой статье разберём эти уровни, два механизма, которые прилагаются к каждому, и правило выбора.
Чем выше уровень, тем больше берёт на себя провайдер и тем меньше остаётся свободы: у функции нет ни машины, ни долгих процессов, ни своего окружения, зато нет и ночных обновлений руками.
Уровень первый: виртуальная машина
Виртуальная машина — сервер с операционной системой, который вы получаете через минуту после запроса. Провайдер отвечает за железо и гипервизор, всё остальное ваше: обновления системы, установка среды выполнения, запуск и перезапуск процесса, логи, диски. Это максимум контроля и максимум ответственности.
Берут машину, когда нужно именно это: перенести сервис «как есть» с его systemd-юнитом и cron, поставить то, что в контейнер не заворачивается, или получить полный контроль над ядром и сетью. Машины не устарели, они просто редко бывают лучшим первым выбором для обычного веб-сервиса.
Уровень второй: контейнер
Контейнер — упакованный образ приложения со всем окружением. На втором уровне провайдер запускает его сам, и здесь два подхода. Без оркестратора: вы отдаёте образ и лимиты, платформа поднимает столько копий, сколько нужно под нагрузку, вплоть до нуля, когда обращений нет; кластера, узлов и их обновлений вы не видите. С оркестратором: провайдер держит управляющую часть Kubernetes, а вы работаете с рабочими узлами и манифестами.
Разница — в цене сложности. Контейнер без кластера — самый простой способ запустить обычный HTTP-сервис, и для одного-двух сервисов он почти всегда лучше. Kubernetes окупается, когда сервисов и команд много и нужны единый выкат, общая сеть между сервисами и тонкая настройка размещения; для двух сервисов это оркестратор ради оркестратора.
Уровень третий: функция
Функция — кусок кода, который облако вызывает на событие: HTTP-запрос, сообщение в очереди, файл в хранилище, время по расписанию. Сервера нет вовсе: платите за миллисекунды работы, в простое ноль. Это идеальный клей между сервисами — обработать загруженную картинку, среагировать на событие, выполнить задачу по расписанию.
Ограничения тоже жёсткие. Функция засыпает без нагрузки, и первый вызов после паузы ждёт, пока окружение поднимется, — холодный старт: у Go это десятки миллисекунд, у Node и Python сотни, у JVM с большим пакетом зависимостей секунды. Есть потолок времени выполнения и размера ответа. Поэтому целый бэкенд в функциях — плавание против течения; функции берут под события, а сервис с постоянным трафиком запускают контейнером.
Балансировщик и автомасштабирование
К любому уровню, кроме одиночной машины, прилагаются два механизма. Балансировщик принимает запросы на одном адресе и раздаёт их между копиями, а копии, не ответившие на проверку здоровья, убирает из ротации. Автомасштабирование меняет число копий по нагрузке в заданных границах: минимум, максимум, целевая загрузка процессора или число запросов на копию.
Оба держатся на проверке здоровья, и здесь главная ловушка. Ответ «порт открыт» ещё не значит «готов работать»: сервис мог подняться, но не подключиться к базе. Проверять надо готовность — отдельный путь, который отвечает «ок» только когда сервис реально может обслуживать запросы. Без этого балансировщик шлёт трафик в неготовую копию, а автомасштабирование плодит копии, которые не помогают.
Как выбирать
Идите снизу вверх по потребности, а не сверху вниз по моде. Нужно перенести сервис как есть или нужен контроль над машиной — виртуальная машина. Один-два сервиса с неравномерной нагрузкой — контейнер без кластера. Много сервисов, несколько команд, единый выкат и команда, знающая Kubernetes, — оркестратор. Редкие события и склейка между сервисами — функции. Самая частая ошибка — начать с Kubernetes ради одного сервиса; вторая — держать сервис с постоянным трафиком в функциях и удивляться холодным стартам и счёту.
Глубже: что общего у всех трёх уровней внутрирасширенное
Под всеми тремя уровнями лежат одни и те же машины провайдера. Контейнерная платформа — это машины, на которых провайдер сам держит среду выполнения и кладёт туда ваши образы; функции — те же контейнеры, которые провайдер поднимает на событие и гасит через минуты простоя. Поэтому холодный старт функции — это время на запуск контейнера с вашим кодом, а «масштабирование до нуля» — просто удаление контейнеров, за которые вы перестаёте платить.
Отсюда же растёт разница в цене. Секунда работы функции стоит дороже секунды той же машины, потому что вы платите ещё и за готовность провайдера поднять её мгновенно. При постоянной нагрузке машина или контейнер с фиксированным числом копий выходят дешевле, при редкой — функция.
Глубже: тонкости автомасштабированиярасширенное
Автомасштабирование реагирует не мгновенно: метрика собирается раз в минуту, копия поднимается минуту-две, и на резкий всплеск трафика оно опаздывает. Поэтому границу «минимум копий» ставят с запасом на пик, а не по ночной нагрузке, и держат буфер в виде очереди там, где всплеск можно переждать.
Уменьшение копий опаснее увеличения: копия, которую гасят, может обрабатывать запросы, и без корректной остановки они оборвутся. Платформы дают время на завершение и сигнал перед остановкой; как приложение должно на него отвечать, разобрано в разделе про остановку приложения.
Глубже: путь образа до боевой среды: реестр, сборка, выкатрасширенное
«Вы отдаёте образ» на втором уровне звучит просто, а откуда платформа его берёт, кто его собрал и как выкатывается новая версия, это отдельная цепочка с четырьмя звеньями.
Реестр. У каждого провайдера есть частный реестр образов (ECR, Container Registry у Azure, Artifact Registry у GCP): хранилище рядом с платформой запуска, куда конвейер кладёт образы, а платформа их берёт по внутренней сети без выхода в интернет. Доступ к нему это те же права: конвейеру право положить, среде выполнения право взять через её сервисный аккаунт, людям право посмотреть. Публичные реестры для боевых образов не используют: они недоступны из закрытых сетей, ограничивают частоту, и образ там может измениться под тем же тегом.
Сборка. Образ собирает конвейер, а не ноутбук: из коммита, с тегом из хеша коммита и с меткой версии, под архитектуру серверов, с проверкой на уязвимости при загрузке в реестр (сканирование у реестров встроено) и, где требуется, с подписью, которую платформа проверяет перед запуском. Тег latest для боевой среды не существует: он не говорит, что именно работает, и не даёт откатиться.
Выкат. Новая версия это новое описание запуска с новым образом: ревизия сервиса, новое определение задачи, новая версия Deployment. Платформа поднимает копии новой версии, проверяет их готовность, переводит трафик и гасит старые; откат это возврат к предыдущему описанию с предыдущим образом, который в реестре обязан остаться. Отсюда правило хранения: реестр чистят политикой (последние N версий и всё старше месяца), а не руками, иначе через год он стоит как база.
Что видно потом. Работающая версия отвечает на вопрос «какой образ у тебя» дайджестом, а не тегом, и этот дайджест совпадает с тем, что положил конвейер; по нему в реестре видно коммит и дату. Это и есть связь между кодом в репозитории и тем, что крутится в проде, и без неё разбор инцидента начинается с вопроса «а что вообще выкачено».
Коротко
- Три уровня: виртуальная машина (всё внутри ваше), контейнер (вы отдаёте образ, платформа его запускает), функция (вы отдаёте код на событие).
- Чем выше уровень, тем меньше эксплуатации: не надо патчить ОС, следить за процессом, держать кластер, но тем меньше контроля и дороже единица ресурса.
- Балансировщик раздаёт запросы между копиями и убирает из ротации те, что не отвечают на проверку здоровья.
- Автомасштабирование меняет число копий по нагрузке в заданных границах; без осмысленной проверки готовности оно вредит.
- Функции засыпают без нагрузки и просыпаются с задержкой холодного старта; для чувствительного к задержке API это важно.
- Выбор идёт снизу вверх по потребности: один сервис с волнами нагрузки — контейнер без кластера; двадцать сервисов и команд — оркестратор; перенос «как есть» — машина.
- Путь образа: конвейер собирает из коммита и кладёт в частный реестр провайдера с тегом по хешу и сканированием, среда выполнения берёт по своим правам, выкат это новая ревизия с новым образом, откат к предыдущей; реестр чистят политикой.
Что почитать дальше
- Где запускать сервис в AWS — четыре сервиса AWS на этой же лестнице.
- Kubernetes — оркестратор целиком: поды, деплойменты, пробы.
- Docker — образ, который облако запускает на втором уровне.