Все уроки
DevOps и облакоПродвинутый15 мин чтенияПолезно знать

Kubernetes

Оркестратор, который держит множество контейнеров запущенными, здоровыми и доступными на множестве машин.

  • Pods
  • Deployments
  • Services
  • Ingress
  • Helm
Шаг 1/4
Kubernetes — капитан. Вы говорите ему, чего хотите — «3 копии моего приложения», — и он это обеспечивает.

За 30 секунд

Kubernetes запускает контейнеры на кластере машин и держит их в описанном вами состоянии: сколько копий, какая версия, сколько памяти. Если контейнер упал или машина умерла, он запускает замену в другом месте. А ещё он даёт балансировку нагрузки, rolling update'ы и автомасштабирование — ценой вполне реальной сложности.

Не в моём ежедневном стеке — я понимаю принципы и изучу детали, когда это понадобится проекту.

Почему это важно

  • Kubernetes важен, потому что стал стандартной платформой для запуска контейнеров в масштабе; каждое крупное облако предлагает его как управляемый сервис.
  • Он нужен команде, когда она запускает много сервисов на многих машинах и хочет, чтобы самовосстановление, автомасштабирование и безопасные rolling update'ы брала на себя платформа.
  • Его декларативная модель — вы описываете желаемое состояние в YAML, а кластер его обеспечивает — хорошо сочетается с Git-процессами и ревью.
  • В моих проектах Docker Compose на хорошо настроенном сервере закрывает потребности за малую долю эксплуатационных затрат; к Kubernetes я бы обратился, когда системе нужны несколько нод и автоматический failover.

Как это работает

01Какие проблемы решает Kubernetes

