В статье про Docker была одна фраза, которую легко пропустить, но которая на самом деле определяет треть решений об инфраструктуре, принимаемых сейчас в индустрии: «переходить на Kubernetes стоит тогда, когда конкретная проблема масштаба уже есть, а не потому что «так делают большие компании»». За этой фразой стоит целая эпидемия — тысячи компаний с тремя серверами и одним сайтом, у которых в проде крутится полноценный K8s-кластер, потому что на собеседовании неловко сказать «у нас просто Docker Compose». «Админ» ставит Kubernetes, потому что это модно и хорошо смотрится в резюме. «Архитектор» сначала считает, какую именно проблему он решает — а потом либо ставит Kubernetes осознанно, либо честно признаёт, что три сервера с Compose закрывают вопрос лучше, дешевле и с меньшим числом того, что может сломаться в три часа ночи.
Эта статья не будет ни агитировать за Kubernetes, ни высмеивать его как «оверинжиниринг для всех подряд» — оба лагеря одинаково бесполезны на практике. Вместо этого — честный разбор: какую именно проблему решает Kubernetes, чего он стоит на самом деле (не только в деньгах), и конкретный набор сигналов, по которым можно понять, действительно ли она у вас есть.
1. Что Kubernetes реально делает — без маркетинга
Compose и Kubernetes решают на первый взгляд похожую задачу — «опиши стек, запусти контейнеры» — но по факту это принципиально разные модели. Compose императивен по духу: вы говорите «подними эти сервисы» на конкретном хосте, и он их поднимает именно там. Kubernetes декларативен и работает по принципу постоянного согласования состояния (reconciliation loop): вы описываете желаемое состояние («мне нужно 3 реплики этого пода, с этими лимитами ресурсов, за этим сервисом»), а контроллер-менеджер непрерывно, десятки раз в секунду, сверяет это описание с реальностью кластера и сам чинит любое расхождение.
Именно из этого вытекают четыре вещи, которые Kubernetes даёт «бесплатно», и ради которых люди на него переходят:
- Самовосстановление на уровне кластера, а не хоста. Если физический сервер, на котором крутился под, полностью упал — Kubernetes сам перепланирует его на другой узел с достаточным количеством ресурсов. Docker Compose такого не умеет в принципе: он управляет контейнерами на одном хосте, и если хост лёг, поднимать сервисы больше некому.
- Планирование по ресурсам, а не по хосту. Вы говорите «этому поду нужно 2 CPU и 4 ГБ», а не «запусти его на сервере web-03» — планировщик сам находит узел, где реально есть это свободное место, с учётом уже запущенного на кластере.
- Rolling-обновления с автоматическим откатом. Deployment сам поднимает новые поды, ждёт, пока они пройдут readiness-проверку, только потом гасит старые — и откатывается назад, если новая версия не проходит health-check.
- Service discovery и балансировка из коробки, на уровне всего кластера, а не одной bridge-сети на хосте, как в Docker (см. статью про сеть Docker) — под может обратиться к сервису по имени независимо от того, на каком узле кластера тот физически сейчас работает.
💡 Ключевая мысль этого раздела: все четыре пункта выше решают задачи, которые становятся реальными только тогда, когда у вас несколько физических узлов, между которыми нужно распределять нагрузку и переживать отказ отдельного узла. Если у вас один сервер — все четыре пункта не имеют смысла: перепланировать под некуда, «весь кластер» и есть один хост, который либо жив, либо нет.
2. Тот же docker-compose.yml, три разных мира
Чтобы разница была не абстрактной, возьмём один и тот же простой стек — веб-приложение, база данных, reverse proxy, ровно тот, что уже был в статье про Docker — и посмотрим, что меняется на каждом следующем уровне.
Уровень 1 — Compose на одном сервере. Ровно то, что уже разбиралось: docker compose up -d, depends_on: condition: service_healthy, именованные тома. Если сервер падает целиком — падает всё, пока кто-то не перезапустит его руками или не сработает скрипт автоперезапуска на уровне облака.
Уровень 2 — Docker Swarm. Часто забытый режим самого Docker: те же compose-файлы (с небольшими отличиями в поддерживаемых ключах), но docker stack deploy вместо docker compose up — и сервисы уже распределяются по нескольким узлам с базовым самовосстановлением. Swarm жив, поддерживается, и для команды из 2-5 человек с 3-10 серверами часто закрывает 80% того, ради чего берут Kubernetes — ценой на порядок меньшей сложности:
Bash — тот же стек, но на кластере из нескольких узлов
# На первом сервере — инициализация кластера
docker swarm init --advertise-addr 10.0.0.1
# На остальных серверах — присоединение по токену,
# который выведет предыдущая команда
docker swarm join --token SWMTKN-xxx 10.0.0.1:2377
# Тот же самый docker-compose.yml, но через stack deploy —
# сервисы сами разъедутся по узлам с учётом реплик
docker stack deploy -c docker-compose.yml myapp
# Масштабирование одной командой, без правки YAML
docker service scale myapp_web=5
# Живой статус — на каком узле что крутится прямо сейчас
docker service ps myapp_web
Уровень 3 — Kubernetes. Тот же стек, но описанный уже не в docker-compose.yml, а в наборе YAML-манифестов (Deployment, Service, PersistentVolumeClaim, Ingress), применяемых через kubectl apply -f либо через Helm-чарт. Разница — не в том, что K8s «мощнее», а в том, что за эту мощность платится совсем другим объёмом движущихся частей, разобранных в следующем разделе.
3. Настоящая цена Kubernetes — не в деньгах за инстансы
Самая частая ошибка при оценке Kubernetes — считать его стоимость в цене серверов под control plane. Реальная цена — в операционной сложности, которая никуда не девается даже на managed-кластере (EKS, GKE, Managed Kubernetes от облачных провайдеров):
- Сеть перестаёт быть простой. В Docker есть одна bridge-сеть и понятный
docker network create. В Kubernetes есть CNI (Container Network Interface) — отдельный плагин (Calico, Cilium, Flannel), который нужно выбрать, настроить и понимать, как он маршрутизирует трафик между подами на разных узлах. Отладка «под А не достучался до пода Б» в Kubernetes на порядок сложнее, чем в Compose, просто потому что слоёв абстракции больше. - Хранилище — отдельная экосистема. Именованный том Docker — это папка на диске хоста. PersistentVolume в Kubernetes — это объект, привязанный к StorageClass, который сам обращается к CSI-драйверу (Container Storage Interface) конкретного облака или SAN. Для нового человека в команде это уже не «примонтировать папку», а разобраться в целой прослойке абстракций.
- YAML разрастается быстрее кода приложения. Простой сервис с базой в Kubernetes — это обычно Deployment + Service + ConfigMap + Secret + PersistentVolumeClaim + Ingress, часто 150-300 строк YAML вместо 30 строк docker-compose.yml. И это без Helm-чартов и Kustomize-оверлеев, которые команды добавляют, чтобы совладать с этим же разрастанием.
- Нужен человек, который спит спокойно с etcd. etcd — распределённая база данных, в которой Kubernetes хранит всё состояние кластера. Её отказ или рассинхронизация — один из самых болезненных инцидентов в мире Kubernetes, и на самостоятельно управляемом кластере ответственность за её здоровье (бэкапы, компакция, мониторинг задержек) целиком на вас. Managed-Kubernetes снимает именно эту головную боль — и это едва ли не главная причина, почему self-hosted control plane сегодня выбирают всё реже.
⚠️ Частая ошибка «Админа»: сравнивать «Compose на 3 серверах» с «managed Kubernetes» и делать вывод, что K8s «почти бесплатен», раз облако само держит control plane. Managed control plane снимает только один пункт из четырёх выше — сеть, хранилище и YAML-сложность остаются полностью на команде в любом случае.
4. Сигналы «пора» — и сигналы «рано»
Вместо абстрактного «когда вырастете» — конкретный список признаков, каждый из которых сам по себе указывает на реальную, а не воображаемую потребность в оркестрации такого уровня:
Сигналы «пора» (хотя бы 2-3 одновременно — весомый аргумент)
- У вас уже больше 5-10 физических/виртуальных узлов, и распределение нагрузки между ними вручную (или через Swarm) начинает требовать отдельного человека на полный день.
- Несколько независимых команд деплоят разные сервисы на одну и ту же инфраструктуру, и им нужна изоляция ресурсов и прав (Namespaces, ResourceQuotas, RBAC) без ручной координации между собой.
- Нагрузка реально скачет в разы в течение суток или сезона (маркетплейс перед распродажей, сервис с пиками по расписанию) — и вам нужен настоящий autoscaling по метрикам, а не «руками добавить пару серверов на выходные».
- Требования по compliance или архитектуре явно требуют network policies на уровне отдельных подов/сервисов, а не только периметрового файрвола.
- У вас уже десятки микросервисов, и часть операционной боли — не в самих контейнерах, а в том, что нужно единообразно управлять их деплоем, конфигами и секретами через общий API, а не через набор bash-скриптов по SSH.
Сигналы «рано» — если это описывает вас, Kubernetes добавит риска, а не снимет его
- Весь прод — 1-3 сервера, и это не собирается измениться в обозримые полгода-год.
- В команде никто, включая вас самих, никогда не отлаживал сетевые проблемы CNI или не читал логи kube-apiserver в реальном инциденте.
- Основной аргумент за переход — «так делают в компании, где я работал раньше» или «это хорошо выглядит в резюме команды», а не конкретная измеримая проблема, которая есть прямо сейчас.
- У вас нет отдельного человека или роли, готовой взять на себя постоянную операционную нагрузку кластера — обновления версий, патчи безопасности control plane, мониторинг здоровья etcd (даже managed control plane не отменяет обновление версии кластера и миграцию API при выходе deprecated-полей).
5. Если решение принято — с чего начинать, а что отложить
Если сигналы из предыдущего раздела реально совпали — вот порядок, который экономит месяцы боли по сравнению с попыткой внедрить «весь Kubernetes сразу»:
- Managed control plane — всегда, если это вообще доступно. Self-hosted etcd и control plane — задача для команд, у которых это осознанный основной продукт (например, инфраструктурные провайдеры), а не побочная деятельность рядом с основным бизнесом.
- Сначала базовые объекты, без Helm и без Operators. Deployment, Service, ConfigMap, Secret, Ingress — этого достаточно для 90% реальных сервисов на старте. Helm-чарты решают проблему повторного использования конфигурации, которой у вас пока, скорее всего, ещё нет с одним-двумя сервисами.
- Один инструмент для наблюдаемости с первого дня.
kubectl logsпо одному поду не масштабируется дальше пяти сервисов — сразу закладывайте централизованный сбор логов и метрик (тот же стек Zabbix/ELK, уже разбиравшийся в статье про аудит Linux-сервера, или облачный аналог), а не откладывайте на «когда будет больно». - Service Mesh (Istio, Linkerd) — сознательно последним, а не по умолчанию. Он решает задачи взаимного TLS между сервисами, детальной трассировки и канареечных релизов на уровне трафика — реальные задачи, но для кластера с 5-10 сервисами это почти всегда чистый оверинжиниринг поверх оверинжиниринга.
Bash — минимальный, но настоящий Deployment + Service, без Helm
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 3
selector:
matchLabels: { app: web }
template:
metadata:
labels: { app: web }
spec:
containers:
- name: web
image: myapp:latest
resources:
requests: { cpu: "250m", memory: "256Mi" }
limits: { cpu: "500m", memory: "512Mi" }
readinessProbe:
httpGet: { path: /healthz, port: 3000 }
initialDelaySeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: web
spec:
selector: { app: web }
ports:
- port: 80
targetPort: 3000
Обратите внимание на resources.requests/limits и readinessProbe — это прямые аналоги лимитов ресурсов и healthcheck из статьи про продакшен-готовность Docker-стека, просто описанные в декларативной модели Kubernetes вместо флагов docker run. Идея не меняется от смены инструмента — меняется только то, кто и как её применяет: раньше вы сами следили за лимитами на каждом хосте, теперь планировщик кластера делает это за вас, но только если вы явно об этом попросили.
Чек-лист: Compose, Swarm или Kubernetes
- Сформулируйте проблему масштаба одним конкретным предложением — не «нам нужен Kubernetes», а «нам нужно X, потому что Y уже происходит прямо сейчас». Если предложение не складывается — это уже ответ.
- Посчитайте узлы, а не серверы вообще — Kubernetes и даже Swarm начинают окупаться там, где узлов действительно несколько и нагрузку нужно между ними реально распределять.
- Попробуйте Swarm как промежуточный шаг, если чувствуете, что Compose на одном сервере уже тесно, но полного набора возможностей Kubernetes пока не нужно.
- Managed control plane, если решение в пользу K8s принято — self-hosted etcd не должен быть первым опытом команды с Kubernetes.
- Начинайте с Deployment/Service/Ingress без Helm, добавляйте инструменты экосистемы по мере реальной, а не воображаемой необходимости.
Вывод
Kubernetes не является «более продвинутой версией Docker» — это ответ на другой вопрос, который имеет смысл задавать только тогда, когда у вас действительно несколько узлов, между которыми нужно распределять нагрузку и переживать отказ. Для инфраструктуры в один-три сервера Compose (или Swarm, если хочется чуть больше отказоустойчивости без прыжка в полный оркестратор) — не временное решение до того, как «дорастёте» — это архитектурно верный выбор, который останется верным, даже когда денег на инфраструктуру станет больше.
Если из всей статьи запомнить одно правило: не спрашивайте «готовы ли мы к Kubernetes» — спрашивайте «какую конкретную, уже случившуюся проблему он решит, которую Compose или Swarm решить не может». Если честный ответ находится — переходите осознанно. Если нет — три сервера с Compose и есть архитектурно правильный выбор, а не временный компромисс.
Теги: #kubernetes #docker #devops #оркестрация #compose #sysadmin #infra #k8s
🚀 Хотите стать архитектором будущего, а не просто админом? Эта статья — лишь верхушка айсберга. В нашем Telegram-канале «Admin Future» мы каждый день публикуем системные гайды, готовые скрипты, AI-лайфхаки и чеклисты, которые не найти в Google. Мы не просто постим команды — мы строим библиотеку знаний для тех, кто хочет расти от админа до архитектора. Подписывайтесь и становитесь экспертом: Admin Future



