Как устроено
Что считается успехом
Правила, по которым запуск становится зелёным, и каждый случай, когда Unruin отказывается его так назвать.
Бо́льшая часть этого продукта — один отказ: запуск не становится зелёным оттого, что передача завершилась. На этой странице собраны все правила, стоящие за этим, в том порядке, в котором они применяются, — чтобы вы могли решить, согласны ли вы с ними, прежде чем им довериться.
Три исхода, а не два
Законченный запуск — success, partial или failed. Средний существует
потому, что округление в любую сторону врёт: округлить вверх — ущербная копия
выглядит здоровой; округлить вниз — оператор привыкает не замечать красное.
| Что случилось | Запуск |
|---|---|
| Основная копия не сделалась | failed — «Не получилось» |
| Не дошла обязательная вторая копия | failed — «Не получилось» |
| Основная сделалась, но restic вышел с кодом 3 (часть файлов не прочиталась) | partial — «С оговорками» |
| Не дошла копия по возможности | partial — «С оговорками» |
| Всё чисто | success — «Всё в порядке» |
Обязательное хранилище или по возможности — решается в задании: это и значит
required: true у хранилища. Так вы говорите «вторая площадка желательна,
основная — нет». Записаны в историю обе в любом случае; флаг решает только, каким
цветом будет запуск.
Статус несёт ещё и форму везде, где показан: круг — всё в порядке, ромб — с оговорками, квадрат — не получилось, пунктирное кольцо — ни разу не шло. Так состояние переживает распечатку и человека с дальтонизмом.
Сломанный источник не может стать точкой восстановления
Самая опасная поломка, которая может случиться со средством резервного копирования, — точка восстановления, снятая с обрезанного дампа: файл на месте, размер правдоподобный, толку ноль. Против этого работают три вещи:
- Каждая удалённая команда обёрнута в
bash -o pipefail -c. Если на той стороне конвейер и умирает любое его звено, весь вызов по SSH завершается ненулевым кодом. - Локальный конвейер смотрит на
PIPESTATUS. Источник, вышедший ненулевым, делает запуск неудачным, даже еслиrestic backup --stdinбыл совершенно доволен полученными байтами. mysqldumpобязан нести--single-transaction. Без него дамп — не согласованный момент времени, а согласованность нельзя обнаружить потом, глядя на файл.
Правило, которое стоит запомнить: код возврата источника старше кода возврата restic.
Код 3 у restic получает собственный статус
restic выходит с кодом 3, когда точку восстановления он создал, но часть файлов, которые его просили прочитать, прочитать не смог. Это не успех и не провал: копия есть и разворачивается, и чего-то, что вы просили, в ней нет. Unruin записывает это как «с оговорками» и сохраняет список непрочитанных файлов, чтобы у следующего вопроса — «каких именно?» — был ответ.
Контрольное восстановление
Задание с verify: true не заканчивается вместе с копированием. Дальше Unruin:
- разворачивает последнюю точку восстановления с меткой этого задания — не просто последнюю в хранилище — во временную папку внутри каталога состояния;
- обходит развёрнутое дерево и требует хотя бы один обычный файл ненулевого размера;
- удаляет временную папку.
Не прошёл любой шаг — запуск неудачный, и в подробностях написано
restore-verify failed: ….
Шаг 2 намеренно обходит всё дерево, а не смотрит на верхний уровень. У источника-пути restic разворачивает данные по исходному абсолютному пути, поэтому на верхнем уровне лежит ровно одна папка, а размер папки всегда ненулевой. Проверка верхнего уровня объявила бы пустое восстановление здоровым — то есть пропустила бы ровно ту поломку, ради которой всё это и делается.
Что контрольное восстановление доказывает, а что нет
Доказывает: хранилище открывается теми ключами, которые есть у Unruin, указатель точки восстановления разрешается, и наружу выходят настоящие байты.
Не доказывает, что ваш дамп — корректный SQL, что приложение его примет и что
читаются точки восстановления постарше. Проверяется свежая — та, за которой вы
потянетесь первой. Полная проверка хранилища (restic check --read-data) — другая
и куда более дорогая работа, и Unruin не делает вид, что выполнил её.
Контрольное восстановление идёт только тогда, когда копия пригодна — «в порядке» или «с оговорками». Разворачивать нечего, если копия не сделалась.
Копия, которая тихо перестала доходить
Хранилище «по возможности» делает запуск всего лишь оговорочным, а оговорку раз в неделю легко перестать читать. Поэтому хранилище по возможности, не дошедшее два запуска подряд, поднимает собственное оповещение — «offsite NAS has failed 2 consecutive runs» — один раз, на том запуске, который перешёл черту, а не каждую ночь после.
Обязательным хранилищам это не нужно: они и так красят запуск в красное.
Метки занятости, и та, которую никогда не снимают сами
Внутри оркестратора операции с хранилищем выстроены в очередь по хранилищу, и хранилища всегда занимаются в одном и том же порядке — поэтому два задания, делящие вторую площадку, не могут заклинить друг друга.
Собственные метки restic — отдельная история. Упавший запуск оставляет метку и блокирует следующий (restic возвращает код 11, а Unruin переводит это во фразу с предложением снять метку).
unruin unlock <config> <job>снимает только те метки, которые restic считает брошенными: процесса, который их поставил, больше нет.unruin unlock <config> <job> --remove-allснимает все, включая живые. Существует для случая, когда вы точно знаете, что тот процесс мёртв, а restic этого понять не может.- Метку уборки не снимает автоматически ничто. Уборка переписывает хранилище, и сорвать её метку, пока она действительно идёт, — это способ хранилище уничтожить. Если метка уборки застряла, вам об этом скажут, а решение останется вашим.
Зависшая цель не может держать задание вечно
У каждого запуска есть предел по времени: собственный timeout задания, иначе
общий run_timeout, иначе шесть часов. По истечении запуск обрывается и
записывается как неудачный — вместо того чтобы вечно висеть в состоянии running
и блокировать все будущие запуски этого задания через проверку на наложение.
Запуски, оставшиеся в running после падения или перезапуска, при старте
приводятся в порядок и помечаются брошенными — по той же причине.
Пропущенные и наложившиеся запуски — это политика, а не случайность
Две настройки задания решают, что делает планировщик, когда реальность не совпала с расписанием:
on_missed:skip(по умолчанию) илиrun— машина была выключена, когда пришло время. Догонять или ждать следующего окна?on_overlap:skip(по умолчанию) илиqueue— предыдущий запуск ещё идёт. Пропустить этот или выполнить его следом?
Обе записаны у каждого задания, потому что правильный ответ разный: ночному дампу
базы обычно нужен skip, недельной синхронизации на вторую площадку — run.
Где всё это видно
- Фраза-вердикт панели и блок Требует внимания выстраивают ровно эти состояния, худшее первым, — см. панель.
- Полный журнал каждого запуска сохраняется и читается по запуску.
- Оповещения приходят по заданию на те события, на которые оно подписано
(
success,partial,failed) — блокnotifyв справочнике jobs.yaml.