Когда у вас несколько контейнеров на одном сервере, хватает Docker Compose. Когда десятки сервисов на двадцати машинах, появляются новые вопросы. На какой машине есть место для этого контейнера (планирование)? Кто перезапустит его, когда он упадёт, или перенесёт, когда умрёт машина (самовосстановление)? Как добавить копий, когда растёт трафик (масштабирование)? Как выпустить новую версию без простоя и откатиться, если она сломалась (rolling update'ы)? Как сервисам находить друг друга, если их адреса постоянно меняются (service discovery)? Kubernetes отвечает на всё это одной идеей: вы объявляете желаемое состояние, например три реплики web версии 1.4.2, а контроллеры непрерывно сравнивают с ним фактическое состояние и действуют, чтобы закрыть разрыв. Этот цикл согласования (reconciliation loop) — сердце Kubernetes.

  • Control plane: API-сервер, планировщик, контроллеры, etcd (база данных кластера).
  • Worker-ноды: kubelet запускает поды; container runtime запускает контейнеры.
  • Всё — это API-объекты, которые вы создаёте, читаете и обновляете через kubectl или GitOps-инструменты.

02Pod'ы и Deployment'ы

Pod — наименьшая единица, которую запускает Kubernetes: один или несколько контейнеров с общим сетевым адресом, которые могут делить volume'ы. Поды одноразовые: при замене они получают новые имена и IP, поэтому напрямую их почти никогда не создают. Deployment управляет подами за вас: держит запрошенное число реплик и выполняет rolling update, когда вы меняете образ. Важны две проверки здоровья. Readiness probe говорит Kubernetes, когда под готов принимать трафик; liveness probe — когда под завис и его надо перезапустить. Resource requests говорят планировщику, сколько CPU и памяти зарезервировать; limits ограничивают потребление. Без requests планирование и автомасштабирование принимают плохие решения.

yaml
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: registry.example.com/web:1.4.2
          ports: [{ containerPort: 3000 }]
          readinessProbe: { httpGet: { path: /healthz, port: 3000 } }
          resources:
            requests: { cpu: 250m, memory: 256Mi }
            limits: { memory: 512Mi }

03Service'ы, конфигурация и автомасштабирование

Service даёт стабильное имя и виртуальный IP меняющемуся набору подов, выбранных по меткам, и балансирует нагрузку между готовыми из них. Другие поды обращаются к нему через DNS, например http://web. Service'ы типа ClusterIP доступны только внутри кластера; Service'ы типа LoadBalancer просят у облака внешний балансировщик. Конфигурация хранится вне образов: ConfigMap — для несекретных настроек, Secret — для паролей и ключей. Учтите, что по умолчанию Secret'ы лишь закодированы в base64, поэтому включите шифрование at rest и ограничьте, кто может их читать, или синхронизируйте их из внешнего менеджера секретов. HorizontalPodAutoscaler (HPA) меняет число реплик по CPU, памяти или пользовательским метрикам в пределах заданных вами минимума и максимума.

yaml
apiVersion: v1
kind: Service
metadata: { name: web }
spec:
  selector: { app: web }
  ports: [{ port: 80, targetPort: 3000 }]
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata: { name: web }
spec:
  scaleTargetRef: { apiVersion: apps/v1, kind: Deployment, name: web }
  minReplicas: 3
  maxReplicas: 10
  metrics:
    - type: Resource
      resource: { name: cpu, target: { type: Utilization, averageUtilization: 70 } }

04Как запрос доходит до пода

Проследим один запрос. Браузер через DNS превращает app.example.com в адрес облачного load balancer'а. Балансировщик передаёт запрос пограничному proxy кластера — это либо Ingress-контроллер, либо, всё чаще, реализация Gateway API. Этот proxy читает правила маршрутизации (хост и путь) и отправляет запрос в подходящий Service. Service, реализованный на каждой ноде через kube-proxy или eBPF data plane вроде Cilium, выбирает один готовый под из своих endpoint'ов и пересылает запрос на IP и порт пода. В этом списке только поды, прошедшие readiness probe, — так rolling update'ы не шлют трафик в контейнеры, которые ещё стартуют. Обратите внимание: популярный community-контроллер ingress-nginx был выведен из эксплуатации в марте 2026 года; новым кластерам стоит использовать Gateway API или поддерживаемый контроллер.

yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata: { name: web }
spec:
  parentRefs: [{ name: public-gateway }]
  hostnames: ["app.example.com"]
  rules:
    - matches: [{ path: { type: PathPrefix, value: / } }]
      backendRefs: [{ name: web, port: 80 }]

05Helm и управляемый Kubernetes

Реальным приложениям нужно множество YAML-файлов, которые слегка отличаются между staging и продакшеном. Helm упаковывает их в chart: шаблоны плюс файл значений, так что вы ставите или обновляете всё приложение одной командой и можете откатиться к предыдущему релизу. Многие сторонние инструменты (базы данных, мониторинг, cert-manager) распространяются как Helm chart'ы. Kustomize — альтернатива без шаблонов, встроенная в kubectl, с overlay'ями для каждого окружения. Самому держать control plane сложно, поэтому большинство команд используют управляемый Kubernetes: Amazon EKS, Google GKE, Azure AKS или DigitalOcean DOKS. Провайдер запускает и обновляет control plane, а на вас по-прежнему пулы нод, обновления ваших нагрузок, сетевые дополнения, политики безопасности и расходы. Готовьтесь регулярно обновлять версию Kubernetes: каждый минорный релиз upstream поддерживается примерно год.

bash
helm install web ./charts/web -f values.prod.yaml -n prod --create-namespace
helm upgrade web ./charts/web -f values.prod.yaml -n prod --atomic --wait
helm history web -n prod
helm rollback web 3 -n prod
kubectl rollout status deployment/web -n prod

06Когда Kubernetes — перебор и что использовать вместо него

Kubernetes приносит с собой много всего: сетевые плагины, ingress или gateway'и, сертификаты, секреты, RBAC, мониторинг, обновления и YAML для всего подряд. Для команды из нескольких разработчиков с одним продуктом эта платформенная работа может стоить больше, чем реальная инфраструктура продукта. Если приложение помещается на один-два сервера, Docker Compose с хорошими бэкапами и мониторингом проще, дешевле и легче в отладке. Если хочется контейнеров с меньшей эксплуатационной нагрузкой, управляемые контейнерные сервисы вроде AWS ECS с Fargate, Google Cloud Run или Fly.io берут на себя планирование и масштабирование без кластера, которым нужно управлять. Инструменты вроде Kamal деплоят контейнеры на обычные серверы с переключением без простоя. Выбирайте Kubernetes, когда у вас много сервисов, реальные потребности в масштабировании и люди, готовые отвечать за платформу.

  • Один-два сервера: Docker Compose.
  • Контейнеры без эксплуатации кластера: ECS/Fargate, Cloud Run, Fly.io.
  • Простые деплои на несколько серверов: Kamal, Docker Swarm, Nomad.

Когда применять

  • Много сервисов, которыми владеют несколько команд и которым нужна общая платформа для деплоя.
  • Нагрузки, которые должны переживать отказ нод и автоматически масштабироваться под трафик.
  • Организации, которым нужна переносимая инфраструктура между облаками или on-premises.
  • Когда в команде есть (или будут наняты) люди, которые будут отвечать за кластер.

Когда не стоит

  • Один монолит с предсказуемым трафиком, который помещается на один-два сервера.
  • Маленькая команда без платформенного опыта: кластер становится продуктом.
  • Stateful-базы данных как первый шаг; пока нет опыта, запускайте их как управляемый сервис или на выделенных серверах.

Альтернативы

  • Docker Compose на VPS

    Один файл, один сервер, легко понять; нет автоматического failover между нодами.

  • AWS ECS / Fargate

    Родная для AWS оркестрация контейнеров с гораздо меньшим числом понятий, чем у Kubernetes.

  • Google Cloud Run

    Запуск контейнеров на запрос, масштабирование до нуля, никакого кластера.

  • Kamal / Nomad / Docker Swarm

    Более лёгкие способы запускать контейнеры на нескольких серверах.

Лучшие практики

  1. Задавайте resource requests и лимиты памяти для каждого контейнера.
  2. Правильно используйте readiness и liveness probe: readiness управляет трафиком, liveness — перезапуском.
  3. Храните манифесты в Git и деплойте через CI или GitOps (Argo CD, Flux), а не руками.
  4. Используйте namespace'ы, RBAC и network policy, чтобы ограничить, куда может достучаться каждая нагрузка.
  5. Фиксируйте теги или digest'ы образов; никогда не деплойте latest.
  6. Планируйте регулярные обновления кластера: неподдерживаемые версии перестают получать исправления безопасности.

Частые ошибки

  • Считать Secret'ы зашифрованными, хотя по умолчанию они лишь в base64.
  • Отсутствие resource requests, из-за чего поды набиваются на одну ноду и вытесняются под нагрузкой.
  • Liveness probe, которая проверяет базу, — и короткий сбой базы разом перезапускает все поды.
  • Внедрить Kubernetes ради одного маленького приложения и тратить на кластер больше времени, чем на продукт.
  • Запускать kubectl apply с ноутбуков, так что никто не знает, что на самом деле задеплоено.

Глубже

07Внутри control plane: API server, etcd и watch'и

Каждый компонент Kubernetes общается только с API server; никто не обращается к etcd или друг к другу напрямую. API server аутентифицирует запрос, проверяет RBAC, прогоняет admission-контроллеры (мутирующие могут добавить значения по умолчанию или sidecar'ы, валидирующие — отклонить объекты, нарушающие политику) и сохраняет объект в etcd — консистентное key-value хранилище на протоколе Raft. Для записи etcd нужно большинство участников, поэтому кластеры держат три или пять членов: три переживают один отказ, пять — два. Контроллеры не опрашивают API: они открывают долгоживущие watch'и, получают поток изменений, держат локальный кэш и выполняют reconcile. Scheduler следит за подами без ноды, отсеивает ноды, на которые под не помещается, оценивает остальные и записывает свой выбор обратно. Kubelet на выбранной ноде видит назначенный ему под и запускает его контейнеры через container runtime.

  • Admission: сначала мутирующие webhook'и, затем валидирующие webhook'и и политики.
  • Кворум etcd: 3 члена переживают 1 отказ, 5 — 2.
  • Делайте бэкап etcd или убедитесь, что это делает managed-провайдер.

08Requests, limits, классы QoS и троттлинг CPU

Requests — это то, что резервирует scheduler; limits — то, что принудительно применяет ядро. Вместе они определяют класс quality of service для каждого пода. Guaranteed: у каждого контейнера requests равны limits по CPU и памяти. Burstable: заданы какие-то requests или limits. BestEffort: не задано ничего. Когда на ноде заканчивается память, kubelet сначала вытесняет BestEffort-поды, затем Burstable-поды, превысившие свои requests. Превышение лимита памяти не обсуждается: контейнер убивает OOM killer, он показывает exit code 137 и причину OOMKilled и перезапускается. Превышение лимита CPU вместо этого приводит к троттлингу через CFS quota ядра, и троттлинг может давать большие всплески задержки нагруженному сервису, даже когда на ноде есть свободный CPU. Поэтому многие команды ставят лимит памяти равным request'у, аккуратно подбирают CPU requests и не задают CPU limits для чувствительных к задержке сервисов, а общий объём держат под контролем квоты namespace'ов.

yaml
resources:
  requests: { cpu: 500m, memory: 512Mi }
  limits:   { memory: 512Mi }       # no CPU limit: avoid throttling
 
# diagnose
# kubectl describe pod web-7d9f -n prod | grep -A3 "Last State"
# kubectl top pod -n prod --containers

09Graceful shutdown и добровольные прерывания

Когда под удаляется — во время rollout'а, уменьшения числа реплик или drain'а ноды, — одновременно происходят две вещи: kubelet отправляет контейнерам SIGTERM, а endpoint-контроллер убирает под из endpoints Service'а. Прокси и балансировщики узнают об этом чуть позже, поэтому секунду-две трафик ещё может приходить в процесс, который уже завершается. Короткий sleep в preStop откладывает SIGTERM, пока маршрутизация не догонит. Дальше приложение должно перестать принимать новую работу, завершить текущие запросы и выйти в пределах terminationGracePeriodSeconds (по умолчанию 30), после чего его убьют. Для плановых событий вроде обновления нод PodDisruptionBudget говорит Kubernetes, сколько реплик может быть недоступно одновременно, и drain это учитывает. Topology spread constraints разносят реплики по разным нодам и зонам, чтобы один сбой никогда не выбил их все.

yaml
spec:
  terminationGracePeriodSeconds: 45
  topologySpreadConstraints:
    - maxSkew: 1
      topologyKey: topology.kubernetes.io/zone
      whenUnsatisfiable: ScheduleAnyway
      labelSelector: { matchLabels: { app: web } }
  containers:
    - name: web
      lifecycle:
        preStop: { exec: { command: ["sleep", "5"] } }
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata: { name: web }
spec: { minAvailable: 2, selector: { matchLabels: { app: web } } }

10Базовая безопасность: Pod Security, RBAC и NetworkPolicy

По умолчанию кластер Kubernetes довольно либерален: любой под может общаться с любым другим, а под может работать от root с широкими привилегиями. Ужесточить это помогают три встроенных механизма. Pod Security Admission применяет Pod Security Standards на уровне namespace через метки: privileged, baseline или restricted. Restricted требует non-root, запрета повышения привилегий, сброшенных capabilities и профиля seccomp — это хорошая цель для namespace'ов приложений. RBAC должен следовать принципу наименьших привилегий: по возможности Role в namespace вместо ClusterRole, никаких wildcard-глаголов, и отключённое автоматическое монтирование токена service account для подов, которые никогда не обращаются к API. NetworkPolicy — это файрвол для подов: начинайте каждый namespace с default deny, а затем разрешайте только нужные потоки, например ingress от gateway и egress к базе данных и DNS. NetworkPolicy работают, только если их поддерживает CNI-плагин, как Calico и Cilium.

yaml
apiVersion: v1
kind: Namespace
metadata:
  name: prod
  labels:
    pod-security.kubernetes.io/enforce: restricted
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: default-deny, namespace: prod }
spec:
  podSelector: {}
  policyTypes: [Ingress, Egress]

Реальные ситуации

01

Поды в CrashLoopBackOff после релиза

Проблема

Выкатили новый образ, и новые поды постоянно перезапускаются со всё большей задержкой back-off. Старый ReplicaSet по-прежнему обслуживает трафик, но rollout завис, и команда не понимает, куда смотреть.

Как решить

Начните с kubectl describe pod, чтобы увидеть последнее состояние, exit code и события: 137 с OOMKilled указывает на память, 1 — на ошибку приложения, а падения проб видны в событиях. Затем выполните kubectl logs <pod> --previous, чтобы увидеть вывод упавшего контейнера, а не текущей попытки. Частые причины — отсутствующая переменная окружения или ключ Secret, падающая миграция при старте или liveness-проба, срабатывающая до готовности приложения; последнее лечится startupProbe. Поскольку readiness оставил старые поды в работе, простоя нет: если исправление займёт время, выполните kubectl rollout undo deployment/web и добавьте недостающую проверку в CI.

02

Новые поды висят в Pending

Проблема

После масштабирования Deployment с 3 до 10 реплик под маркетинговую кампанию семь подов много минут висят в состоянии Pending, и дополнительная мощность так и не появляется.

Как решить

Выполните kubectl describe pod для одного из них и прочитайте событие scheduler'а — там точно написано почему: например, нехватка CPU на всех нодах, неподходящий node selector или taint, или volume, привязанный к другой зоне. При нехватке мощности проверьте, что установлен cluster autoscaler или Karpenter и что максимум node pool'а позволяет расти, и сравните requests с реальным потреблением, потому что завышенные requests впустую тратят мощность. kubectl describe node показывает выделенные ресурсы против доступных. Для volume'ов, привязанных к зоне, используйте storage class с volumeBindingMode WaitForFirstConsumer. Перед следующей кампанией масштабируйтесь заранее или поднимите minReplicas, а не полагайтесь на автомасштабирование под нагрузкой.

03

Обновления нод вызывают короткие простои

Проблема

Каждое ежемесячное обновление node pool'а приводит к нескольким минутам ошибок. У Deployment три реплики, но пользователи всё равно видят сбои, пока ноды drain'ятся.

Как решить

Посмотрите, где работают реплики: часто все три оказываются на одной-двух нодах, и drain одной ноды убирает большую часть мощности. Добавьте topology spread constraints по нодам и зонам. Добавьте PodDisruptionBudget с minAvailable: 2, чтобы drain ждал, а не вытеснял слишком много подов сразу. Затем проверьте завершение: sleep в preStop на несколько секунд, обработку SIGTERM с закрытием соединений и grace period длиннее самого медленного запроса. Убедитесь, что readiness-проба действительно отражает способность обслуживать запросы. И наконец, проверьте на практике: сделайте drain ноды в staging через kubectl drain под синтетической нагрузкой и убедитесь, что доля ошибок не растёт.

Шпаргалка

Последние события, новые в конце
kubectl get events -n prod --sort-by=.metadata.creationTimestamp
Что не так с этим подом?
kubectl describe pod <pod> -n prod
Логи упавшей попытки
kubectl logs <pod> -n prod --previous
Текущее потребление ресурсов
kubectl top pod -n prod --containers
Откатить Deployment
kubectl rollout undo deployment/web -n prod
Отладить distroless-под через временный shell
kubectl debug -it <pod> -n prod --image=busybox:1.37 --target=web
Достучаться до ClusterIP-сервиса с ноутбука
kubectl port-forward svc/web 8080:80 -n prod
Проверить, что разрешено identity
kubectl auth can-i list secrets -n prod --as=system:serviceaccount:prod:web
Сделать drain ноды для обслуживания
kubectl drain <node> --ignore-daemonsets --delete-emptydir-data
Перезапустить поды, чтобы подхватить новый конфиг
kubectl rollout restart deployment/web -n prod

Вопросы и ответы

Q1Чем Pod отличается от Deployment?

Pod — наименьшая единица деплоя: один или несколько контейнеров с общим сетевым namespace и хранилищем, которые планируются вместе на одну ноду. Поды эфемерны: если под умер или упала его нода, его не воскрешают, а замена получает новое имя и IP. Deployment — контроллер, который управляет ReplicaSet'ом, а тот, в свою очередь, держит заданное число одинаковых подов. Он также выполняет rolling update'ы и откаты, когда вы меняете шаблон пода, например тег образа. На практике вы создаёте Deployment'ы (или StatefulSet'ы для stateful-нагрузок) и почти никогда — голые Pod'ы.

