Перейти к основному содержанию Перейти к навигации Перейти к нижнему колонтитулу

Резервное копирование и восстановление

Снимайте снимки зашифрованных репозиториев в адресуемое по содержимому хранилище чанков, где загружаются только изменённые ячейки, а каждый снимок восстанавливается напрямую. Или храните копию на другой машине. Восстанавливайте где угодно и автоматизируйте это именованными стратегиями и таймерами systemd.

Резервное копирование и восстановление

Rediacc делает резервные копии зашифрованных репозиториев и восстанавливает их на той же машине или на другой. Резервные копии зашифрованы, потому что зашифрован сам репозиторий: за пределы машины уходит только шифротекст, а для восстановления нужны данные LUKS вашего репозитория.

Есть два способа резервного копирования, и они отвечают на разные вопросы.

  • Снимки в хранилище чанков (rdc backup snapshot) сохраняют историю, по которой можно вернуться назад. Это основной путь.
  • Копия на другой машине (rdc repo push, rdc repo pull) сохраняет репозиторий таким, какой он есть сейчас, на оборудовании, которое вы контролируете. Облачный аккаунт здесь не участвует.

Эти два пути независимы. Репозиторий, для которого сделана резервная копия одним способом, не защищён другим.

Как работают снимки

Образ репозитория нарезается на ячейки фиксированного размера по фиксированной сетке. Каждая ячейка либо является дырой, то есть в неё никогда ничего не записывалось, либо хранится под ключом, который является SHA-256 шифротекста этой ячейки.

Именно из этого единственного решения вытекают все остальные свойства.

Платить приходится только за реальные изменения. Первый снимок загружает все записанные ячейки. Каждый следующий запуск спрашивает у файловой системы, какие экстенты были затронуты, читает и хеширует только их и загружает только те ячейки, которых ещё нет в хранилище. Репозиторий, данные которого почти не двигались, загружает почти ничего, и запуск занимает минуты, а не время, пропорциональное размеру образа.

Одинаковые данные хранятся один раз. Поскольку ключ – это хеш содержимого, два снимка, у которых совпадает ячейка, разделяют один и тот же объект, и то же самое верно для репозитория и его форков: семейство форков резервируется относительно одной линии происхождения, а не дублирует родителя.

Восстановление старого снимка не медленнее, чем восстановление свежего. Здесь нет цепочки инкрементов, которую нужно проигрывать по порядку. Восстановление разворачивает снимок в полный список ячеек и забирает эти ячейки напрямую, поэтому время восстановления зависит от размера образа и вашей пропускной способности, а не от того, как долго вы делаете резервные копии. Дыры остаются дырами, поэтому разреженный образ восстанавливается разреженным, а ячейка, встречающаяся в нескольких местах образа, скачивается один раз.

Каждый снимок самодостаточен. Нет «полной резервной копии», которую нельзя потерять, и нет окна, в котором сломанный инкремент делает недействительными все последующие. Любой снимок из списка можно восстановить напрямую.

Проверка – это пересчёт хеша, а не доверие. Поскольку ключ – это хеш содержимого, проверка резервной копии означает получение ячеек и их хеширование. rdc backup verify делает выборочную проверку; rdc backup verify --deep пересчитывает хеш каждой сохранённой ячейки.

Прерванный запуск не пропадает зря. Загрузка возобновляется, не отправляя повторно уже дошедшие ячейки, а перезапуск частичного восстановления пересчитывает хеш того, что уже есть на диске, и переиспользует это, вместо повторного скачивания.

Во что это вам обходится

Квота считается в физически уникально сохранённых байтах: это то, что реально хранится после дедупликации, а не сумма того, что ваши снимки логически представляют. Тридцать снимков медленно меняющегося репозитория обходятся почти как один. rdc backup usage показывает сохранённые байты относительно вашей квоты, которая является числом на подписку, начиная с 10 ГБ на плане Community.

Что нужно для снимков

Загрузка снимка проходит через сервер аккаунта, который авторизует каждый запуск по установленной на репозитории лицензии и выдаёт машине кратковременное разрешение на запись. Поэтому для этого пути нужны сервер аккаунта, доступный машине, и лицензированный репозиторий. Без них снимок не пропускается тихо, а отклоняется, и rdc backup manifests, rdc backup usage и rdc backup retention не находят ничего для чтения.

Это касается и --dry-run. Лицензия считывается ещё до того, как запуск решает, планирует он или загружает, поэтому пробный запуск – это предпросмотр реальной работы, а не способ попробовать команду без учётных данных.

Передача между машинами, push и pull, не нуждается ни в том, ни в другом. Это прямая передача между двумя машинами, уже присутствующими в вашей конфигурации.

Чего снимок не обещает

  • Снимок покрывает один репозиторий, а не всю машину сразу. Каждый репозиторий захватывается в свой собственный момент времени. Если два репозитория зависят друг от друга, их снимки не образуют согласованную пару.
  • Это не непрерывная репликация. Снимок – это точка во времени, которую вы зафиксировали, и вы можете потерять всё, что было записано с момента последнего снимка. Сколько именно, зависит от того, как часто вы запускаете снимки.
  • Сохранённые объекты пишутся один раз, но это не сертифицированный WORM. Ячейки записываются с условием «только создание», разрешение, которое получает машина, не позволяет ничего удалять, а удаления происходят на стороне сервера согласно политике хранения. Это реальный барьер против того, чтобы скомпрометированная машина уничтожила собственные резервные копии. Это не сертификация соответствия, и она не проходит аудит как таковая.

Путь хранения через rclone исчез

