StatefulSet и DaemonSet: stateful-нагрузки и системные агенты

Рейтинг: 49% · 10 голосов
Kubernetes для разработчиков: поды, деплойменты, сервисы, ingress, конфиги и отладка. Уроки по главам с обсуждением.
Ответить
Аватара пользователя
anton_k8s
Сообщения: 55
Зарегистрирован: 12 май 2026, 03:23

StatefulSet и DaemonSet: stateful-нагрузки и системные агенты

Сообщение anton_k8s »

Оглавление курса (48)
  1. Зачем нужен Kubernetes и из чего состоит кластер
  2. Поднимаем локальный кластер: minikube и kind
  3. Поды: базовая единица запуска
  4. Deployment и ReplicaSet: управляем репликами
  5. Service: сетевой доступ к подам
  6. ConfigMap и Secret: выносим конфигурацию
  7. Ingress: пускаем трафик снаружи
  8. Хранилище: Volumes и PersistentVolumeClaim
  9. Namespaces, requests и limits
  10. Health checks: liveness и readiness пробы
  11. Отладка: почему под не стартует
  12. Helm: пакетный менеджер для Kubernetes
  13. Базовая безопасность: RBAC и доступы
  14. Job и CronJob: разовые и периодические задачи
  15. StatefulSet и DaemonSet: stateful-нагрузки и системные агенты (вы здесь)
  16. Стратегии обновления и планирование: rollout и rollback, graceful shutdown, nodeSelector, affinity, taints
  17. Автомасштабирование: HPA по метрикам, обзор VPA и Cluster Autoscaler
  18. Наблюдаемость: логи, метрики, events, обзор Prometheus и Grafana
  19. Безопасность глубже: securityContext, Pod Security Standards, NetworkPolicy, шифрование секретов
  20. Архитектура control plane: api-server, etcd, scheduler
  21. Узел кластера: kubelet, kube-proxy и container runtime
  22. Декларативная модель: reconciliation, контроллеры, CRD
  23. kubectl профессионально: get, describe, explain, jsonpath
  24. Под глубже: init, sidecar, lifecycle hooks и QoS
  25. Метки, селекторы и организация ресурсов
  26. Сервисы и kube-proxy глубже: типы, IPVS, EndpointSlices
  27. Ingress, ingress-контроллеры и Gateway API
  28. Секреты в кластере: шифрование, External Secrets, Vault
  29. Хранилище глубже: PV, StorageClass, CSI и StatefulSet
  30. Сеть кластера: CNI, NetworkPolicy и CoreDNS
  31. Планировщик: affinity, taints, topology spread
  32. Ресурсы, QoS и вытеснение: requests, limits, eviction
  33. Helm глубже: шаблоны, хуки, зависимости, OCI
  34. Kustomize и управление конфигурацией без шаблонов
  35. RBAC и аутентификация глубже
  36. Admission и Pod Security: контроль на входе
  37. Policy as code: Kyverno и OPA Gatekeeper
  38. GitOps: Argo CD и Flux
  39. Операторы и CRD: расширяем Kubernetes
  40. Сервис-меш: Istio, Linkerd и когда он нужен
  41. Эксплуатация кластера: апгрейд, узлы, бэкап etcd
  42. Где запускать кластер: managed, self-hosted, k3s, Deckhouse
  43. Траблшутинг кластера: Pending, CrashLoop, узлы NotReady
  44. Стоимость и эффективность кластера: FinOps
  45. CI/CD в Kubernetes: сборка, деплой, прогрессивные релизы
  46. Лучшие практики и антипаттерны Kubernetes
  47. Сквозной проект и путь дальше: от манифеста до прода
  48. Деплой приложений через Argo CD: Application, sync и App-of-Apps
Боль, с которой ты столкнешься на втором месяце в проде

Deployment ты уже знаешь. Он гениален для stateless: накатил три реплики nginx, поды называются как-нибудь вроде web-7d4f9c-x9q2, любой из них взаимозаменяем, упал - подняли новый, и плевать на порядок. Но как только ты пытаешься засунуть в кластер базу данных, очередь сообщений или распределенное хранилище - Deployment начинает рассыпаться.