Q2Какие бывают типы Service и когда какой использовать?

ClusterIP — тип по умолчанию: стабильный внутренний виртуальный IP и DNS-имя, доступные только внутри кластера, для трафика между сервисами. NodePort открывает один и тот же порт на каждой ноде — полезно в основном для тестов или чтобы подать трафик во внешний балансировщик. LoadBalancer просит облачного провайдера создать внешний балансировщик, направленный на сервис, — это просто, но стоит по одному балансировщику на сервис. ExternalName сопоставляет имя сервиса внешнему DNS-имени. Для HTTP-трафика обычная схема — ClusterIP-сервисы за одним Gateway или Ingress, который делает маршрутизацию по хосту и пути и TLS для многих сервисов через один балансировщик.

Q3Как работает rolling update и какова роль readiness probe?

Когда вы меняете шаблон пода в Deployment, он создаёт новый ReplicaSet и постепенно масштабирует его вверх, одновременно уменьшая старый. maxSurge управляет тем, сколько лишних подов может существовать во время обновления, а maxUnavailable — сколько может отсутствовать. Новый под считается доступным только после того, как прошёл readiness probe, и трафик от Service'ов получают только готовые поды. Поэтому если новая версия не стартует или проваливает readiness-проверку, раскатка останавливается, а не заменяет здоровые поды, и можно выполнить kubectl rollout undo, чтобы вернуться к предыдущему ReplicaSet. Без правильных readiness probe трафик попадает в поды, которые ещё не готовы.