rdc repo push --to <storage> и связанные с ним команды раньше копировали весь файл резервной копии в облачного провайдера, которого вы регистрировали сами. Теперь они отклоняют хранилище в качестве назначения и называют замену. Передача между машинами никогда не шла через rclone и не затронута. Если вам всё ещё нужно прочитать архив, записанный таким способом, см. Чтение архива, записанного до отказа от этого пути.

Команды хранилища чанков

# Загрузить снимок. Первый запуск сеет, последующие отправляют только изменённые ячейки.
rdc backup snapshot my-app

# Спланировать без загрузки: сообщает, что будет перемещено.
rdc backup snapshot my-app --dry-run

# Остановить контейнеры, заморозить, перезапустить, затем загрузить.
rdc backup snapshot my-app --cold

# Не доверять локальному якорю и заново загрузить весь инвентарь.
# Это заново загружает всё и заново расходует квоту; используйте только
# когда точно известно, что якорь повреждён.
rdc backup snapshot my-app --reseed

# Проверить сохранённый инвентарь и вашу квоту.
rdc backup verify my-app
rdc backup manifests my-app
rdc backup usage
ОпцияОписание
<repo-ref> (позиционный)Репозиторий для снимка
--dry-runТолько планирование: без загрузки. Сообщает, что будет перемещено
--coldОстановить контейнеры, заморозить, перезапустить, затем загрузить. Нельзя сочетать с --dry-run
--reseedНе доверять локальному якорю и загрузить полный инвентарь. Заново загружает всё и заново расходует квоту
--debugВключить подробный вывод

Холодные снимки (--cold)

Холодный снимок останавливает репозиторий перед заморозкой, поэтому сохранённый образ получается согласованным на уровне приложения, а не только на уровне сбоя. Он выполняется на самой машине:

# Все репозитории на хранилище данных по умолчанию.
sudo renet backup snapshot --cold

# Только названные вами репозитории. --repo принимает GUID репозитория и может повторяться.
sudo renet backup snapshot --cold --repo <guid> --repo <guid>

--cold нельзя сочетать с --dry-run. Пробный запуск, который останавливает контейнеры, не является пробным, а запуск, который их не останавливает, не является холодным, поэтому renet отклоняет это сочетание, вместо того чтобы выбирать значение за вас.

Что делает холодный запуск

Для каждого выбранного репозитория, в этом порядке:

  1. Остановить его контейнеры.
  2. Сбросить точку монтирования репозитория и хранилище данных на диск.
  3. Подтвердить, что контейнеры действительно остановились.
  4. Сделать copy-on-write reflink образа репозитория.
  5. Снова запустить контейнеры.

Только после этого начинается загрузка, когда все репозитории уже снова работают.

Простой – это заморозка, а не передача. Reflink – это только метаданные, поэтому он занимает одно и то же время независимо от того, весит репозиторий 1 ГБ или 100 ГБ. С загрузкой так не получится: она растёт вместе с изменившимися байтами, а первый снимок загружает весь ненулевой инвентарь. Если держать контейнеры остановленными до завершения загрузки, простой оказался бы привязан к объёму данных, а при первом посеве это означает часы вместо миллисекунд.

Все выбранные репозитории останавливаются не по одному, а в рамках одного окна. Это стоит немного более долгого простоя на репозиторий, но взамен даёт единую точку согласованности для всего набора.

Репозиторий без запущенных контейнеров уже находится в тихом состоянии. Снимок делается без какого-либо простоя, и это нормальный результат, а не сбой.

Во что обходится простой

По измерениям на реальной машине суммарный простой составил 222 мс:

ФазаИзмереноЧто происходит
cold_down64 мсКонтейнеры останавливаются
cold_sync26 мсТочки монтирования репозитория и хранилище данных сбрасываются на диск
cold_verify31 мсПодтверждается, что контейнеры остановились
cold_stage0 мсReflink образа репозитория
cold_up99 мсКонтейнеры запускаются снова

Основную часть занимает перезапуск контейнеров, а стадирование фактически бесплатно: reflink не фиксируется на уровне миллисекундного разрешения. Впрочем, читайте этот ноль вместе с записями по каждому репозиторию, а не отдельно. Запуск, отклонивший все репозитории, тоже сообщает cold_stage=0ms, и только записи говорят, с каким из этих двух случаев вы имеете дело.

Эта разбивка – доказательство, а не украшение. Ни одна из этих пяти фаз не читает и не отправляет данные репозитория, поэтому ни одна из них не растёт вместе с ростом резервной копии. Растёт только загрузка, а она выполняется уже после того, как простой закончился.

renet выводит те же цифры по завершении запуска, так что вы можете измерить свои собственные машины, а не доверять нашим цифрам:

Cold backup: <n> repositories quiesced, outage 222ms (cold_down=64ms cold_sync=26ms cold_verify=31ms cold_stage=0ms cold_up=99ms)

JSON-запись каждого репозитория тоже хранит тот же простой и те же фазы, поэтому позже можно отличить холодный снимок от горячего, не гадая по времени.

Когда выбирать холодный режим

По умолчанию используется горячий режим, и это правильный выбор для большинства репозиториев. Горячий снимок согласован на уровне сбоя, то есть находится в том состоянии, в каком оказался бы репозиторий после отключения питания, и не стоит вообще никакого простоя. Большинство баз данных и очередей восстанавливаются из этого состояния самостоятельно.

Холодный режим выбирайте для данных, которые нельзя безопасно захватить в момент записи. База данных со своим собственным журналом упреждающей записи и состоянием в памяти – очевидный пример. Вы обмениваете короткий, измеренный простой на снимок, который приложение может открыть без предварительного восстановления.

От чего отказывается холодный запуск