Представь, что ты разворачиваешь реплику PostgreSQL: один primary и две standby-реплики. Им нужно три вещи, которых Deployment дать НЕ может. Первое - стабильное сетевое имя: standby должен знать, к кому подключаться, а не гадать, какой из подов сегодня primary. Второе - свой персональный диск на каждую реплику: данные primary и standby - это разные данные, их нельзя шарить. Третье - предсказуемый порядок: нельзя поднять реплику, пока не готов primary, и нельзя гасить primary, пока не остановлены реплики.

Вот ровно для этого и придуман statefulset. А когда тебе нужно обратное - не stateful-сервис, а агент на каждой ноде (сборщик логов, экспортер метрик, плагин сети) - в дело вступает daemonset. Разберем оба контроллера по косточкам: как они устроены под капотом, где грабли, и когда базу данных в Kubernetes тащить можно, а когда лучше десять раз подумать.

Изображение

StatefulSet: механика стабильной идентичности

kubernetes statefulset - это контроллер из группы apps/v1, как и Deployment, но с тремя фундаментальными гарантиями, которые меняют все.

1. Стабильная сетевая идентичность. Поды называются не случайно, а строго по индексу: имя-0, имя-1, имя-2. Это ordinal index. Под с индексом 0 - это всегда тот же логический член кластера, даже если физически его пересоздали на другой ноде. Имя пода стабильно на протяжении всей жизни StatefulSet.

2. Стабильное хранилище. Через volumeClaimTemplates каждому поду создается СВОЙ PersistentVolumeClaim. Под pg-0 получает PVC data-pg-0, под pg-1 - data-pg-1 и так далее. Если pg-1 умрет и пересоздастся, он подцепит ровно свой старый PVC со своими данными. Диски не шарятся между репликами - у каждой свое состояние.

3. Упорядоченность. По умолчанию (политика OrderedReady) поды создаются строго последовательно: 0, потом ждем, пока 0 станет Ready, потом 1, ждем Ready, потом 2. Удаление - в обратном порядке: сначала гасим под с самым большим индексом. Это и есть тот самый предсказуемый порядок для primary/standby.

Чтобы стабильные имена работали на уровне DNS, StatefulSet требует headless-сервис - Service с clusterIP: None. Обычный Service дает один виртуальный IP и балансирует трафик по всем подам случайно. Headless не дает виртуального IP вообще - вместо этого DNS отдает A-записи на каждый под отдельно. Благодаря этому появляется стабильный FQDN на каждую реплику: pg-0.pg-headless.namespace.svc.cluster.local. Standby берет этот адрес и точно знает, куда стучаться.

Вот минимальный, но боевой манифест:

Код: Выделить всё

apiVersion: v1
kind: Service
metadata:
  name: pg-headless
spec:
  clusterIP: None          # вот это делает сервис headless
  selector:
    app: pg
  ports:
    - port: 5432
      name: postgres
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: pg
spec:
  serviceName: pg-headless # обязательная привязка к headless-сервису
  replicas: 3
  podManagementPolicy: OrderedReady   # по умолчанию; альтернатива - Parallel
  selector:
    matchLabels:
      app: pg
  template:
    metadata:
      labels:
        app: pg
    spec:
      containers:
        - name: postgres
          image: postgres:16
          ports:
            - containerPort: 5432
              name: postgres
          volumeMounts:
            - name: data
              mountPath: /var/lib/postgresql/data
  volumeClaimTemplates:    # шаблон PVC на КАЖДУЮ реплику
    - metadata:
        name: data
      spec:
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 20Gi
Поле serviceName - не косметика, это жесткая связка: именно по нему StatefulSet строит DNS-имена подов. Если перепутать имя сервиса, DNS-записи не появятся, и кластер БД не соберется.

Что реально происходит: разбор вывода

Применили манифест и смотрим, как поды рождаются по порядку:

Код: Выделить всё

$ kubectl get pods -l app=pg -w
NAME   READY   STATUS    RESTARTS   AGE
pg-0   0/1     Pending   0          0s
pg-0   1/1     Running   0          14s
pg-1   0/1     Pending   0          0s     # pg-1 стартует ТОЛЬКО после Ready pg-0
pg-1   1/1     Running   0          12s
pg-2   1/1     Running   0          11s
Обрати внимание: pg-1 не появляется, пока pg-0 не дошел до 1/1 Ready. Это OrderedReady в действии. Теперь PVC:

Код: Выделить всё