Q4Что происходит, когда умирает нода?

kubelet на этой ноде перестаёт отчитываться перед control plane, и через таймаут нода помечается как NotReady. Поды на ней становятся недоступны, их endpoint'ы удаляются из Service'ов, так что трафик туда больше не идёт, а после задержки на вытеснение эти поды помечаются на удаление. Контроллеры вроде Deployment'ов замечают, что реплик меньше желаемого, и создают новые поды, которые планировщик размещает на здоровых нодах с достаточной мощностью. Stateless-приложения восстанавливаются автоматически. Со stateful-нагрузками нужно больше осторожности: volume'ы, привязанные к одной зоне, нужно переподключить, а StatefulSet'ы ждут, пока не убедятся, что старый под исчез, прежде чем запускать замену.

Q5ConfigMap или Secret: как вы работаете с конфигурацией и секретами?

Оба передают конфигурацию в поды как переменные окружения или смонтированные файлы. ConfigMap'ы хранят нечувствительные настройки вроде feature flag'ов или URL. Secret'ы предназначены для учётных данных, но по умолчанию они лишь закодированы в base64 и лежат в etcd, так что любой с правом чтения видит значения. Я включаю шифрование at rest для etcd, ограничиваю доступ к Secret'ам через RBAC, не коммичу их в Git и предпочитаю синхронизировать их из внешнего менеджера вроде AWS Secrets Manager или Vault через External Secrets Operator, либо использую sealed secrets или секреты, зашифрованные SOPS, для GitOps. Чтобы подхватить изменённые значения, подам обычно нужен перезапуск или механизм перезагрузки.