Отказ – это и есть функция. Резервная копия, помеченная как холодная, но ничего на самом деле не остановившая, – это ложь, которая вскроется только во время восстановления, поэтому renet никогда не понижает холодный запуск до горячего молча:

  • Контейнеры, которые не остановились. После остановки renet спрашивает у собственного Docker-сокета репозитория, работает ли ещё что-нибудь. Если да, этот репозиторий отклоняется вместо того, чтобы быть снятым. Проверка отказывает в безопасную сторону: если сокет недоступен или список контейнеров нельзя прочитать, состояние покоя считается неподтверждённым, а неподтверждённое отклоняется.
  • Лицензия, которую невозможно прочитать. Лицензии проверяются до простоя, а не после, потому что репозиторий, чью лицензию нельзя прочитать, всё равно никогда не смог бы ничего загрузить. Такой репозиторий пропускается без остановки. Если ни у одного из выбранных репозиториев нет читаемой лицензии, весь запуск отклоняется прежде, чем упадёт хоть один контейнер.
  • Второй холодный запуск на том же хранилище данных. Блокировка охватывает всё хранилище данных, и занятая блокировка отклоняется сразу же, не остановив ничего. Два перекрывающихся запуска остановили бы контейнеры, которые каждый из них считает своими, а второй запустил бы репозитории, которые первый ещё замораживает. Пропустить запуск и подождать следующего – лучше, чем это.

Если запуск прерывается, пока контейнеры остановлены, будь то systemctl stop или перезагрузка, renet снова запускает их перед завершением. Восстановление на уровне машины – это последний рубеж: оно обнаруживает холодную резервную копию, чей владелец пропал, и поднимает эти репозитории обратно.

Отправка резервной копии на другую машину

Копирование репозитория на вторую машину по SSH:

rdc repo push my-app --to server-1

--to <machine> разрешает назначение из вашей конфигурации, а --to-machine <machine> говорит то же самое явно. Имя хранилища отклоняется: этот путь упразднён.

Зашифрованный образ копируется с ТЕМ ЖЕ GUID, поэтому это резервная копия или миграция, а не форк. Чтобы получить независимую копию, сначала выполните rdc repo fork, а затем отправьте форк.

Первый push переносит весь образ. Каждый последующий push отправляет только изменившиеся блоки относительно неизменного базового образа, хранящегося на обеих машинах, без необходимости выставлять какие-либо флаги. --delta-base <guid> позволяет назвать эту базу самостоятельно, если нужно.

Отправленная копия оказывается на цели как артефакт резервной копии, а не как работающий репозиторий. Превратите её в репозиторий с помощью rdc backup restore:

rdc backup restore my-app@server-1 --as my-app --machine server-1 --up

Для резервного копирования на определённый момент времени используйте вместо этого хранилище чанков: rdc backup snapshot my-app загружает только изменившиеся ячейки, а rdc backup restore my-app --at <snapshot> возвращает любой из них.

ОпцияОписание
<ref> (позиционный)Ссылка на репозиторий для отправки
--to <remote>Целевая машина или кластер
--to-machine <machine>Целевая машина, указанная явно
--provision <provider>Развернуть целевую машину через этого облачного провайдера, если её ещё не существует
--checkpointСоздать контрольную точку CRIU перед отправкой (для контейнеров с меткой rediacc.checkpoint=true). Цель автоматически восстанавливается при repo up
--forceПерезаписать существующую резервную копию
--bwlimit <limit>Ограничение пропускной способности для передачи rsync (например, 10M, 500K)
--delta-base <guid>Передавать только изменившиеся блоки относительно этого неизменного базового GUID. Опустите для автоматического выбора базы
--strategy <strategy>Стратегия блочной дельты при использовании базы дельты: auto, physical или shared
--debugВключить подробный вывод
--skip-router-restartПропустить перезапуск сервера маршрутов после операции

Получение резервной копии с другой машины

Забрать репозиторий обратно с машины, которая его хранит:

rdc repo pull my-app --from server-1

Добавьте --up, чтобы смонтировать и развернуть в той же команде. Чтобы восстановить из хранилища чанков вместо этого, используйте rdc backup restore my-app --at <snapshot-id>.

Pull отказывается перезаписывать репозиторий, который в данный момент смонтирован. Сначала размонтируйте его, выполните pull, затем снова поднимите его командой rdc repo up. Репозитории на основе каталогов – исключение: они синхронизируются на месте, пока смонтированы.

ОпцияОписание
<ref> (позиционный)Ссылка на репозиторий для получения
--from <remote>Исходная машина или кластер
--from-machine <machine>Исходная машина, указанная явно
--forceПерезаписать существующую локальную резервную копию
--upСмонтировать и развернуть репозиторий после получения
--bwlimit <limit>Ограничение пропускной способности для передачи rsync (например, 10M, 500K)
--delta-base <guid>Получать только изменившиеся блоки относительно этого неизменного базового GUID
--strategy <strategy>Стратегия блочной дельты при использовании базы дельты: auto, physical или shared
--debugВключить подробный вывод
--skip-router-restartПропустить перезапуск сервера маршрутов после операции

Список резервных копий

Список снимков в хранилище чанков:

rdc backup manifests my-app

Каждая строка – это одна сохранённая точка во времени:

СтолбецЗначение
RepoИмя репозитория, определённое из вашей локальной конфигурации (для репозиториев не из конфигурации используется GUID)
SnapshotИдентификатор снимка. Именно его принимает rdc backup restore --at
CreatedВремя в UTC, когда был сделан снимок
TotalРазмер образа репозитория, который представляет этот снимок
AddedБайты, которые этот снимок реально загрузил поверх предыдущих
ChunksСколько ячеек он добавил

Чтобы увидеть, что rdc repo push --to <machine> оставил на месте назначения, спросите у этой машины, что она хранит:

