Snapshot, backup чи clone VPS: що обрати
Snapshot зберігається у провайдера, тому не є backup. Дізнайтеся, що відновлює кожен варіант і що виправити в cloned VPS перед запуском.
Що насправді таке snapshot, backup і clone
Snapshot VPS — це образ диска вашого сервера, який провайдер зберігає у своїй інфраструктурі у вашому обліковому записі. Backup — це незалежна копія ваших даних, яку можна відновити в іншому місці без допомоги провайдера, у якого зберігався оригінал. Clone — це новий інстанс, розгорнутий зі snapshot. Тому спочатку він є точною копією оригіналу, включно з його ідентичністю.
Вони вирішують різні завдання. Snapshot дає змогу за кілька хвилин скасувати невдале оновлення, але не допоможе, якщо обліковий запис закрито. Backup зберігається навіть у разі припинення роботи провайдера. Його відновлення триває довше, оскільки спочатку потрібно повторно створити машину. Clone за один крок створює другий робочий сервер. Водночас ви отримуєте дві машини, які вважають себе тією самою машиною.
Чому snapshot VPS не є backup
Проблема полягає не в якості образу, а в домені відмови. Snapshot зберігається на платформі сховища вашого провайдера, зазвичай у тому самому регіоні, де розташований сервер, і завжди в тому самому обліковому записі. Одна подія може одночасно вивести з ладу сервер і його snapshot.
- Обліковий запис призупинено, платіж не проходить або хтось викрадає облікові дані для входу.
- Користувач або скрипт із доступом до API видаляє інстанс. У багатьох провайдерів видалення інстансу також видаляє його snapshots. Перш ніж робити інші висновки, ознайомтеся з документованою поведінкою вашого провайдера.
- У регіоні виникає серйозний збій, і все в ньому одночасно стає недоступним.
- Процес, що працює від імені root на сервері, знаходить токен API провайдера, залишений у
/root, і видаляє snapshots, перш ніж перейде до диска.
Backup — це копія, яка переживає всі чотири сценарії. Перевірка складається з одного запитання: якщо обліковий запис у вашого провайдера припинив би існувати сьогодні вдень, що ви все ще могли б відновити і куди саме? Усе, що не проходить цю перевірку, є інструментом відкату. Продовжуйте створювати snapshots, оскільки ніщо не відновлюється швидше. Але додатково зберігайте другу копію у сховищі, яке не контролює ваш провайдер.
Старе правило досі актуальне: три копії даних, на двох типах сховищ, одна з них — за межами платформи. Snapshot провайдера разом із backup-репозиторієм restic на окремій інфраструктурі забезпечує це за допомогою двох компонентів.
Чому snapshot запущеної бази даних може відновитися з помилками
Snapshot від провайдера копіює блоковий пристрій у тому стані, у якому він перебуває в певний момент. Він не просить застосунки спочатку зупинитися і не бачить дані, які ще залишаються в page cache. Тому в найкращому разі образ є узгодженим після збою. Він точно відповідає стану диска, який був би після раптового вимкнення живлення.
Більшість стеку коректно обробляє таку ситуацію. ext4 і XFS відтворюють свій журнал під час монтування, тому файлова система запускається. PostgreSQL під час запуску відтворює свій write-ahead log, про що повідомляє в журналі:
LOG: database system was not properly shut down; automatic recovery in progressInnoDB робить те саме та виводить власні повідомлення про crash recovery під час запуску. Це штатний процес відновлення бази даних. Тому snapshot одного тому зі спокійною базою PostgreSQL або MySQL зазвичай відновлюється без проблем.
Є випадки, коли узгодженості після збою недостатньо. Саме вони спричиняють найскладніші проблеми. Якщо дані розподілені між двома томами, root-диск і окремий диск із даними snapshot-яться в різні моменти. Через це файли даних і каталог журналів можуть не відповідати один одному, а відновленню не буде що коректно відтворювати. Будь-який файл, який застосунок записує без виклику fsync, наприклад файл неповністю отриманого завантаження або файл черги, може відновитися обрізаним. Дані, які застосунок зберігає в пам’яті та скидає на диск за таймером, просто не потраплять до образу.
Тому перед створенням snapshot запишіть dump на диск. Тоді образ міститиме один файл, про внутрішню узгодженість якого ви знаєте, незалежно від стану поточних файлів даних.
sudo -u postgres pg_dumpall --clean --file=/var/backups/pg-$(date +%F).sql
sudo mysqldump --single-transaction --routines --all-databases > /var/backups/mysql-$(date +%F).sql--single-transaction створює узгоджений dump таблиць InnoDB без блокування запису, оскільки dump виконується в межах однієї транзакції з рівнем ізоляції repeatable-read. Він не охоплює таблиці MyISAM, для яких потрібне блокування або зупинений сервер. Перед тим як довіряти dump, перевірте, що він не порожній і не обрізаний: tail -n 1 /var/backups/mysql-$(date +%F).sql у повному виведенні mysqldump завершується коментарем Dump completed.
Якщо у вас є окремий том із даними, його можна заморозити на кілька секунд, потрібних для створення snapshot:
sudo fsfreeze -f /srv
# take the snapshot from your provider's panel or API
sudo fsfreeze -u /srvЗаморожуйте лише том із даними. Ніколи не заморожуйте /. Заморожена коренева файлова система блокує всі записи на сервері, зокрема записи shell, у якому ви мали б ввести команду розморожування. У результаті ви втратите доступ до системи й будете змушені чекати на апаратне перезавантаження.
Поза межами сервера: restic або Borg
Знімок — це швидка частина резервного копіювання. Копія поза сервером — це частина, яка зберігається навіть у разі проблем із вашим провайдером. restic — хороший варіант за замовчуванням, оскільки він усуває дублікати, шифрує дані на стороні клієнта та записує їх у S3-сумісне об’єктне сховище, через SFTP або у звичайний каталог. Storage VPS як ціль для копії поза сервером добре підходить для цього, оскільки репозиторіям резервних копій потрібен обсяг, а не IOPS.
sudo apt update && sudo apt install -y restic
sudo sh -c 'umask 077; head -c 24 /dev/urandom | base64 > /root/.restic-pass'
sudo chmod 600 /root/.restic-pass
sudo cat /root/.restic-passСкопіюйте цю парольну фразу до менеджера паролів зараз, використовуючи пристрій, який не є цим сервером. Репозиторій restic неможливо відкрити без неї, і механізму відновлення немає. Якщо єдина копія пароля залишилася на щойно втраченому сервері, резервна копія перетворюється на зашифровані дані без можливості використання.
export RESTIC_REPOSITORY="s3:https://s3.example.com/vps-backups"
export RESTIC_PASSWORD_FILE=/root/.restic-pass
export AWS_ACCESS_KEY_ID="..."
export AWS_SECRET_ACCESS_KEY="..."
sudo -E restic init
sudo -E restic backup /etc /srv /var/backups
sudo -E restic snapshotssudo -E зберігає ці змінні, оскільки без нього root отримує чисте середовище, а restic повідомляє, що розташування репозиторію не вказано. restic snapshots має показати щойно виконаний запуск, а також його host і paths. Регулярно перевіряйте сам репозиторій і зчитуйте з нього частину даних, а не лише перевіряйте його структуру:
sudo -E restic check --read-data-subset=5%
sudo -E restic restore latest --target /tmp/restore-check
sudo -E restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --pruneНеперевірена резервна копія — це лише припущення. Відновіть її на іншому VPS щонайменше один раз, виміряйте тривалість операції та запишіть цей час, оскільки саме це значення є вашою реальною ціллю відновлення. Borg — інший надійний варіант. Він зберігає репозиторій через SSH, а не в об’єктному сховищі; компромісні рішення описано в порівнянні restic і BorgBackup.
Що потрібно виправити, перш ніж клонований VPS наблизиться до production
Клон є точною копією. У цьому його перевага і проблема. Усе, що робило оригінал унікальним, дублюється, а дублікати конфліктують.
Повторно згенеруйте ключі SSH host. Клон містить файли /etc/ssh/ssh_host_* оригіналу, тому два сервери представляють однакову ідентичність host. Той, хто контролює один сервер, може видати себе за інший для кожного клієнта, який прийняв цей ключ. SSH не показує попередження, оскільки ключ відповідає тому, якого очікував клієнт.
sudo rm -f /etc/ssh/ssh_host_*
sudo ssh-keygen -A
sudo systemctl restart ssh
ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pubssh-keygen -A записує новий ключ кожного типу, якого очікує daemon. Fingerprint з останньої команди має відрізнятися від fingerprint на оригіналі. Поточна сесія не перерветься, оскільки перезапуск sshd не закриває встановлені з’єднання. Зробіть це до того, як хтось підключиться до клона. Якщо відкласти це на потім, кожен клієнт, який уже довіряв успадкованому ключу, отримує WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! і спочатку має виконати ssh-keygen -R <host>.
Скиньте machine ID. /etc/machine-id — це унікальний ідентифікатор, який systemd генерує один раз під час першого завантаження, а клон його успадковує.
sudo truncate -s 0 /etc/machine-id
sudo rm -f /var/lib/dbus/machine-id
sudo ln -s /etc/machine-id /var/lib/dbus/machine-id
sudo rebootПорожній /etc/machine-id вказує systemd згенерувати нове значення під час наступного завантаження. Саме тому файл потрібно обрізати, а не видаляти. Якщо цей ідентифікатор дублюється, виникають дві проблеми. В образах, які отримують адресу через DHCP, systemd-networkd за замовчуванням формує DHCP client identifier на основі machine ID. Тому обидва клони запитують lease як один і той самий клієнт, а сервер надає їм однакову адресу. Крім того, journald додає machine ID до кожного запису, тому центральний log collector зберігає обидва сервери як один. Після перезавантаження виконайте cat /etc/machine-id і переконайтеся, що значення змінилося.
Змініть hostname.
sudo hostnamectl set-hostname web-02
grep 127.0.1.1 /etc/hostshostnamectl записує /etc/hostname і негайно застосовує це ім’я. Команда не змінює /etc/hosts, тому відредагуйте рядок 127.0.1.1 відповідно. Якщо цього не зробити, нове ім’я ніде не розпізнається, тому кожен виклик sudo чекатиме на невдале розпізнавання і виведе sudo: unable to resolve host web-02: Name or service not known.
Замініть усі облікові дані, вбудовані в образ. Клон містить секрети оригіналу, і тепер дві машини можуть діяти від його імені. Перевірте SSH authorized_keys файли, токени API провайдера та DNS, файли .env застосунків, паролі баз даних, приватні TLS-ключі, токени реєстрації в системі моніторингу та пароль репозиторію restic. Ця команда знаходить більшість із них:
sudo grep -rIlE 'PASSWORD|SECRET|TOKEN|API_KEY' /etc /srv /opt /home 2>/dev/null
sudo find / -name '.env' -not -path '/proc/*' -not -path '/sys/*' 2>/dev/nullЯкщо клон є тестовою копією, яка ніколи не обслуговуватиме network traffic, відкличте облікові дані замість їх заміни. Staging-сервер із чинним production API-токеном — це production-сервер із гіршим оновленням.
Вимкніть jobs, які тепер запускаються двічі. Два сервери з однаковим crontab звертаються до тих самих зовнішніх систем в одну й ту саму хвилину.
systemctl list-timers --all
sudo crontab -l
sudo ls -l /etc/cron.d /etc/cron.dailyВипадок із restic варто розглянути окремо, оскільки він порушує retention, а не просто завершується очевидною помилкою. restic додає до кожного snapshot hostname, а restic forget --keep-daily 7 застосовує політику окремо для кожного host. Дві машини з однаковим hostname вважаються одним host, тому всі сім "daily" snapshots можуть походити від клона, а snapshots оригіналу буде видалено під час pruning. Виправте hostname до першого запуску backup або зупиніть timer на клоні. Випадок із certbot простіший: два сервери, які поновлюють ті самі імена, досягають duplicate certificate rate limit центру сертифікації, а програшний запуск завершується помилкою про те, що для точно такого набору імен уже випущено забагато сертифікатів. Клон, домен якого досі вказує на оригінал, у будь-якому разі не пройде HTTP challenge, тому вимкніть поновлення на ньому.
Налаштуйте monitoring agent. Більшість agent-ів визначають себе за hostname або за ID-файлом, записаним під час інсталяції. Тому два agent-и звітують як один host і змішують свої метрики в одному series. На графіках CPU з’являються значення, яких не створювала жодна окрема машина, а alerts починають спрацьовувати нестабільно. Зупиніть і видаліть agent на клоні або повторно зареєструйте його під новим hostname за процедурою, документованою вашим vendor.
Перевірте network configuration на наявність адреси оригіналу. Якщо образ містить статичну адресу в netplan, клон заявляє IP, який належить іншій машині.
ip -br addr
sudo grep -r addresses /etc/netplan/Очистьте стан cloud-init, якщо цей клон стане template.
sudo cloud-init clean --logsЦе видаляє стан cloud-init у /var/lib/cloud, тому під час наступного завантаження модулі першого запуску виконуються знову. Серед них є генерація SSH host keys, якщо ключі відсутні. Деякі версії також підтримують flag для скидання machine ID. Виконайте cloud-init clean --help на власному образі, щоб перевірити підтримувані ним можливості, замість того щоб покладатися на список flags з іншого джерела.
Коли що використовувати
Відкат ризикованого оновлення: створіть snapshot. Створіть його за кілька хвилин до зміни, виконайте оновлення та відновіть образ, якщо щось піде не так. Відновлення скасовує всі записи, виконані після створення snapshot, тому на сервері, який обробляє робочий трафік, спочатку створіть дамп бази даних і чітко визначте, які дані за відповідний проміжок часу буде втрачено. Для do-release-upgrade на сервері, який можна вимкнути на десять хвилин, snapshot — це весь план.
Міграція на більший план: розгорніть clone. Створіть clone зі snapshot у межах більшого плану, перевірте всі пункти наведеного вище списку ідентичності, а потім протестуйте його на власній IP-адресі до перенесення будь-якого трафіку. Зменште DNS TTL за день до перенесення, щоб cutover відбувся швидко, і залиште початковий сервер працювати, доки новий сервер не обробить реальний трафік. Спочатку переконайтеся, що більший план справді швидший для вашого робочого навантаження, використовуючи однаковий метод бенчмаркінгу на обох серверах, оскільки більша кількість vCPU на більш завантаженому обладнанні не завжди означає покращення.
Створення шаблону: створіть snapshot очищеної машини. Встановіть і захистіть один сервер, а потім видаліть усе унікальне для цього сервера перед створенням образу. Не залишайте host keys, очистіть machine ID, видаліть персональний authorized_keys і облікові дані, очистіть cloud-init. Створіть snapshot. Кожен екземпляр, розгорнутий із нього, під час першого запуску створює власну ідентичність, тому наведений вище список перевірок більше не потрібен. Поєднайте це зі стандартними першими десятьма хвилинами на новому VPS, щоб шаблон уже містив роботу, яку інакше довелося б повторювати.
FAQ
Чи є snapshot VPS резервною копією?
Ні, оскільки він належить до того самого домену відмов, що й сервер, з якого його створено. Snapshot зберігається в storage вашого провайдера, у вашому обліковому записі, зазвичай у тому самому регіоні. Блокування облікового запису, викрадений API key або випадкове видалення instance можуть одним кроком видалити сервер і його snapshots. У багатьох провайдерів видалення instance за задумом також видаляє його snapshots. Snapshot — це найшвидший доступний вам спосіб виконати rollback, тому створюйте їх і надалі. Окремо зберігайте другу зашифровану копію на інфраструктурі, якою ваш провайдер не керує.
Чи потрібно зупиняти базу даних перед створенням snapshot?
Не завжди, але потрібно розуміти, який результат ви отримаєте. Snapshot провайдера є crash-consistent. Це означає, що образ відповідає стану диска після раптового вимкнення живлення. PostgreSQL та InnoDB відновлюються з такого стану під час запуску, а PostgreSQL записує database system was not properly shut down; automatic recovery in progress у журнал. Відновлення не гарантується, якщо дані розміщені на двох томах, для яких snapshot створено в різні моменти, або якщо застосунок записує дані без fsync. Спочатку виконайте pg_dumpall або mysqldump --single-transaction на диску, щоб образ містив один файл, узгодженість якого вам відома.
Чому два клоновані сервери конфліктують через одну IP-адресу?
Тому що вони мають спільний /etc/machine-id. В образах, які використовують DHCP, systemd-networkd за замовчуванням формує ідентифікатор DHCP-клієнта на основі machine ID. Тому обидва клони запитують lease як один і той самий клієнт, а DHCP-сервер призначає їм одну адресу. Обріжте /etc/machine-id до нульової довжини, видаліть /var/lib/dbus/machine-id, створіть символічне посилання на /etc/machine-id і перезавантажте систему, щоб systemd згенерував нове значення. Інша поширена причина — статична адреса, записана в /etc/netplan/, яку клон скопіював без змін. Перевірте це за допомогою ip -br addr.
Як найшвидше перевірити, що клон безпечно додавати у production?
Порівняйте з оригіналом чотири параметри. Виконайте ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub на обох серверах і переконайтеся, що fingerprints відрізняються. Виконайте cat /etc/machine-id на обох серверах і переконайтеся, що значення відрізняються. Потім виконайте hostnamectl status і переконайтеся, що ім’я нове та коректно розпізнається, щоб sudo не видавав попередження. Після цього виконайте systemctl list-timers --all і зупиніть кожен timer, який взаємодіє зі спільною системою, наприклад резервне копіювання, поновлення сертифікатів або monitoring agent, доки не визначите, яка машина відповідає за це завдання.