Q6Когда бы вы не стали использовать Kubernetes?

Когда эксплуатационные затраты превышают пользу. Одно приложение со стабильным трафиком, которое помещается на один-два сервера, прекрасно работает на Docker Compose с хорошими бэкапами и мониторингом, и вся команда может его понять и отладить. Kubernetes добавляет сеть, gateway'и, сертификаты, RBAC, обновления и огромную поверхность YAML, и за всё это кто-то должен отвечать. Если хочется контейнеров без управления кластером, ECS с Fargate или Cloud Run дают автомасштабирование и rolling-деплой с гораздо меньшим числом понятий. Я бы выбрал Kubernetes для множества сервисов, нескольких команд, реальных потребностей в автомасштабировании или переносимости и людей, готовых вести платформу.

Вопросы уровня senior

S1Как разместить в Kubernetes несколько команд или клиентов: namespace'ы или отдельные кластеры?

Namespace'ы дают мягкую мультиарендность: каждая команда получает свой namespace с RBAC, resource quotas, limit ranges, network policies и метками Pod Security. Это эффективно и достаточно, когда арендаторы доверяют друг другу, например команды внутри одной компании. Но все арендаторы делят control plane, ноды и ядро, поэтому «шумный сосед» или побег из контейнера может затронуть других, а общекластерные объекты вроде CRD общие. Отдельные кластеры дают жёсткую изоляцию, независимые обновления и понятное распределение затрат — ценой большего числа control plane'ов в эксплуатации и менее плотной упаковки. Распространённый компромисс — несколько кластеров, разделённых по окружению и чувствительности, например production и non-production, с namespace'ами по командам внутри, плюс выделенные node pool'ы или кластеры для недоверенных или регулируемых нагрузок.