rdc repo list --machine server-1

Отправленная копия появляется под собственным именем. Вторая строка с необработанным GUID рядом с ней – это сохранённая база дельты, благодаря которой следующая отправка на эту машину становится инкрементальной, а не полной передачей.

rdc backup list --machine <machine> читает папки hot/ и cold/, в которые пишут запланированные запуски, поэтому это неподходящий инструмент для копии, оставленной push, и он ничего вам не покажет.

СтолбецЗначение
Modehot или cold. В какой папке запланированной резервной копии находится эта запись
NameИмя репозитория, определённое из вашей локальной конфигурации (для репозиториев не из конфигурации используется GUID)
GUIDGUID репозитория на диске
SizeРазмер файла резервной копии в удобочитаемом виде
ModifiedМетка времени UTC файла на машине

Список бэкендов хранилища упразднён вместе с ветвью rclone; команда отклоняется и называет эти две замены.

Хранение

Сервер применяет политику хранения для каждого репозитория ко всему хранилищу чанков, поэтому старые снимки прореживаются без ручного удаления чего-либо. Если политика не объявлена, сохраняются все снимки.

# Что применяется прямо сейчас.
rdc backup retention my-app

# Сохранять скользящее окно: 7 ежедневных, 4 еженедельных, 6 ежемесячных.
rdc backup retention set my-app --keep-daily 7 --keep-weekly 4 --keep-monthly 6

# Вернуться к сохранению всего.
rdc backup retention clear my-app
ОпцияОписание
--keep-last <n>Сохранить это число самых последних снимков
--keep-hourly <n>Сохранять новейший снимок за каждый из этого числа часов
--keep-daily <n>Сохранять новейший снимок за каждый из этого числа дней
--keep-weekly <n>Сохранять новейший снимок за каждую из этого числа недель
--keep-monthly <n>Сохранять новейший снимок за каждый из этого числа месяцев
--keep-yearly <n>Сохранять новейший снимок за каждый из этого числа лет

Укажите хотя бы одно правило. set без правил отклоняется, а не воспринимается как «не хранить ничего», потому что для очистки политики существует clear.

Восстановление

rdc backup restore превращает резервную копию в живой репозиторий, и это один и тот же глагол для обоих путей. Различается то, на что вы указываете.

# Точка во времени из хранилища чанков.
rdc backup restore my-app --as my-app-yesterday --at <snapshot-id> --up

# Артефакт, оставленный push на машине.
rdc backup restore my-app@server-1 --as my-app --machine server-1 --up

--at принимает идентификатор снимка из rdc backup manifests либо время в формате RFC 3339, например 2026-08-14T12:00:00Z, которое разрешается в самый новый снимок, сделанный в этот момент или раньше. Время, для которого нет ни одного снимка ранее или в этот момент, отклоняется, а не округляется вперёд.

Восстановление под новым именем с помощью --as ничего не перезаписывает, поэтому тренировочное восстановление безопасно запускать на работающей машине. Восстановление под уже существующим именем отклоняется.

ОпцияОписание
<artifact-ref> (позиционный)Что восстанавливать. repo для снимка из хранилища чанков, repo@place для артефакта на машине
--as <name>Имя для восстановленного репозитория (по умолчанию – имя артефакта)
-m, --machine <machine>Машина, на которую восстанавливать
--datastore <name>Восстановить в это именованное хранилище данных, которое хостит подключённая к нему машина
--at <time>Восстановить точку во времени: идентификатор снимка или время RFC 3339
--upРазвернуть восстановленный репозиторий после передачи
--health-window <seconds>Как долго наблюдать за состоянием развёрнутого репозитория
--health-timeout <seconds>Сколько ждать, пока он станет исправным
-y, --yesПропустить подтверждение
--debugВключить подробный вывод

Для восстановления репозитория нужны данные LUKS, которые хранятся в вашей конфигурации. Если у вас включено хранение конфигурации, эти данные возвращаются вместе с конфигурацией на новой машине. Если нет, храните копию конфигурации там, куда не доберётся отказ этой машины.

Проверяйте восстановление на каждой машине

Машина, которая ни разу не прошла полный цикл туда-обратно, не защищена резервным копированием, каким бы гладким ни выглядели её загрузки. Загрузки и восстановления ломаются по разным причинам, и второй вид проблем проявляется только тогда, когда вы действительно пробуете восстановить.

Сделайте это один раз для каждой машины, прежде чем полагаться на резервные копии:

  1. Сделайте снимок: rdc backup snapshot my-app.
  2. Убедитесь, что он записан: rdc backup manifests my-app.
  3. Восстановите его под одноразовым именем: rdc backup restore my-app --as my-app-drill --at <snapshot-id>.
  4. Сравните восстановленный репозиторий с исходным, затем удалите тренировочную копию командой rdc repo delete my-app-drill --yes.

Ничто в этой последовательности не затрагивает живой репозиторий, поэтому её безопасно выполнять на машине, обслуживающей реальный трафик. Если вы переходите со старой схемы резервного копирования, держите её работающей, пока это не пройдёт успешно на данной машине хотя бы один раз. Два пути резервного копирования стоят места в хранилище; один непроверенный путь стоит самих данных.

Синхронизация по одному репозиторию за раз

Push и pull работают с одним репозиторием, адресуемым через ref (name, name:tag или name@machine). Формы «все репозитории сразу» не существует: запускайте команду по одному разу на каждый репозиторий.

Ref, указывающий и на форк, и на машину, работает так же, как и простое имя:

rdc repo push shop:nightly@server-1 --to server-2
rdc repo pull shop:nightly@server-1 --from server-2

Полные списки опций находятся в разделах Отправка резервной копии на другую машину и Получение резервной копии с другой машины.

