Существует момент, который переживает каждый основатель. Вы только что закрыли первый раунд финансирования, ваш продукт набирает обороты, и кто-то в канале Slack упоминает, что вам «вероятно, стоит задуматься о Kubernetes». Ваш CTO задумчиво кивает. Все начинают искать учебники по Kubernetes в полночь. К следующему утру вы убеждаете себя, что Kubernetes — неизбежная часть вашего пути к статусу единорога с миллиардной оценкой.
Вот неудобная правда: вы, вероятно, совершаете ужасную ошибку.
Я здесь не для того, чтобы критиковать Kubernetes. Это инженерное чудо. Это отраслевой стандарт по уважительной причине. Такие компании, как Spotify, OpenAI и бесчисленное множество других предприятий, масштабируют свои услуги на тысячах машин с помощью Kubernetes. Но вот в чём дело — вы не Spotify. Вы стартап. И у вас нет проблемы с масштабированием, которую решает Kubernetes; у вас просто нет такой проблемы.
Ловушка Kubernetes
Позвольте мне описать, как это обычно происходит в стартапах. Вы запускаете несколько сервисов на простой инфраструктуре. Может быть, вы используете Docker Compose локально и развёртываете на пару инстансов AWS. Бизнес идёт хорошо. Трафик растёт. Где-то через шесть или двенадцать месяцев подкрадывается тревога. А что, если трафик вдруг увеличится в 10 раз? А что, если нам нужно будет развернуть несколько инстансов? Что, если… что, если… что, если?
На сцену выходит Kubernetes.
Kubernetes обещает автоматическое масштабирование, самовосстановление, декларативную инфраструктуру, независимость от поставщиков и все другие замечательные вещи, о которых вы читали в публикациях на Medium. Звучит идеально. Звучит именно так, как вам нужно. Поэтому вы выделяете двух разработчиков для «модернизации инфраструктуры». Следующие три месяца вы пишете YAML-файлы, боретесь с сетью, отлаживаете проблемы с разрешением DNS для пода в 2 часа ночи и постепенно осознаёте, что Kubernetes — это не просто оркестрация контейнеров, это полное изменение образа жизни.
Вишенка на торте? Ваш трафик не увеличился в 10 раз. Вы по-прежнему обрабатываете столько же запросов, сколько и шесть месяцев назад. Но теперь у вас есть требования к знаниям Kubernetes в вакансиях, DevOps-специалист, чья работа заключается в управлении Kubernetes, и вы тратите примерно в четыре раза больше денег на инфраструктуру, потому что у вас минимальный жизнеспособный производственный кластер, требующий постоянного присмотра.
Это то, что я называю преждевременной оптимизацией инфраструктуры. Это эквивалент преждевременной оптимизации в коде — корень всего зла.
Реальность трафика стартапов
Позвольте мне поделиться тем, о чём большинство успешных стартапов не говорят: большинство из них никогда не достигают масштаба, при котором Kubernetes становится необходим. Исследование 2019 года показало, что примерно 90% стартапов, которые терпят неудачу, делают это из-за отсутствия спроса, а не из-за проблем с масштабируемостью. Ваша инфраструктура не убивает ваш стартап. Отсутствие клиентов убивает ваш стартап.
Подумайте, что на самом деле важно, когда вы пытаетесь найти соответствие продукта и рынка:
- Выпуск функций (а не управление оркестрацией контейнеров).
- Понимание поведения пользователей (а не отладка сети Kubernetes).
- Итерация продукта (а не посещение конференций SRE).
- Снижение затрат (а не оплата кластера Kubernetes, работающего на 5% мощности).
Инфраструктура, которая вам нужна на этом этапе, должна не мешать. Она должна решать скучные операционные задачи, чтобы ваши инженеры могли сосредоточиться на создании действительно важных вещей. Вам нужны управляемые сервисы, а не инфраструктура, которой вы должны управлять.
Скрытые затраты, о которых никто не говорит
Вот о чём вас не предупреждают в маркетинговых материалах Kubernetes: истинные затраты на внедрение.
Время разработчиков: каждый инженер, который работает с вашей инфраструктурой, должен знать Kubernetes. Это не бесплатно. Это месяцы времени на обучение новых сотрудников. Это сеансы отладки, которые занимают в три раза больше времени, потому что вы пытаетесь понять, в вашем ли коде проблема, в конфигурации Docker или в политике сети Kubernetes.
Операционные затраты: кто-то должен управлять вашим кластером Kubernetes. Кто-то должен устанавливать патчи безопасности, обновлять версии Kubernetes, управлять реестром контейнеров, настраивать мониторинг, реализовывать надлежащий RBAC, управлять постоянными томами и справляться с неизбежными инцидентами в 3 часа ночи, когда падает управляющая плоскость кластера.
Ментальная сложность: у Kubernetes кривая обучения, которая больше похожа не на кривую, а на обрыв. Вы не просто учите оркестрацию контейнеров — вы учите сервисы, ингрессы, деплойменты, стейтефулсеты, DaemonSets, операторов, определения пользовательских ресурсов и около сорока других концепций, которые взаимодействуют неочевидным образом.
Увеличение затрат: теперь вы платите за ресурсы по-другому. С традиционными виртуальными машинами вы могли бы запустить три инстанса, которые стоили бы вам 50 долларов в месяц каждый. С Kubernetes вам нужен минимально жизнеспособный кластер — обычно не менее трёх узлов для высокой доступности. Вы смотрите на 300–500 долларов в месяц минимум, прежде чем даже развернуть первое приложение. И вот забавная часть: ваша инфраструктура Kubernetes не очень хорошо справляется с эффективным управлением всплесками трафика, потому что вы обязуетесь запускать эти минимальные узлы, даже когда они в основном бездействуют.
Забытая золотая середина
О чём никто не говорит, так это о том, что существует огромная золотая середина между «я запускаю всё на одном сервере» и «мне нужен кластер Kubernetes». Именно здесь 95% стартапов должны находиться в первые несколько лет.
Cloud Run, AWS Lambda, Google App Engine, Azure Container Apps — эти бессерверные и похожие на бессерверные платформы — то, на чём должны работать стартапы. Вы развёртываете контейнер. Платформа управляет масштабированием, балансировкой нагрузки, проверкой работоспособности и развёртыванием. Вы платите за то, что используете. Вы не управляете кластером. Ваши разработчики тратят время на создание продуктов, а не на устранение неполадок в YAML.
Позвольте мне привести конкретный пример. Я работал с стартапом, который обрабатывал около 50 запросов в секунду с предсказуемыми шаблонами трафика. Они были на Kubernetes, и это стоило им примерно 800 долларов в месяц на инфраструктуру плюс эквивалент 0,5 FTE на работу DevOps. Мы переместили их в Google Cloud Run с некоторыми управляемыми базами данных и кэшированием Redis. Те же производительность и надёжность, 200 долларов в месяц на инфраструктуру, и вдруг DevOps-специалист может сосредоточиться на других вещах.
Прелесть в том, что если им в конечном итоге понадобится Kubernetes — действительно понадобится, а не гипотетически — они смогут сделать этот шаг. В отличие от проприетарных решений Platform-as-a-Service, которые вас запирают, Cloud Run использует стандартные контейнеры. Ваш путь миграции на Kubernetes будет плавным.
Давайте поговорим об архитектуре
Вот как выглядит типичная инфраструктура стартапа до ошибки с Kubernetes:
Заметьте, чего не хватает? Нет кластера Kubernetes. Нет управления узлами. Нет конфигурации балансировки нагрузки между подами. Нет обнаружения сервисов. Все эти проблемы решает платформа.
Ваша команда по инфраструктуре состоит из: никого. Ваша инфраструктура определена в вашем репозитории infrastructure-as-code примерно в 50 строках Terraform. Новым инженерам не нужно изучать Kubernetes. Им просто нужно знать, как писать код, который может работать в контейнере, что они, вероятно, уже знают.
Практический пример
Позвольте мне показать вам, как это выглядит на самом деле в коде. Вот минимальная настройка для стартапа с использованием Terraform и Google Cloud Run:
# main.tf
terraform {
required_providers {
google = {
source = "hashicorp/google"
version = "~> 5.0"
}
}
}
provider "google" {
project = var.gcp_project
region = var.gcp_region
}
resource "google_cloud_run_service" "api" {
name = "startup-api"
location = var.gcp_region
project = var.gcp_project
launch_stage = "GA"
template {
spec {
containers {
image = "${var.gcp_region}-docker.pkg.dev/${var.gcp_project}/startup/api:${var.image_tag}"
env {
name = "DATABASE_URL"
value = google_cloud_sql_instances.postgres.connection_name
}
env {
name = "REDIS_URL
