ADG Оставить заявку
Блог DevOps 5 мин чтения

Kubernetes 1.3: StatefulSets и federation - тестируем на PostgreSQL

Kubernetes 1.3 принёс StatefulSets и federation. Тестируем StatefulSets для PostgreSQL на стенде: что с persistent volumes при переезде пода между нодами.

Контекст момента

Kubernetes 1.3 вышел с поддержкой federation кластеров и StatefulSets, сделав управление stateful-приложениями частью основного API оркестратора

Kubernetes 1.3 вышел на прошлой неделе, и два анонса сразу привлекли внимание: federation кластеров и StatefulSets (в 1.3 они ещё называются PetSets, но уже анонсировано переименование). С federation понятно - это про запуск одного логического кластера поверх нескольких физических, для мультирегионального деплоя. Нам это пока не актуально. А вот StatefulSets - другое дело.

Вопрос о том, как запускать stateful-приложения в Kubernetes, у нас возникал с самого начала работы с кластером. Деплоим через Helm уже несколько приложений, но всё это stateless-сервисы - контейнер упал, поднялся на другой ноде, взял конфиг из ConfigMap и продолжил работу. Для базы данных так не получается.

Что такое StatefulSets и почему это важно

До 1.3 запустить PostgreSQL в Kubernetes означало либо смириться с ограничениями обычного Deployment (поды не имеют стабильных имён, при перезапуске могут оказаться на другой ноде без гарантии что к ним примонтируется тот же том), либо держать базу снаружи кластера и подключаться через Service. Большинство команд выбирали второй вариант.

StatefulSets дают три вещи, которых раньше не было:

  • Стабильные сетевые идентификаторы. Под получает предсказуемое имя вида pg-0, pg-1, и оно не меняется при перезапуске. Это критично для репликации, где реплика должна знать адрес мастера.
  • Стабильное хранилище. PersistentVolumeClaim создаётся per-pod и не отвязывается при удалении пода. Если pg-0 упал и поднялся на другой ноде, он получит тот же PVC - при условии что storage backend это поддерживает.
  • Упорядоченный запуск. Поды стартуют последовательно: сначала pg-0, потом pg-1. Это позволяет мастеру подняться раньше реплик.

Звучит как то что мы давно хотели. Решили проверить на стенде.

Стенд и что пошло не так

Собрали тестовый кластер: три ноды на KVM, Kubernetes 1.3.0, в качестве storage backend - hostPath, что заведомо плохо для продакшна, но для понимания механики сойдёт. Написали минимальный StatefulSet-манифест для PostgreSQL 9.5:

apiVersion: apps/v1alpha1
kind: PetSet
metadata:
  name: pg
spec:
  serviceName: "postgres"
  replicas: 2
  template:
    spec:
      containers:
      - name: postgres
        image: postgres:9.5
        volumeMounts:
        - name: pgdata
          mountPath: /var/lib/postgresql/data
  volumeClaimTemplates:
  - metadata:
      name: pgdata
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 5Gi

pg-0 поднялся, инициализировал базу, всё нормально. Потом мы убили ноду, на которой он жил - и вот тут началось интересное.

Под завис в статусе Terminating значительно дольше, чем ожидали. Kubernetes ждал подтверждения что нода мертва, прежде чем разрешить переместить под с его PVC на другую ноду. Это не баг - это намеренное поведение: оркестратор защищается от ситуации когда старый под ещё жив и пишет в том, а новый уже тоже поднялся и пишет туда же. Split-brain на уровне хранилища.

Проблема с PVC и RWO

Разобравшись с задержкой, столкнулись с более фундаментальным вопросом. AccessMode ReadWriteOnce означает что том монтируется только к одной ноде одновременно. Когда pg-0 переехал с ноды A на ноду B, PVC надо отмонтировать от A и примонтировать к B. Если storage backend - сетевое блочное устройство вроде Ceph или iSCSI, это происходит штатно. Если hostPath - том физически привязан к конкретной ноде и переехать не может.

Это не открытие, но важно понять это экспериментально, а не из документации. Для реального PostgreSQL в Kubernetes нужен сетевой storage, который умеет открепляться от одной ноды и прикрепляться к другой. Ceph, NFS, iSCSI, облачные диски - всё это потенциально подходит. hostPath, local-storage без дополнительной логики - нет.

В нашей инфраструктуре есть Ceph-кластер. Следующий шаг - перепробовать тот же манифест с Ceph RBD вместо hostPath и посмотреть насколько переезд пода становится менее болезненным.

Federation: смотрели, но не трогали

Вторая крупная фича 1.3 - federation. Идея в том чтобы один federation API server управлял несколькими кластерами Kubernetes как одним целым: деплоишь приложение в federation, оно раскатывается по кластерам согласно политике. Для мультирегиональных инсталляций или DR-сценариев это выглядит интересно.

Пока не трогали: для federation нужно как минимум два полноценных кластера и DNS-провайдер (поддерживается Route53 и Google Cloud DNS). Ни того ни другого на стенде нет. Если будет задача развернуть приложение в двух ЦОД с автоматическим failover - вернёмся к этой теме.

Где мы сейчас

StatefulSets - это реальный шаг к тому чтобы Kubernetes стал пригоден для баз данных. Но «пригоден» не означает «просто». Persistent volumes, сетевой storage, аккуратное обращение с PVC при обслуживании нод - это всё требует понимания и правильного storage backend. Просто взять и запустить PostgreSQL в кластере с hostPath - работает только пока ноды не падают.

Следующий шаг у нас - тот же стенд, но с Ceph RBD. Если переезд пода с данными пройдёт штатно, будем думать о том, имеет ли смысл переносить часть stateful-нагрузки внутрь кластера.

Пока PostgreSQL для продакшн-проектов живёт снаружи Kubernetes. Менять это не торопимся.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.