Запланированные резервные копии

Rediacc использует именованные стратегии резервного копирования. Каждая стратегия определяет расписание, режим резервного копирования, необязательное ограничение пропускной способности и файловые фильтры. Вы привязываете имена стратегий к машинам, чтобы контролировать, какие резервные копии выполняются где.

Режимы резервного копирования

РежимПоведениеПростой
hotОбраз репозитория замораживается, пока сервисы продолжают работать (согласованность на уровне сбоя)Отсутствует
coldСервисы останавливаются, снимок делается, сервисы перезапускаются, снимок загружается (согласованность на уровне приложения)Окно остановки+запуска на репозиторий, распараллеленное между репозиториями. См. «Оценка простоя холодных резервных копий» ниже.

Используйте hot для сервисов, которые допускают снимки, согласованные на уровне сбоя. Используйте cold, когда нужна гарантированная согласованность и можно принять короткий перезапуск.

Семантика холодного резервного копирования

Холодная резервная копия выполняется в три фазы для каждого включённого репозитория: остановка → снимок → запуск. Зная, где заканчиваются гарантии, вы рано заметите частичные сбои.

Что гарантирует холодное резервное копирование:

  • Перед снимком каждый работающий контейнер в каждом включённом репозитории аккуратно останавливается через хук down() в его Rediaccfile, а Docker-демон этого репозитория переводится в состояние покоя. Поэтому снимок согласован не просто на уровне сбоя, а на уровне приложения.
  • Набор ID контейнеров, работавших перед снимком, сохраняется в сайдкаре /var/run/rediacc/cold-backup-<guid>.running.json. Это источник истины о том, «что должно снова работать по завершении».
  • После снимка вызывается хук up() из Rediaccfile репозитория, чтобы восстановить полный compose-стек.
  • Сайдкар статуса для каждого запуска /var/run/rediacc/cold-backup-<guid>.status.json записывает фазу, результат и любую ошибку каждой попытки.

Чего холодное резервное копирование НЕ гарантирует:

  • up() выполняется по принципу «максимум усилий». Он может не сработать по причинам вне контроля холодного резервного копирования (условие depends_on: service_healthy всё ещё в ожидании, синтаксическая ошибка в compose-файле, временный сбой сети при получении образа). Когда это происходит, холодное резервное копирование записывает ошибку на уровне error, пишет сайдкар статуса и переходит к следующему репозиторию.
  • Если up() не срабатывает, включается резервный прямой перезапуск: сайдкар с работающими контейнерами читается, и каждый записанный ID контейнера перезапускается напрямую через Docker API (без compose). Это возвращает сервисы к жизни, даже если в потоке compose есть заминка, хотя без повторного выполнения хуков Rediaccfile.
  • Если даже резервный вариант не срабатывает для некоторых ID контейнеров (например, сам Docker-демон недоступен), сайдкар остаётся на месте, чтобы watchdog маршрутизатора мог продолжать попытки на каждом тике.

Восстановление watchdog: на каждом тике watchdog проверяет наличие работающего сайдкара. Любой перечисленный там ID контейнера, который сейчас остановлен, перезапускается независимо от сохранённой restart_policy контейнера. Это означает, что сервисы с политикой restart: on-failure (которую Docker НЕ стал бы перезапускать после чистой остановки) всё равно возвращаются после холодной резервной копии. Как только все перечисленные контейнеры снова работают, сайдкар удаляется.

Как обнаружить сбои:

  • rdc machine status <machine> --containers показывает состояние выполнения. Сравните с ожидаемым набором.
  • /var/run/rediacc/cold-backup-<guid>.status.json на машине. Проверьте через rdc term connect <repo> -c "cat /var/run/rediacc/cold-backup-$GUID.status.json". success: false с устаревшим startedAt означает, что последняя резервная копия не завершилась корректно.
  • Логи запуска резервного копирования renet (journalctl -u renet-* или прямой вызов rdc backup schedule) выводят итоговую строку вида Cold backup: post-snapshot restart summary total=N compose_ok=N fallback_ok=N failed=N failed_repos=[...]. Непустой failed_repos – цель для grep.

Оценка простоя холодных резервных копий

Каждый репозиторий простаивает только в течение своего собственного окна down() + up(). На прогретом хосте это обычно:

Профиль репозиторияТипичное время остановки+запуска
Небольшой (1-2 контейнера, без БД)5-15 с
Средний (веб-приложение + кеш)20-45 с
Тяжёлый (БД + очереди + почта)60-120 с

Шаг заморозки – это copy-on-write reflink образа репозитория. Это только метаданные, поэтому он занимает одно и то же время, весит ли репозиторий 1 ГБ или 100 ГБ, и в измеренном прогоне не зафиксировался на миллисекундном разрешении. Репозиторий не остаётся остановленным из-за заморозки других репозиториев. Загрузка затем выполняется относительно замороженной копии, пока все репозитории уже снова работают.

Общее время выполнения всего запуска (по настенным часам) определяется тем, сколько репозиториев перезапускается одновременно. renet выводит это значение из хоста:

concurrency = min(repoCount, max(2, NumCPU/2), 8)

Примеры:

ХостРепозиторииПараллелизмВремя перезапуска по настенным часам
ВМ на 4 CPU5 репозиториев, в среднем по 30 с2~75 с
Сервер на 16 CPU10 репозиториев, в среднем по 40 с8~80 с
Узел флота на 64 CPU50 репозиториев, в среднем по 40 с8~4 мин

Переопределение через переменную окружения: установите REDIACC_COLD_BACKUP_CONCURRENCY=N в окружении сервиса резервного копирования (обычно через systemd drop-in), чтобы зафиксировать конкретное значение. =1 заставляет перезапуски идти строго последовательно, что полезно при отладке цикла падений в хуке up() одного репозитория.

