← Kubernetes: рабочие нагрузки
8 мин · средне · Урок 8 из 11

StatefulSet и headless Service

Четыре гарантии StatefulSet, per-pod DNS через clusterIP None и как volumeClaimTemplates выдаёт каждой реплике свой диск.

StatefulSet против Deployment

Deployment - контроллер, который держит заданное число одинаковых Pod. Pod - наименьшая единица, которую запускает Kubernetes. Под управлением Deployment Pod взаимозаменяемы. Один упал, контроллер поднял новый со случайным именем, и разницы нет. Это подходит для stateless-сервисов (без своего состояния и диска): nginx, REST API, фронтенд.

StatefulSet - контроллер для приложений, где у каждой реплики своя постоянная привязка. Это устойчивое имя, свой диск и своё сетевое имя. Он даёт четыре гарантии.

Это нужно базам данных (Postgres, MySQL), очередям (Kafka) и координаторам (etcd).

Headless Service

Service · headless name: db clusterIP: None StatefulSet serviceName: db replicas: 2 совпадение строк
Имя связывает два объекта: разошлось и per-pod DNS не появится.

Service - объект, который даёт группе Pod единый сетевой адрес. Обычный Service выдаёт один cluster IP и распределяет запросы между Pod. Какой именно Pod ответит, неважно.

StatefulSet требует обращаться к конкретному Pod. Для этого Service делают headless (без единого cluster IP): задают clusterIP: None. Тогда DNS вместо одного адреса Service возвращает адреса всех Pod по отдельности.

db-0.db.<namespace>.svc.cluster.local
db-1.db.<namespace>.svc.cluster.local

Headless Service добавляет каждому Pod своё DNS-имя:

apiVersion: v1
kind: Service
metadata:
  name: db
spec:
  clusterIP: None
  selector:
    app: db
  ports:
    - port: 5432
      protocol: TCP

Эти имена работают, только если в StatefulSet задано поле serviceName со ссылкой на этот Service.

volumeClaimTemplates

volumeClaimTemplates - шаблон PVC для StatefulSet. Это не один общий диск на всех. По шаблону каждой реплике создаётся свой PVC.

volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: [ReadWriteOnce]
      resources:
        requests:
          storage: 1Gi

При replicas: 2 контроллер создаст два PVC. Имя PVC строится по формуле <template-name>-<sts-name>-<ordinal>. Для шаблона data и StatefulSet db это data-db-0 и data-db-1.

db-0 ─┬─ PVC  data-db-0
      └─ DNS  db-0.db
db-1 ─┬─ PVC  data-db-1
      └─ DNS  db-1.db

Том монтируется в контейнер по имени шаблона. volumeMounts.name: data подключает PVC data-db-0 к Pod db-0.

Полный минимальный манифест

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: db
spec:
  serviceName: db
  replicas: 2
  selector:
    matchLabels:
      app: db
  template:
    metadata:
      labels:
        app: db
    spec:
      containers:
        - name: db
          image: busybox:1.36
          command: ["sh", "-c", "echo running; sleep 3600"]
          volumeMounts:
            - name: data
              mountPath: /data
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes: [ReadWriteOnce]
        resources:
          requests:
            storage: 1Gi

Поле serviceName: db ссылается на headless Service. Без него per-pod DNS не появится.

Поле selector.matchLabels должно совпадать с template.metadata.labels. Если они расходятся, apply упадёт с ошибкой selector does not match template labels.

Удаление Pod и удаление StatefulSet

Pod db-0 удалили: контроллер создаст его заново с тем же именем. PVC data-db-0 остаётся отдельным объектом, и новый db-0 снова смонтирует тот же диск. Данные на месте.

Весь StatefulSet удалили через kubectl delete sts db: PVC по умолчанию не удаляются. Данные не теряются. Чтобы убрать тома, PVC удаляют отдельно.

Поведение настраивается полем spec.persistentVolumeClaimRetentionPolicy (стабильно с 1.32). В нём два ключа, whenDeleted и whenScaled, у обоих значения Retain (умолчание) или Delete. При whenScaled: Delete уменьшение реплик уносит PVC лишних Pod вместе с ними.

Полезные команды

# Список объектов StatefulSet
kubectl get sts

# Подробно: serviceName, ordinals, PVC
kubectl describe sts db

# Проверка per-pod DNS из временного Pod
kubectl run dns-test --rm -it --image=busybox:1.36 -- nslookup db-0.db

# PVC, созданные по шаблону
kubectl get pvc

Домашка

Теорию закрепляет задание на своём кластере: 2 шага с проверкой. Кластер поднимается по треку Kubernetes локально, задания лежат в репозитории k8s-practice.

./run.sh start k8s-statefulset     # развернуть стартовое состояние
./run.sh verify k8s-statefulset    # проверить текущий шаг

Коротко

Дрилл · вопросы как на собесе

Проверь себя без подсказок

Сформулируй ответ вслух или письменно, потом раскрой и честно отметь: знал или нет.

01

В чём разница между Deployment и StatefulSet?

K8S-014

Прошли материал до конца? Отметьте урок пройденным.