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

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

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

Сообщение anton_k8s »

АкадемияKubernetes на практикеГлава 15 из 19
Оглавление курса (19)
  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, шифрование секретов
Deployment из главы 4 считает поды взаимозаменяемыми: любой можно убить и поднять заново, имена случайные, постоянного диска нет. Для API-сервисов это то, что нужно. Для базы данных или брокера сообщений нет: у каждой реплики свои данные и своя роль. Плюс третий случай: агент, который обязан работать на каждой ноде ровно в одном экземпляре. Под эти две задачи в Kubernetes есть StatefulSet и DaemonSet.

Чем StatefulSet отличается от Deployment:

Три гарантии. Первая: стабильные имена. Поды называются redis-0, redis-1, redis-2 и сохраняют имя при пересоздании. Вторая: порядок. По умолчанию поды стартуют по очереди, следующий не запустится, пока предыдущий не пройдет readiness-пробу (глава 10), гасятся в обратном порядке. Третья: личный диск. volumeClaimTemplates создает отдельный PVC (глава 8) на каждый под, и после пересоздания под получает свой же диск с данными.

Для стабильных DNS-имен StatefulSet нужен headless Service: обычный Service из главы 5, но с clusterIP: None. Он не балансирует, а отдает DNS-запись на каждый под отдельно. Его имя указывается в поле serviceName.

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

apiVersion: v1
kind: Service
metadata:
  name: redis
  namespace: shop
spec:
  clusterIP: None
  selector:
    app: redis
  ports:
  - port: 6379
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis
  namespace: shop
spec:
  serviceName: redis
  replicas: 3
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
      - name: redis
        image: redis:7.4-alpine
        ports:
        - containerPort: 6379
        readinessProbe:
          exec:
            command: ["redis-cli", "ping"]
          periodSeconds: 5
        resources:
          requests:
            cpu: 100m
            memory: 256Mi
          limits:
            memory: 512Mi
        volumeMounts:
        - name: data
          mountPath: /data
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 5Gi
Проверяем:

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

kubectl get pods -n shop -l app=redis
# NAME      READY   STATUS    AGE
# redis-0   1/1     Running   2m10s
# redis-1   1/1     Running   95s
# redis-2   1/1     Running   70s

kubectl get pvc -n shop
# NAME           STATUS   CAPACITY   AGE
# data-redis-0   Bound    5Gi        2m10s
# data-redis-1   Bound    5Gi        95s
# data-redis-2   Bound    5Gi        70s
Каждый под доступен по собственному имени: redis-0.redis.shop.svc.cluster.local. Приложение может считать, что мастер живет в redis-0, и это имя переживет любые пересоздания.

При уменьшении replicas StatefulSet удаляет поды с конца (сначала redis-2), а их PVC оставляет. Вернете replicas обратно, под поднимется со старыми данными. С версии 1.32 поведением можно управлять штатно:

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

spec:
  persistentVolumeClaimRetentionPolicy:
    whenDeleted: Delete
    whenScaled: Retain
whenScaled: Delete удаляет PVC при уменьшении replicas, whenDeleted при удалении самого StatefulSet. По умолчанию везде Retain.

Честное предупреждение: StatefulSet не делает из трех redis-подов кластер. Репликацию, выборы мастера и failover настраивает само приложение. В проде для Postgres и Kafka обычно берут операторы (CloudNativePG, Strimzi) с логикой конкретной СУБД, но строятся они на тех же примитивах, так что понимать StatefulSet все равно придется.

DaemonSet: один под на каждой ноде:

Типовые жильцы: сборщики логов (fluent-bit, vector), node-exporter для Prometheus, CNI-плагины и CSI-драйверы. Поля replicas тут нет вообще, количество диктует кластер: добавили ноду, под на ней появился сам, нода ушла, под исчез вместе с ней.

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

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: node-exporter
  namespace: monitoring
spec:
  selector:
    matchLabels:
      app: node-exporter
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 10%
  template:
    metadata:
      labels:
        app: node-exporter
    spec:
      hostNetwork: true
      hostPID: true
      tolerations:
      - key: node-role.kubernetes.io/control-plane
        operator: Exists
        effect: NoSchedule
      containers:
      - name: node-exporter
        image: quay.io/prometheus/node-exporter:v1.9.1
        args: ["--path.rootfs=/host"]
        ports:
        - containerPort: 9100
        resources:
          requests:
            cpu: 50m
            memory: 64Mi
          limits:
            memory: 128Mi
        volumeMounts:
        - name: rootfs
          mountPath: /host
          readOnly: true
      volumes:
      - name: rootfs
        hostPath:
          path: /
Toleration здесь не для красоты: на control-plane нодах стоит taint, и без toleration агент туда не попадет. Подробно про taints и affinity поговорим в следующей главе. Если агент нужен не на всех нодах, добавьте nodeSelector в шаблон пода. А maxUnavailable в процентах ускоряет выкатку: с дефолтной единицей обновление на сотне нод ползет по одной за раз.

Обновления StatefulSet:

RollingUpdate идет от последнего пода к нулевому, по одному, с ожиданием Ready. Поле partition дает канарейку: при partition: 2 обновятся только поды с порядковым номером 2 и выше, остальные сидят на старой ревизии, пока не опустите partition до нуля.

Типичные грабли:

serviceName не совпадает с именем headless Service, или сервис вообще забыли создать. Поды работают, а DNS-имен нет.

volumeClaimTemplates неизменяемый. Увеличить диск правкой StatefulSet нельзя, kubectl apply вернет ошибку. Правят сами PVC (если StorageClass умеет allowVolumeExpansion), а потом пересоздают объект, не трогая поды: kubectl delete sts redis --cascade=orphan и apply манифеста с новым размером.

Под не прошел readiness после обновления, и выкатка молча стоит, остальные ждут. kubectl rollout status это покажет, не выкатывайте StatefulSet вслепую.

Нода умерла, под на ней завис в Terminating, новый не создается. Это не баг. StatefulSet гарантирует, что под с данным именем существует максимум в одном экземпляре, иначе два пода могли бы писать в один диск. Штатный выход: удалить объект ноды или поставить на нее taint node.kubernetes.io/out-of-service. Сносить под с --force можно, только когда точно уверены, что нода мертва.

Уменьшили replicas у базы, не выведя реплику из кластера средствами самой СУБД, и сломали кворум. Сначала команды СУБД, потом kubectl scale.

DaemonSet без requests. Агент стоит на каждой ноде, и утечка памяти в нем умножается на размер кластера. Requests и limits из главы 9 тут обязательны.

Что усвоили:

StatefulSet дает стабильные имена, порядок запуска и личные PVC, но кластеризацию оставляет приложению. DaemonSet держит ровно по одному поду на ноде и сам реагирует на изменения состава кластера. В следующей главе займемся тем, что здесь мелькало краем: стратегии обновления и rollback, graceful shutdown, nodeSelector, affinity и taints.
👍 ❤️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
  • Похожие темы

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

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

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