Если у вас есть репозиторий, чувствительный к задержкам (публичное веб-приложение, почта), его простой ограничен собственным временем остановки+запуска (обычно 30-90 с), а не длиной всего запуска. Репозитории распределяются по слотам параллелизма в порядке обнаружения; очереди приоритетов нет. Дайте тяжёлым репозиториям собственную стратегию с областью действия --include, если вам нужно более тонкое планирование.

Долго выполняющиеся резервные копии и пересекающиеся расписания

Холодная резервная копия, которая занимает больше времени, чем интервал собственного расписания (например, первый посев репозитория на 500 ГБ на скромном канале может законно занять больше 24 часов, и за это время ночной таймер сработает снова), не ставит в очередь и не запускает второй прогон. Юнит systemd с Type=oneshot – это единственный экземпляр: когда таймер срабатывает, а сервис уже находится в состоянии activating, systemd объединяет запуск с уже существующей задачей. Новый процесс не запускается, никакой прогон не откладывается на потом.

Конкретно, прогон, начавшийся в понедельник в 03:00 UTC и завершившийся в четверг в полдень:

ДеньСрабатывание в 03:00 UTCРезультат
ПонедельникПервое срабатываниеПрогон начинается
ВторникВторое срабатываниеТихо отброшено (предыдущий прогон всё ещё активен)
СредаТретье срабатываниеТихо отброшено (предыдущий прогон всё ещё активен)
ЧетвергПрогон завершается в полденьБез наверстывания; следующий прогон – в пятницу в 03:00 UTC

Директива таймера Persistent=true не спасает эти срабатывания. Persistent=true воспроизводит срабатывания, пропущенные потому, что сам таймер был неактивен (система выключена, таймер отключён). Срабатывания, отброшенные из-за занятости сервиса, теряются безвозвратно.

Такое поведение по умолчанию выбрано намеренно. Параллельный запуск двух холодных резервных копий для одного и того же хранилища данных привёл бы к конфликту на пути заморозки, при загрузке и в сайдкарах для каждого репозитория в /var/run/rediacc/cold-backup-<guid>.status.json. Ждать за уже выполняющимся экземпляром лучше, чем бить по одним и тем же данным с двух сторон. Блокировка хранилища данных обеспечивает это: второй холодный прогон обнаруживает занятую блокировку и отклоняется сразу же, не остановив ничего.

Последствия для мониторинга. Зависшая резервная копия (например, загрузка, застрявшая в сетевой чёрной дыре) тихо отбрасывает все последующие срабатывания таймера. Планировщик не выдаёт никакой тревоги. Следите за systemctl show <unit> -p ActiveEnterTimestamp: если сервис находится в состоянии activating дольше, чем ожидаемая длительность вашего прогона (например, больше 48 часов для ночного таймера), расследуйте.

Если вам нужно, чтобы срабатывало каждое запланированное событие, переключите таймер с OnCalendar=<cron> на OnUnitInactiveSec=<интервал>. Он срабатывает через N часов после завершения предыдущего прогона, а не по фиксированному расписанию настенных часов, поэтому долгие прогоны не приводят к пропускам. Они просто сдвигают следующий прогон позже. Компромисс – дрейф расписания: ваш ночной прогон в 03:00 превращается в «через 24 часа после завершения предыдущего».

Снимки, прерывания и место в пуле

Каждый push работает от мгновенного снимка хранилища данных, поэтому загружаемые данные согласованы, даже пока репозитории продолжают запись. Пока резервное копирование выполняется, этот снимок продолжает ссылаться на каждый блок, который он разделяет с работающими репозиториями: удаления и обрезка освобождают меньше места в пуле, пока цикл не завершится и снимок не будет удалён. Отчёт о состоянии хранилища показывает, сколько места прямо сейчас закреплено за снимками резервного копирования.

Прерывания безопасны. Остановка сервиса (или перезагрузка машины) заставляет резервное копирование прервать передачу и удалить свой снимок перед завершением; следующий запланированный прогон продолжит с того места, где остановился, потому что уже сохранённые ячейки не загружаются повторно. Если процесс убит слишком грубо, чтобы успеть очиститься (потеря питания), осиротевший снимок обнаруживается и автоматически удаляется обслуживающим процессом хранилища в течение нескольких минут.

Определение стратегии

Настройка по умолчанию – это разделение на две стратегии: быстрый ежечасный горячий поток, охватывающий каждый репозиторий, и более медленный еженедельный холодный поток, который переводит контейнеры в состояние покоя ради снимков, согласованных на уровне приложения. Оба пишут в одно и то же хранилище чанков, и общие блоки хранятся один раз, а не по одному на поток.

rdc backup strategy set hourly-hot \
  --destination rediacc \
  --cron "0 * * * *" \
  --mode hot \
  --bwlimit 20M \
  --enable
rdc backup strategy set weekly-cold \
  --destination rediacc \
  --cron "15 3 * * 0" \
  --mode cold \
  --include shop --include mail \
  --enable

--destination <name> называет назначение внутри стратегии; это метка, которую вы сами выбираете, и она описывает хранилище чанков. --include перечисляет репозитории для резервного копирования, и повторение флага добавляет ещё. Если опустить его, стратегия охватывает каждый репозиторий в хранилище данных. Имена должны совпадать с именем репозитория в локальной конфигурации (без :tag).

--exclude отклоняется для назначения в хранилище чанков, а не тихо игнорируется, потому что базовая команда backup snapshot выбирает репозитории по имени и не имеет собственного исключения. Учёт этого флага означал бы резервное копирование репозиториев, которые вы просили оставить в стороне. Вместо этого ограничивайте область стратегии через --include, чтобы то, что покрывает запланированный прогон, было записано явно, а не выводилось по догадке.