S2Стоит ли запускать базы данных внутри Kubernetes?

Это возможно и встречается всё чаще, но это должно быть осознанным решением. StatefulSet'ы дают стабильные идентичности и persistent volume'ы, а операторы вроде CloudNativePG для PostgreSQL автоматизируют репликацию, failover, бэкапы и обновления — это намного лучше самописных манифестов. Аргументы за: одна платформа, переносимость и более низкая стоимость, чем у managed-сервисов, на масштабе. Аргументы против: производительность хранилища и поведение volume'ов зависят от CSI-драйвера и облака, drain'ы и обновления нод теперь затрагивают ваш primary, а команде нужна настоящая экспертиза и в базах данных, и в Kubernetes, когда что-то ломается ночью. Для большинства команд безопаснее по умолчанию managed-база вроде RDS или Cloud SQL, а Kubernetes — для stateless-слоёв; базы внутри кластера подходят платформенным командам, которые уже отработали учения по бэкапам и failover.

S3Что даёт GitOps и каковы его компромиссы?

В GitOps контроллер внутри кластера, например Argo CD или Flux, постоянно сравнивает кластер с манифестами в Git-репозитории и применяет разницу. Git становится единым источником истины: каждое изменение — это проверенный коммит, история — журнал аудита, откат — revert, а дрейф из-за ручных правок через kubectl обнаруживается и отменяется. CI больше не нужны креды от кластера, потому что кластер сам вытягивает изменения, и поверхность атаки уменьшается. Компромиссы: ещё одна система, которую надо эксплуатировать и защищать; секретам нужно отдельное решение вроде External Secrets или SOPS; обновление тегов образов требует автоматизации, которая коммитит обратно в Git; а структура репозитория для окружений и кластеров становится отдельной задачей проектирования. Если сервисов или кластеров больше пары, преимущества обычно перевешивают.

