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. Менять это не торопимся.
- Helm: менеджер пакетов для Kubernetes от Deis · 1 декабря 2015
- Docker 1.11: containerd и runc как отдельные процессы · 6 июля 2016