$ kubectl get pvc -l app=pg
NAME        STATUS   VOLUME     CAPACITY   ACCESS MODES   STORAGECLASS
data-pg-0   Bound    pvc-a1..   20Gi       RWO            yc-network-ssd
data-pg-1   Bound    pvc-b2..   20Gi       RWO            yc-network-ssd
data-pg-2   Bound    pvc-c3..   20Gi       RWO            yc-network-ssd
Три отдельных PVC, по одному на реплику. Имя строится как имя_шаблона-имя_statefulset-индекс. StorageClass здесь yc-network-ssd - это сетевой диск в Yandex Managed Kubernetes; в VK Cloud или Deckhouse будет свой провижионер, но принцип тот же.

Проверим DNS изнутри кластера:

Код: Выделить всё

$ kubectl run dns-test --rm -it --image=busybox:1.36 -- \
    nslookup pg-0.pg-headless
Name:      pg-0.pg-headless.default.svc.cluster.local
Address 1: 10.112.3.14 pg-0.pg-headless.default.svc.cluster.local
Каждая реплика резолвится в собственный адрес. Вот эта стабильность имени и есть фундамент stateful kubernetes-нагрузок.

DaemonSet: по одному поду на узел

daemonset решает зеркально противоположную задачу. StatefulSet говорит "мне нужно N именованных реплик с дисками". DaemonSet говорит "мне нужен ровно один под на каждой подходящей ноде - сколько нод, столько и подов". Добавили ноду в кластер - DaemonSet автоматически выкатил на нее под. Убрали ноду - под уехал вместе с ней. Тебе не надо указывать replicas, контроллер сам считает количество по числу нод.

Кто это по жизни: агент сбора логов (Fluent Bit, Vector), экспортер метрик (node-exporter), плагин сети CNI (Cilium, Calico), агент безопасности, драйвер CSI. Все, что должно физически присутствовать на каждой машине, потому что собирает данные именно с этой машины. Сам kube-proxy в большинстве кластеров крутится как DaemonSet.

Код: Выделить всё

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: node-exporter
  namespace: monitoring
spec:
  selector:
    matchLabels:
      app: node-exporter
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1     # за раз обновляем не больше одной ноды
  template:
    metadata:
      labels:
        app: node-exporter
    spec:
      hostNetwork: true      # агенту часто нужна сеть хоста
      tolerations:
        - operator: Exists   # ехать даже на ноды с любыми taint, включая control-plane
      containers:
        - name: node-exporter
          image: quay.io/prometheus/node-exporter:v1.8.2
          ports:
            - containerPort: 9100
              hostPort: 9100
          volumeMounts:
            - name: proc
              mountPath: /host/proc
              readOnly: true
      volumes:
        - name: proc
          hostPath:
            path: /proc
Ключевой момент - tolerations. По умолчанию на control-plane-нодах висит taint, который отгоняет обычные поды. Агенту мониторинга, наоборот, надо быть везде, поэтому toleration с operator: Exists пускает его на все ноды без исключения. Второй частый прием - nodeSelector или affinity, если агент нужен только на части нод (например, только на GPU-узлах).

Обновляется DaemonSet по стратегии RollingUpdate: maxUnavailable: 1 значит, что под обновляется по одной ноде за раз - выкатили новую версию на ноду, дождались готовности, пошли дальше. Есть и maxSurge - он позволяет на время обновления поднять новый под рядом со старым на той же ноде, чтобы агент не пропадал ни на секунду; но maxSurge несовместим с hostPort и hostNetwork, потому что два пода не займут один и тот же порт хоста. Это типичная ловушка: ставишь maxSurge на node-exporter с hostPort - и обновление встает колом.

Три контроллера рядом: когда что брать
  • Deployment - stateless, взаимозаменяемые реплики, случайные имена, общий или вовсе никакой диск. Веб-приложения, API, воркеры. 95% твоих сервисов.
  • StatefulSet - нужна стабильная идентичность, персональный диск на реплику, упорядоченный запуск. Базы данных, Kafka, ZooKeeper, Elasticsearch, Redis-кластер, etcd.
  • DaemonSet - по одному агенту на узел, привязка к железу/ОС ноды. Логи, метрики, сеть, безопасность, CSI-драйверы.
Простое правило: если поды должны помнить, кто они и где их данные - StatefulSet. Если поды должны быть на каждой машине - DaemonSet. Иначе - Deployment.

Грабли StatefulSet, на которых горят все