S4Как безопасно обновить кластер Kubernetes?

Upstream поддерживает каждую минорную версию около четырнадцати месяцев, и патчи получают только три последние минорные версии, так что обновления — рутина, по одной минорной версии за раз. Перед обновлением прочитайте release notes на предмет удалённых API и просканируйте манифесты и Helm-чарты инструментом вроде Pluto или kubent, потому что удалённые версии API ломают деплои. Проверьте, что дополнения — CNI, CSI-драйверы, ingress- или gateway-контроллер, cert-manager — поддерживают целевую версию. Сначала обновите staging и дайте ему поработать какое-то время. В production обновите control plane, затем node pool'ы — через surge-обновления или создав новый pool и сделав drain старого; это работает, только если PodDisruptionBudget'ы, topology spread и graceful shutdown настроены правильно. Blue-green кластеры — построить новый кластер и переключить на него трафик — самый безопасный и самый дорогой вариант.

Глоссарий

Control plane
Компоненты, которые хранят состояние кластера и действуют на его основе: API server, etcd, scheduler и controller manager.
etcd
Консистентное key-value хранилище всех объектов кластера; для записи ему нужно большинство участников.
kubelet
Агент на каждой ноде, который запускает назначенные ей поды и сообщает об их состоянии.
Controller
Цикл, который следит за объектами и приводит фактическое состояние к желаемому.
ReplicaSet
Поддерживает фиксированное число одинаковых подов; Deployment'ы создают ReplicaSet'ы и управляют ими за вас.
StatefulSet
Запускает поды со стабильными именами, упорядоченным стартом и собственными persistent volume'ами — для баз данных и подобного.
DaemonSet
Запускает по одной копии пода на каждой ноде — обычно для сборщиков логов, агентов мониторинга или сети.
PodDisruptionBudget
Правило, ограничивающее, сколько подов приложения может быть недоступно одновременно при добровольных прерываниях вроде drain'а.
QoS class
Guaranteed, Burstable или BestEffort — выводится из requests и limits и определяет порядок вытеснения при нехватке памяти.
CNI
Container Network Interface: плагин, например Cilium или Calico, который даёт подам сеть и применяет политики.

Есть идея продукта?Давайте создадим его.

Расскажите о своей идее, продукте или команде. Обычно отвечаю в течение одного рабочего дня.