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

Kubernetes 1.17: Volume Snapshot в beta - пробуем автоматические снимки PVC перед деплоем

В Kubernetes 1.17 Volume Snapshot перешёл в beta. Разбираемся, как использовать снимки PVC для безопасного rollback StatefulSet-приложений перед деплоем.

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

Kubernetes 1.17 вышел 9 декабря 2019: Volume Snapshot достиг beta, Cloud Provider extraction продолжается, улучшен topology-aware routing.

Kubernetes 1.17 ещё не вышел - релиз ожидается 9 декабря - но release notes уже опубликованы, и мы их прочитали. Главное что нас там зацепило: Volume Snapshot переходит в beta. Не потому что это красивая строчка в changelog, а потому что мы давно присматривались к этой фиче с прицелом на конкретную задачу: автоматические снимки PVC перед деплоями StatefulSet-приложений.

Расскажем, что это даёт и как выглядит в нашей managed-инфраструктуре.

Почему это важно именно для StatefulSet

С Deployment-ами жизнь проще: откатить kubectl rollout undo - и за несколько секунд поды пересоздались, приложение вернулось к прошлой версии. Данных там нет, данные в базе - это отдельная история.

StatefulSet - другое дело. Там данные живут на PVC, привязанных к конкретным подам. Если деплой новой версии приложения побил схему базы, изменил формат данных на диске или просто что-то пошло не так с init-контейнером, который мигрирует состояние - откат пода не поможет. Контейнер откатился, данные на томе - нет.

Раньше стандартный ответ на этот вопрос был такой: делайте бэкап средствами самого приложения или storage-платформы, снаружи Kubernetes. Это работает, но разрывает процесс деплоя: у вас GitOps, ArgoCD, автоматические helm upgrade - а резервную копию перед деплоем нужно руками запускать снаружи или городить отдельный Job с доступом к хранилищу.

Volume Snapshot позволяет встроить снимок прямо в Kubernetes-логику.

Что такое Volume Snapshot в 1.17

Три новых ресурса:

  • VolumeSnapshot - запрос на создание снимка конкретного PVC.
  • VolumeSnapshotContent - фактический снимок в хранилище, аналог PersistentVolume.
  • VolumeSnapshotClass - класс снимков с параметрами драйвера, аналог StorageClass.

Создать снимок - это манифест типа:

apiVersion: snapshot.storage.k8s.io/v1beta1
kind: VolumeSnapshot
metadata:
  name: postgres-snapshot-before-deploy
spec:
  volumeSnapshotClassName: csi-hostpath-snapclass
  source:
    persistentVolumeClaimName: postgres-data-0

После применения CSI-драйвер уходит в хранилище, делает снимок и возвращает статус. Из снимка можно восстановить PVC - через dataSource в PersistentVolumeClaim. Именно это нам и нужно для rollback.

Важное условие: всё это работает только с CSI-драйверами. In-tree storage-плагины (kubernetes.io/aws-ebs, kubernetes.io/gce-pd и подобные) снимки через этот API не поддерживают - нужен CSI-эквивалент.

Как это встраивается в деплой

Идея, которую мы хотим проверить: перед каждым helm upgrade для StatefulSet-приложения автоматически создавать снимки всех PVC, связанных с этим релизом.

Схема работы выглядит так:

flowchart LR
    A[helm upgrade] --> B[pre-upgrade Job]
    B --> C[VolumeSnapshot PVC-0]
    B --> D[VolumeSnapshot PVC-1]
    C --> E[деплой]
    D --> E
    E --> F{всё ок?}
    F -- нет --> G[восстановить PVC из снимка]
    F -- да --> H[удалить старые снимки]

Конкретно: Helm hook pre-upgrade запускает Job, который создаёт VolumeSnapshot для каждого PVC релиза. Job дожидается readyToUse: true в статусе снимка, потом завершается. Helm продолжает деплой.

Если деплой пошёл не так и нужен rollback: берём последний снимок, создаём из него PVC, переключаем StatefulSet - и данные на момент снимка восстановлены. Без ручных бэкапов снаружи кластера.

Что не так идеально, как звучит

Несколько нюансов, которые мы уже поняли при тестировании на dev.

Снимок не гарантирует консистентность данных приложения. Volume Snapshot делает снимок блочного устройства или файловой системы. Это не дамп базы. Postgres может быть в середине транзакции, данные в buffer cache могут быть не записаны. Для корректного снимка приложение надо либо остановить, либо попросить его сбросить буферы - что добавляет сложности в pre-upgrade Job.

Retention - наша головная боль. Снимки занимают место. Если деплоев в день несколько, снимки будут копиться. Нужна логика удаления старых - например, хранить только N последних снимков на релиз. Это не встроено в Kubernetes, придётся реализовывать самим или через дополнительный CronJob.

Не все CSI-драйверы поддерживают снимки. Beta API есть в 1.17, но драйвер для вашего хранилища может не реализовывать snapshot capability. Надо проверять конкретный драйвер - не все обновились под VolumeSnapshot beta.

Где мы сейчас

На production-кластерах этого нет. Мы тестируем на dev: взяли PostgreSQL в StatefulSet на CSI-хранилище, написали минимальный pre-upgrade Job, прогнали несколько циклов деплой-снимок-восстановление. В целом работает.

Вопрос с консистентностью данных для Postgres решаем через pg_start_backup()/pg_stop_backup() в Job-е - грубо, но для тестирования достаточно. Правильное решение выглядит сложнее.

После выхода 1.17 GA и обновления кластеров начнём постепенно переводить реальные StatefulSet-окружения на эту схему. Начнём с не самых критичных - там где потеря снимка или баг в rollback-механизме не катастрофа, а неприятность. Доделаем retention, протестируем на реальных нагрузках - тогда и посмотрим, насколько это решение надёжнее того, что было.

Пока наблюдение такое: сама концепция правильная, и то что это наконец дошло до beta - сигнал что API устаканился и можно начинать строить на нём реальные процессы. Но от идеи до работающего production-паттерна ещё есть что допиливать.

Контакт

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

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