9.0 KiB
Разнесение restic-операций на фазы под Intelligent Tiering
- Дата: 2026-06-22
Контекст
Бэкапы restic уже лежат в Yandex Object Storage на стандартном классе хранения (STANDARD). Yandex выпустил класс «Умное хранилище» (Intelligent Tiering, IT): объекты автоматически охлаждаются до архивного уровня (примерно 0,63 ₽/ГБ против 2,38 ₽/ГБ у STANDARD) с сохранением мгновенного доступа на всех уровнях (анонс: https://yandex.cloud/ru/blog/s3-intelligent-tiering). Данные restic — это профиль «записал один раз, читаю редко», то есть идеальный кандидат на охлаждение. Цель — перевести уже лежащие бэкапы со STANDARD на IT и сэкономить на хранении.
Проблема в том, что любой repack/recompress объекта создаёт новый
объект, который входит в IT как «Частый доступ» и заново стартует таймер
охлаждения (30 дней → «Нечастый», ещё 90 → «Архивный»). А наш оркестратор
backup-all.py гнал каждую ночь связку backup → check → forget --prune → check. Ночной prune перепаковывает data-паки → постоянно сбрасывает
охлаждение → отменяет экономию IT. Нужно было перестроить обслуживание
так, чтобы не мешать охлаждению.
Дополнительное ограничение по миграции существующих данных: по докам
Yandex изменение класса бакета по умолчанию не трогает уже загруженные
объекты — они остаются в STANDARD, новый класс применяется только к новым
загрузкам. Перевести уже лежащие бэкапы в IT можно lifecycle-правилом или
copy-in-place (aws s3 cp --storage-class INTELLIGENT_TIERING); оба
тарифицируются как операция TRANSITION. Перезаливка объектов заново для
restic не годится — это churn, эквивалентный репаку. Управления бакетом
(terraform/aws-cli) в проекте нет, так что миграцию пришлось бы делать
вручную или заводить такое управление.
Идентификатор класса подтверждён доками — INTELLIGENT_TIERING (Yandex
поддерживает STANDARD, COLD, ICE, INTELLIGENT_TIERING). Остаётся
неподтверждённым только min-retention / штраф за раннее удаление архивного
уровня внутри IT.
Рассмотренные варианты
- Отдельные скрипты на репозиторий (
backup.sh/check.sh/prune.sh/verify.sh+ свои cron-записи, как в исходном гайде). Ближе к гайду дословно, но теряем оркестратор: авто-дискавери приложений, мультистор и единые apprise-уведомления. Плюс 4 независимые cron-записи провоцируют наложение операций (долгийpruneналезает на ночнойbackup→ конфликт restic-локов). Отвергнут. - Адаптировать
backup-all.py— добавить фазы и расписание внутрь оркестратора, один ночной триггер. Сохраняет всю существующую инфраструктуру, фазы идут последовательно в одном процессе → локи не конфликтуют. Выбран. - Расписание: простые knobs (день недели/число/месяцы как поля
конфига) vs cron-выражения через
croniter. Knobs — без зависимости, но негибко (новая ось → правка кода). Выбранcroniter: пакет ставится из apt (python3-croniter) тем же механизмом, что и остальное, а гибкость реальная — поменять «раз в квартал» на «раз в месяц» = правка одной строки конфига. - Storage class сейчас: Вариант A (
-o s3.storage-class=INTELLIGENT_TIERINGв restic) vs Вариант B (lifecycle-правило / copy-in-place на бакете) vs отложить. A влияет только на новые объекты — уже лежащие бэкапы в STANDARD так и останутся; B переводит существующие данные, но требует завести управление бакетом, которого в проекте нет. Выбрано отложить до подтверждения min-retention архивного уровня IT.
Решение
Операции restic в files/backups/backup-all.py разнесены на фазы с
разной частотой, потому что у них принципиально разная цена для IT:
backup+forget— каждый прогон.forgetтеперь без--prune: удаляет только метаданные снапшотов (операция DELETE не тарифицируется), не репакует data-паки и не сбивает охлаждение.check(структурный) — еженедельно;prune— квартально;verify(check --read-data-subset) — помесячно. Расписание задано cron-выражениями в секции[schedule]конфига и вычисляется черезcroniter. Триггер один ночной, фазы одного прогона идут последовательно в одном процессе → restic-локи между ними не конфликтуют. Наложение соседних прогонов гаситсяflock -nв cron.
prune тюнингован под IT (--max-unused 20%, --max-repack-size 5G):
чем меньше холодных паков переписываем, тем дольше держится охлаждение.
Storage class IT и lifecycle на бакете намеренно отложены: пока не
подтверждён min-retention архивного уровня IT, transition существующих
данных рискован (возможен штраф за раннее удаление при последующем prune).
Сама миграция уже лежащих бэкапов делается lifecycle-правилом или
copy-in-place с --storage-class INTELLIGENT_TIERING, а не сменой
дефолтного класса бакета (та сработает только для новых объектов).
Retention оставлен прежним (--keep-daily 90 --keep-monthly 36) — это
решение про охлаждение и частоту операций, а не про глубину истории.
Последствия
+Ночной prune больше не сбрасывает охлаждение — IT реально экономит на архивном уровне.+Нет наложения restic-операций: последовательные фазы +flock.+Расписание обслуживания меняется правкой конфига, без релиза кода.-Новая зависимость на сервере:python3-croniter(и явно зафиксированныйpython3-requests).-Структурныйcheckтеперь еженедельный, а не каждую ночь: битый бэкап может остаться незамеченным до недели. Для хобби-сервера приемлемо.-Подвох croniter: при суточном триггере поля минут/часов в выражениях декоративны (держим* *) — фаза идёт в момент ночного прогона, а не во время из выражения.- Осталось сделать: подтвердить min-retention / штраф за раннее удаление
архивного уровня IT, затем перевести существующие бэкапы со STANDARD на
INTELLIGENT_TIERINGчерез lifecycle-правило или copy-in-place.