ОпцияОписание
<strategy> (позиционный)Имя стратегии (используется для привязки к машине)
--destination <name>Имя назначения внутри стратегии. По умолчанию – хранилище чанков
--storage <name>Явно включить упразднённый тип назначения rclone. Расписание, использующее его, нельзя развернуть
--cron <expression>Выражение cron (например, "0 2 * * *" для ежедневного запуска в 2 часа ночи)
--mode <hot|cold>Режим резервного копирования
--bwlimit <limit>Ограничение пропускной способности для загрузок (например, 10M)
--include <repos>Репозитории, которые охватывает эта стратегия (можно повторять)
--exclude <repos>Репозитории, которые нужно пропустить (можно повторять). Отклоняется для назначения в хранилище чанков
--folder <path>Подпапка внутри бакета rclone. Отклоняется для назначения в хранилище чанков
--enable / --disableВключить или отключить стратегию

Просмотр стратегий

rdc backup strategy list
rdc backup strategy show weekly-cold

Удаление стратегии

rdc backup strategy remove weekly-cold

Привязка стратегий к машине

Стратегия, не привязанная ни к одной машине, никогда не разворачивается. Привяжите одну или несколько к машине:

rdc backup strategy bind hourly-hot --machine hostinger
rdc backup strategy bind weekly-cold --machine hostinger
rdc backup strategy unbind weekly-cold --machine hostinger

Привязка записывается в вашей конфигурации как список на машине, который читает rdc backup schedule, чтобы решить, какие юниты разворачивать:

{
  "machines": {
    "hostinger": {
      "backupStrategies": ["hourly-hot", "weekly-cold"]
    }
  }
}

Привязка существует только в локальной конфигурации. Определение стратегии и привязка её к машине не затрагивает саму машину. Выполните rdc backup schedule -m <machine> (см. Развёртывание расписания на машине), чтобы развернуть таймеры systemd, и повторяйте запуск после любого изменения стратегии или привязки.

Выбор между горячим и холодным режимом и фильтрация по репозиториям

Горячий и холодный режим на взгляд

ГорячийХолодный
СогласованностьНа уровне сбоя (образ замораживается во время работы)На уровне приложения (остановка → заморозка → запуск)
ПростойОтсутствуетОкно остановки+запуска на репозиторий (обычно 5-120 с)
Подходящая частотаВысокая (например, ежечасно)Низкая (например, ежедневно или еженедельно)
Типичное применениеЧастая подстраховкаЗапланированная резервная копия с гарантированной согласованностью

Горячий режим – правильный выбор по умолчанию для высокочастотных прогонов. Сервисы продолжают работать, пока делается снимок, поэтому для ваших приложений нет простоя вообще. Снимок согласован на уровне сбоя: это эквивалентно тому, что вы получили бы после аварийного завершения работы. Для большинства современных баз данных и очередей сообщений этого достаточно.

Холодный режим уместен, когда нужен гарантированно согласованный на уровне приложения снимок и можно принять короткий перезапуск на репозиторий. Сервисы останавливаются перед снимком и перезапускаются перед началом загрузки, поэтому медленная или неудачная загрузка никогда не продлевает окно простоя. Полную модель гарантий см. в разделе Семантика холодного резервного копирования.

Оба режима пишут в одно и то же хранилище чанков. Режим определяет то, как репозиторий обрабатывается, пока образ заморожен, а не то, где оказываются данные. Репозиторий, охваченный и ежечасным горячим, и еженедельным холодным расписанием, хранит общие ячейки один раз, а не дважды.

Определение области репозиториев для стратегии

Стратегия без --include охватывает каждый репозиторий в хранилище данных. Повторение --include сужает её до названных вами репозиториев, сопоставленных по имени репозитория в локальной конфигурации (без :tag).

# Горячая стратегия: резервировать всё ежечасно
rdc backup strategy set hourly-hot \
  --destination rediacc \
  --cron "0 * * * *" \
  --mode hot \
  --bwlimit 6M \
  --enable

# Холодная стратегия: еженедельно, и только репозитории, которым нужен покой
rdc backup strategy set weekly-cold \
  --destination rediacc \
  --cron "15 3 * * 0" \
  --mode cold \
  --include shop --include mail \
  --enable

Когда исключать репозиторий из частой горячей стратегии

Называйте репозитории, которые хотите включить в высокочастотный прогон, вместо того чтобы позволять ему захватывать всё, когда:

  • Репозиторий большой и полностью восстановим из исходных данных, уже присутствующих на томе, так что каждая ежечасная резервная копия тратит пропускную способность, не добавляя реальной ценности для восстановления.
  • Прогон резервного копирования превысит собственный интервал расписания при доступной скорости загрузки.

Пример. Репозиторий analytics-demo содержит примерно 114 ГБ производных таблиц Postgres, которые можно восстановить из исходных дампов CSV, хранящихся на том же томе. При ограничении загрузки 6 МБ/с первый снимок этого репозитория занимает более 5 часов. Если запускать его ежечасно, каждый прогон всё ещё выполняется, когда срабатывает следующий, поэтому все последующие срабатывания тихо отбрасываются (см. Долго выполняющиеся резервные копии и пересекающиеся расписания). Если перечислить остальные репозитории в hourly-hot, а analytics-demo оставить для weekly-cold, он будет резервироваться раз в неделю вместо того, чтобы не резервироваться вообще.