volumeClaimTemplates не удаляются автоматически. Самая дорогая ловушка. Удалил StatefulSet - PVC и данные остаются. Это сделано специально, чтобы ты случайно не снес базу. Но это же значит, что diски тихо копятся и жгут деньги. С версии 1.23 есть persistentVolumeClaimRetentionPolicy, где можно задать поведение whenDeleted и whenScaled (Retain или Delete). По умолчанию Retain - и это правильный дефолт для БД, но за уборкой следить надо руками.

Масштабирование вниз не уменьшает данные. Уменьшил replicas с 5 до 3 - поды pg-4 и pg-3 удалились, но их PVC по умолчанию остались висеть. Снова увеличил до 5 - новые поды подцепят СТАРЫЕ диски со старыми данными. Иногда это спасает, иногда - источник коварных багов, когда реплика поднимается с протухшим состоянием.

Обновление идет в обратном порядке и может застрять. RollingUpdate обновляет поды от старшего индекса к младшему: pg-2, потом pg-1, потом pg-0. Если новый под не становится Ready (кривой образ, проблема с readinessProbe) - обновление встает намертво и не трогает остальные поды. Это защита от полного развала кластера, но первый раз пугает. Лечится через partition в rollingUpdate для канареечного выката: обновляются только поды с индексом не ниже partition.

Headless-сервис обязателен. Забыл clusterIP: None или serviceName - DNS-имена подов не поднимутся, и stateful-приложение, которое ждет своих собратьев по FQDN, зависнет на старте без внятной ошибки.

Parallel - острый нож. podManagementPolicy: Parallel ускоряет запуск, поднимая все поды разом, но убивает гарантию порядка. Для primary/standby-топологии, где порядок критичен, это прямой путь к расколу кластера (split-brain). Используй Parallel только если приложение реально не зависит от порядка реплик.

Kubernetes база данных: тащить или нет

Вопрос kubernetes база данных - религиозный, разберемся трезво. StatefulSet дает тебе примитивы (имена, диски, порядок), но НЕ дает саму логику БД: бэкапы, failover, репликацию, point-in-time recovery. Голый StatefulSet с PostgreSQL - это просто три пода с дисками, а не отказоустойчивый кластер.

Поэтому в проде БД в Kubernetes запускают через операторы: CloudNativePG или Zalando Postgres Operator для PostgreSQL, Strimzi для Kafka, оператор от Percona для MySQL/MongoDB. Оператор - это контроллер, который поверх StatefulSet добавляет мозги: сам делает failover, поднимает реплики, гоняет бэкапы в S3, чинит split-brain. Без оператора эксплуатация stateful-БД руками - это боль и риск потери данных.

Честный взгляд: сетевые диски (тот же yc-network-ssd) добавляют латентность по сравнению с локальным NVMe, а сетевые блипы внутри кластера для БД болезненнее, чем для stateless-сервиса. Если у тебя есть managed-БД от облака (Yandex Managed Service for PostgreSQL, VK Cloud Databases) и нет жесткой причины держать базу в кластере - чаще разумнее взять managed. В кластер БД тащат, когда нужна полная переносимость, GitOps-управление всем стейтом или специфические требования, которых managed не закрывает. И тогда - только через зрелый оператор, никогда не голым StatefulSet в продакшене.

Мини-лаба: собери и сломай руками
  • Подними StatefulSet из трех реплик с volumeClaimTemplates (можно nginx с диском - суть в механике, не в БД). Запусти kubectl get pods -w и убедись своими глазами, что поды стартуют строго по порядку 0, 1, 2.
  • Проверь kubectl get pvc - найди три отдельных PVC с именами по индексу. Запиши на диск пода-0 файл, удали под-0 (kubectl delete pod имя-0), дождись пересоздания и убедись, что файл на месте - PVC подцепился обратно.
  • Уменьши replicas до 1, посмотри kubectl get pvc - убедись, что PVC удаленных подов остались висеть. Это и есть та самая грабля с дисками.
  • Подними DaemonSet с node-exporter, выполни kubectl get pods -o wide и убедись, что подов ровно столько, сколько нод, и каждый на своей. Добавь toleration и проверь, доехал ли под до control-plane-ноды.
Контрольные вопросы
  • Почему StatefulSet требует headless-сервис и что сломается, если задать обычный Service с clusterIP?
  • Что произойдет с PVC при удалении StatefulSet и при scale down, и как это поведение настраивается?
  • В каком порядке StatefulSet создает и удаляет поды при OrderedReady, и чем грозит переключение на Parallel для кластера БД?
  • Почему для node-exporter с hostPort нельзя использовать maxSurge в стратегии обновления DaemonSet?
