From 7d711425fdb8d9fd7125100c186fcc88561b822d Mon Sep 17 00:00:00 2001 From: Anton Vakhrushev Date: Fri, 22 May 2026 21:06:16 +0300 Subject: [PATCH] Migration: update steps --- docs/drafts/timeweb.md | 79 ++++++++++++++++++++++++++---------------- 1 file changed, 50 insertions(+), 29 deletions(-) diff --git a/docs/drafts/timeweb.md b/docs/drafts/timeweb.md index d000db3..209e4d7 100644 --- a/docs/drafts/timeweb.md +++ b/docs/drafts/timeweb.md @@ -284,28 +284,33 @@ sudo /usr/local/sbin/backup-all.py 2>&1 | tee /tmp/final-backup.log ### Шаг 2. Остановить все приложения на источнике -Аккуратно остановить контейнеры каждого приложения (через -`docker compose down` от соответствующего пользователя или одним -проходом): +Останавливаем docker-демон целиком — это атомарно гасит все +контейнеры за один вызов, не зависит от текущего списка приложений +и шлёт корректный SIGTERM (с грейс-периодом ~15 сек) каждому, что +функционально эквивалентно `docker compose down` по всем стекам. ```bash inv ssh -for user in caddyproxy authelia netdata miniflux rssbridge wakapi \ - dozzle transcriber wanderer memos gitea outline homepage gramps \ - calibre remembos apprise tuwunel goaccess; do - sudo -iu "$user" bash -c "cd /mnt/applications/$user && docker compose down" -done +sudo systemctl stop docker.service docker.socket +sudo systemctl disable docker.service docker.socket # страховка от автостарта при ребуте +sudo systemctl stop cron # чтобы ночной backup-cron не побежал ``` -(Можно завести вспомогательный плейбук `playbook-shutdown-all.yml`, -если такое будет часто.) +Финальный бэкап (шаг 1) **обязательно** должен пройти до этого +момента — `backup-all.py` запускает скрипты приложений, которые +делают `docker compose exec ... pg_dump ...`; без работающего +daemon это сломается. -Проверить `docker ps`, что пусто. Снять флаги cron на бэкап (чтобы -финальный backup не побежал во время миграции): +`disable` — страховка: если по какой-то причине старая машина +перезагрузится во время rsync (или мы вернёмся на источник для +проверки/отката), docker не поднимется автоматически и сервисы +не начнут писать в данные, которые мы уже считаем «фиксированной +копией». В случае отката — `enable` + `start` обратно. -```bash -sudo systemctl stop cron -``` +Проверить, что `docker ps` сейчас отвечает «daemon not running» +(или вернёт пустой список — зависит от того, как `inv ssh` пройдёт +до/после стопа). Если нужно убедиться, что контейнеры реально +ушли — `ps auxf | grep -E "containerd|docker" | grep -v grep`. ### Шаг 3. Раскатать инфраструктуру на target БЕЗ запуска приложений @@ -323,7 +328,9 @@ uv run ansible-playbook -i timeweb.yml --diff \ Цель — после этого на target есть: - Корректные uid/gid для всех приложений. -- Каталоги `/mnt/applications//{data,config,backups}`. +- Каталоги `/srv/applications//{data,config,backups}` (на + Timeweb дефолт изменён с `/mnt/applications`; см. + [журнал шаг 5](timeweb-migration-log.md)). - Шаблоны `docker-compose.yml` и application-конфиги — отрендерены и лежат на месте. - Docker и сети созданы. @@ -331,34 +338,48 @@ uv run ansible-playbook -i timeweb.yml --diff \ ### Шаг 4. Перенос данных -Два варианта. +Пути меняются: на YC данные лежат в `/mnt/applications/`, на +Timeweb — в `/srv/applications/`. Rsync делает remap сам +(потому что мы указываем источник и приёмник явно). Для трёх +приложений без backup-механизма (`caddyproxy`, `remembos`, +`transcriber`) rsync — **единственный** канал переноса, restic +для них не альтернатива. -**Вариант A — rsync напрямую (быстрее).** С target-машины тянем -данные со старой: +**Вариант A — rsync напрямую (основной путь).** С target-машины +тянем данные со старой: ```bash sudo rsync -aAX --info=progress2 --delete \ --exclude='lost+found' \ major@158.160.46.255:/mnt/applications/ \ - /mnt/applications/ + /srv/applications/ ``` `-aAX` сохраняет ACL/xattrs и uid/gid (численные значения). +Численные uid/gid на target совпадают с источником, потому что +плейбуки на обеих машинах создают пользователей с одинаковыми +явно заданными `app_owner_uid`/`gid`. Каждое приложение можно тянуть отдельно — удобнее наблюдать -прогресс и можно частично пересинхронизировать в случае ошибок. - -**Вариант B — restore из restic.** Если по сети источник недоступен -(например, IP уже закрыли) или хочется проверить, что бэкапы вообще -рабочие — восстанавливаемся из YC S3: +прогресс и можно частично пересинхронизировать в случае ошибок: ```bash -sudo /usr/local/sbin/restic-shell.sh -restic restore latest --target /mnt/applications --path /mnt/applications +sudo rsync -aAX --info=progress2 --delete \ + major@158.160.46.255:/mnt/applications/gitea/ \ + /srv/applications/gitea/ ``` -Рекомендую **A с фолбэком на B**: rsync быстрее и точнее (с -точностью до секунды), restic держим как страховку. +**Вариант B — restore из restic (страховка).** Если по сети +источник недоступен или хочется проверить, что бэкапы вообще +рабочие. Подробный пример (с учётом смены `/mnt` → `/srv`) — в +[журнале миграции, шаг 5](timeweb-migration-log.md). + +Для `caddyproxy`, `remembos`, `transcriber` использовать B +**нельзя** — у них нет архивации, в restic-снапшоте данных просто +нет. Только A. + +Рекомендую **A как основной метод**, B держим как страховку +для приложений, у которых есть восстановимый снапшот. ### Шаг 5. Запуск приложений на target