Если данные полностью восстановимы, подумайте, нужно ли вообще делать их резервную копию. Альтернатива – резервировать только исходные входные данные (в этом примере – дампы CSV) и полностью пропускать производную копию. Еженедельная холодная резервная копия исходных данных намного меньше и полностью достаточна для восстановления.

Репозиторий, охваченный обеими стратегиями, получает и ежечасные снимки, согласованные на уровне сбоя, и еженедельный снимок, согласованный на уровне приложения. rdc backup manifests <repo> показывает их вместе, а общие ячейки хранятся один раз.

Операции резервного копирования

Развёртывание расписания на машине

Отправляет привязанные стратегии на машину в виде таймеров systemd:

rdc backup schedule -m server-1
rdc backup schedule -m server-1 --dry-run

Развёртывание – это согласователь состояния. Он читает текущие файлы юнитов и состояние systemd на машине, сравнивает с тем, что произвела бы конфигурация (SHA-256 для каждого файла), и трогает только те юниты, содержимое которых действительно изменилось. Повторный запуск без изменений конфигурации – это no-op: без записи, без daemon-reload, без изменений таймеров.

--dry-run печатает план для каждой стратегии (created, updated (service, timer, env), unchanged, removed), не трогая машину. Сочетайте с --debug, чтобы также напечатать тела сгенерированных юнитов с скрытыми учётными данными. Юнит хранилища чанков вообще не несёт никаких учётных данных: машина аутентифицируется собственной подписанной лицензией репозитория, а сервер возвращает кратковременное разрешение, поэтому в файл юнита ничего чувствительного не записывается.

Если резервное копирование для стратегии, которую вы собираетесь обновить или удалить, выполняется прямо сейчас, развёртывание немедленно завершается неудачей с подсказкой отменить его или передать --force. С --force выполняющийся вызов сохраняет свой юнит в памяти, а новая конфигурация применяется на следующем тике таймера, поэтому выполняющаяся резервная копия никогда не завершается принудительно.

--reset-failed включается опционально. Если передать этот флаг, после успешного развёртывания он очищает состояние failed в systemd для затронутых сервисов. По умолчанию выключен, чтобы прежние сигналы о сбоях оставались видны для оповещений.

Запуск резервного копирования прямо сейчас

Запускает резервное копирование немедленно, не дожидаясь таймера. Работает, даже если ни один таймер не развёрнут, используя systemd-run для разового запуска:

rdc backup run -m server-1
rdc backup run weekly-cold -m server-1

Просмотр статуса резервного копирования

Показывает текущее состояние таймеров резервного копирования и результаты недавних заданий:

rdc backup status -m server-1
rdc backup status hourly-hot -m server-1

Отмена выполняющейся резервной копии

rdc backup cancel -m server-1
rdc backup cancel weekly-cold -m server-1

Миграция репозитория

Перемещение репозитория с одной машины на другую:

rdc repo migrate my-app@server-1 --to server-2
ОпцияОписание
<ref> (позиционный)Ссылка на репозиторий для миграции; его @machine называет источник
--to <place>Целевая машина или кластер
--provision <provider>Автоматически развернуть целевую машину через этого облачного провайдера (например, hetzner, linode)
--checkpointСоздать контрольную точку CRIU перед миграцией, чтобы память процесса тоже переместилась
--delta-base <guid>Неизменный базовый GUID для дельты переключения. По умолчанию – база первой фазы
--strategy <strategy>Стратегия блочной дельты для переключения: auto, physical или shared
--skip-dnsПропустить обновление записей DNS после миграции
--keep-sourceСохранить исходные образы после успешного перемещения
--bwlimit <limit>Ограничение пропускной способности для передачи (например, 50M)

Миграция передаёт зашифрованные данные репозитория через rsync в две фазы: массовую передачу, пока репозиторий продолжает работать, а затем короткую остановку для дельты. Миграция перемещает репозиторий, поэтому исходные образы удаляются после успешного перемещения. Передайте --keep-source, чтобы сохранить их. Это и есть разница между repo migrate и repo push: push оставляет источник работающим и нетронутым.

Чтение архива, записанного до отказа от этого пути

rdc storage – это то, что осталось от ветви rclone, и он доступен только для чтения. Он больше не может быть назначением резервного копирования, но всё ещё может получить доступ к архиву, записанному туда ранее.

# Зарегистрировать удалённое хранилище, уже настроенное для rclone.
rdc storage import rclone.conf
rdc storage list

# Посмотреть, что там есть. Это запускает rclone из вашего PATH.
rdc storage browse my-storage

import читает файл конфигурации rclone и записывает удалённые хранилища в вашу конфигурацию; поддерживаемые типы: S3, B2, Google Drive, OneDrive, Mega, Dropbox, Box, Azure Blob и Swift.

browse требует rclone в вашем PATH. Он запускает rclone, установленный на той машине, где вы набираете команду; встроенной копии больше нет. Без него команда сообщит об этом и не сделает ничего другого.

Отправка, получение, просмотр списка и восстановление бэкенда хранилища упразднены; каждая из этих операций отклоняется и называет команду, которая её заменяет.

Лучшие практики

  • Планируйте ежедневные холодные снимки для копий критически важных данных, согласованных на уровне приложения
  • Используйте горячие снимки для высокочастотных прогонов, где требуется нулевой простой
  • Периодически тестируйте восстановление. rdc backup restore --as <new-name> ничего не перезаписывает, поэтому тренировку безопасно запускать на работающей машине
  • Задайте политику хранения вместо ручного прореживания, чтобы окно, которое вы сохраняете, было записано явно
  • Держите копию с машины на машину в дополнение к снимкам, если хотите иметь копию на оборудовании, которое вы контролируете
  • Храните учётные данные в безопасности; резервные копии зашифрованы, но для восстановления нужны данные LUKS