Итог

StatefulSet дает три вещи, которых нет у Deployment: стабильные имена подов по индексу, персональный диск на каждую реплику через volumeClaimTemplates и упорядоченный запуск/останов поверх headless-сервиса - это фундамент для БД и кластерных систем. DaemonSet решает обратную задачу: ровно один агент на каждую подходящую ноду для логов, метрик, сети и безопасности. Главные грабли StatefulSet - неудаляемые PVC и застревающее обновление. А базу данных в Kubernetes в проде запускают через оператор, а не голым контроллером - либо честно берут managed-сервис.
👍 ❤️3 🔥 😄 🤔1
✔ Лучший ответ сформирован автоматически — torch_veteran
anton_k8s писал(а):StatefulSet гарантирует, что под с данным именем существует максимум в одном экземпляре, иначе два пода могли бы писать в один диск о, так вот что это было. в феврале нода в облаке сдохла, постгрес висел в Terminating полтора часа, я психанул и снес под через --force. обошлось, но теперь дошло, чем рисковал, если бы кубелет на той ноде внезапно ожил. out-of-service taint беру…
Перейти к ответу →
Аватара пользователя
torch_veteran
Сообщения: 2
Зарегистрирован: 02 июн 2026, 07:11

Re: StatefulSet и DaemonSet: stateful-нагрузки и системные агенты

Сообщение torch_veteran »

✔ Лучший ответ — сформирован автоматически
anton_k8s писал(а):StatefulSet гарантирует, что под с данным именем существует максимум в одном экземпляре, иначе два пода могли бы писать в один диск
о, так вот что это было. в феврале нода в облаке сдохла, постгрес висел в Terminating полтора часа, я психанул и снес под через --force. обошлось, но теперь дошло, чем рисковал, если бы кубелет на той ноде внезапно ожил. out-of-service taint беру на заметку
👍1 ❤️2 🔥 😄 🤔
Аватара пользователя
cpplord
Сообщения: 1
Зарегистрирован: 19 май 2026, 19:19

Re: StatefulSet и DaemonSet: stateful-нагрузки и системные агенты

Сообщение cpplord »

честно говоря не понял, зачем в 2026 писать statefulset для базы руками. для постгреса cloudnativepg, для кафки strimzi, у редиса свой оператор. ощущение, что глава про внутренности, которые трогают полтора человека. хотя ладно, когда оператор начинает чудить, дебажить приходится ровно вот это все, так что пусть будет
👍1 ❤️1 🔥 😄 🤔
Аватара пользователя
Josephboyd
Сообщения: 1
Зарегистрирован: 17 май 2026, 18:10

Re: StatefulSet и DaemonSet: stateful-нагрузки и системные агенты

Сообщение Josephboyd »

за --cascade=orphan отдельное спасибо. месяц назад расширяли диски у кликхауса, про этот флаг никто не знал, удалять sts на проде боялись. в итоге подняли второй statefulset с большими дисками и перелили данные, день потеряли на ровном месте
👍1 ❤️2 🔥1 😄 🤔
Аватара пользователя
python_coder
Сообщения: 1
Зарегистрирован: 02 июн 2026, 01:23

Re: StatefulSet и DaemonSet: stateful-нагрузки и системные агенты

Сообщение python_coder »

подтверждаю про requests у daemonset. fluent-bit с утечкой как-то выжрал по 3 гига на каждой из 40 нод, начался eviction прода по всему кластеру. с тех пор лимиты на агентах ставлю раньше, чем конфиг пишу
👍 ❤️ 🔥 😄 🤔
Ответить
← Предыдущая глава
Job и CronJob: разовые и периодические задачи
Следующая глава →
Стратегии обновления и планирование: rollout и rollback, graceful shutdown, nodeSelector, affinity, taints

Все главы курса «Kubernetes: оркестрация контейнеров от основ до продакшена»

Поделиться темой: ✈ Telegram VK
  • Похожие темы
Похожие запросы: как восстановить испорченный tfstate terraformкак настроить агенты jenkins для сборкиstatefulset в kubernetes для баз данных и хранилищаkubernetes pvc и storageclass как подключить хранилище

Вернуться в «Kubernetes на практике»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость