Сервис: owncloud.4gain.pro (
owncloud/server:10.15swarm-stack на firefly). Backup → annalium weekly Sun 05:00 MSK.
rsync -a --delete (incremental: после первого full последующие run'ы передают только diff).owncloud_own_mysql.1.<task-id>, task-id меняется при restart. Скрипт находит через docker ps -qf 'name=owncloud_own_mysql' | head -1.backup-owncloud.timer запускает /usr/local/sbin/backup-owncloud.sh Sun 05:00 MSK (после gitlab 04:00). Раскатывается ансибл-плейбуком service-owncloud-backup.yml.
Структура на annalium /mnt/storage/firefly-backup/owncloud/:
db/
├── db_owncloud-2026-05-03-0500.sql.gz ← 30-дневная история DB
├── db_owncloud-2026-05-10-0500.sql.gz
└── ...
files/ ← single mirror (текущий state)
├── 4gain.pro/
├── tatarnikov.org/
└── nc13.ru/
Retention:
rsync --delete синхронизирует один-в-один). Если нужна history с дельтой — переключить на rsync --link-dest=<previous> (snapshot per backup).cd ansible/playbooks
ansible-playbook -c ssh service-owncloud-backup.yml -e sm2c_backup_owncloud_run_now=true
# Прямо на firefly:
ssh -p 21150 root@firefly.e1.4gain.pro 'systemctl start backup-owncloud.service'
journalctl -u backup-owncloud -f
⚠ Первый запуск — rsync 684 GB, занимает часы. Последующие запуски — десятки минут (только diff).
ssh -p 21150 root@firefly.e1.4gain.pro
MYSQL_CID=$(docker ps -qf 'name=owncloud_own_mysql' | head -1)
DATE=$(date +%F-%H%M)
docker exec $MYSQL_CID mysqldump \
--single-transaction --quick --routines \
-u u_owncloud -p'9fUvH9gp4RWDTbm' db_owncloud \
| gzip -1 \
| ssh root@10.19.1.13 \
"cat > /mnt/storage/firefly-backup/owncloud/db/manual-$DATE.sql.gz"
ssh root@10.19.1.13 'ls -lh /mnt/storage/firefly-backup/owncloud/db/ | tail -3
du -sh /mnt/storage/firefly-backup/owncloud/files/'
Предусловия:
owncloud.4gain.pro (+ multi-host: owncloud.tatarnikov.org, owncloud.nc13.ru) на public IP.10.15) — DB schema связана.Шаги:
# 1. Получить data с annalium на firefly (rsync, не скрипт-back)
ssh -p 21150 root@firefly.e1.4gain.pro
mkdir -p /mnt/swarm/owncloud_data/{files,mysql,redis,patch}
rsync -a --info=progress2 \
root@10.19.1.13:/mnt/storage/firefly-backup/owncloud/files/ \
/mnt/swarm/owncloud_data/files/
# 2. Поднять стек через ансибл-роль (mysql начнёт пустой)
exit
cd ansible/playbooks
ansible-playbook -c ssh service-owncloud-bootstrap.yml
# Подождать пока mysql инициализируется (CREATE DATABASE db_owncloud).
# 3. Restore DB-dump
ssh -p 21150 root@firefly.e1.4gain.pro
LATEST_DUMP=$(ssh root@10.19.1.13 'ls -1t /mnt/storage/firefly-backup/owncloud/db/db_owncloud-*.sql.gz | head -1')
ssh root@10.19.1.13 "cat $LATEST_DUMP" \
| gunzip \
| docker exec -i $(docker ps -qf 'name=owncloud_own_mysql' | head -1) \
mysql -u u_owncloud -p'9fUvH9gp4RWDTbm' db_owncloud
# 4. Очистить redis cache (старая cache из restored mysql state может конфликтовать)
docker exec $(docker ps -qf 'name=owncloud_own_redis' | head -1) redis-cli FLUSHALL
# 5. Reindex (по желанию — поможет если file-tree рассинхрон):
docker exec $(docker ps -qf 'name=owncloud_owncloud' | head -1) \
occ files:scan --all
# 6. Проверка
curl -kI https://owncloud.4gain.pro/
ssh -p 21150 root@firefly.e1.4gain.pro
# 1. Скачать конкретный dump (например 7 дней назад)
ssh root@10.19.1.13 "cat /mnt/storage/firefly-backup/owncloud/db/db_owncloud-2026-04-26-0500.sql.gz" \
> /tmp/owncloud-restore.sql.gz
# 2. Stop owncloud-сервис чтобы не было конкурентных писем
docker service scale owncloud_owncloud=0
# 3. DROP + recreate DB, restore
MYSQL_CID=$(docker ps -qf 'name=owncloud_own_mysql' | head -1)
docker exec -i $MYSQL_CID mysql -u root -p'<root-pass>' << 'SQL'
DROP DATABASE db_owncloud;
CREATE DATABASE db_owncloud CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
GRANT ALL ON db_owncloud.* TO 'u_owncloud'@'%';
SQL
gunzip < /tmp/owncloud-restore.sql.gz \
| docker exec -i $MYSQL_CID mysql -u u_owncloud -p'9fUvH9gp4RWDTbm' db_owncloud
# 4. Очистить redis cache
docker exec $(docker ps -qf 'name=owncloud_own_redis' | head -1) redis-cli FLUSHALL
# 5. Поднять обратно
docker service scale owncloud_owncloud=1
sleep 15
docker exec $(docker ps -qf 'name=owncloud_owncloud' | head -1) occ files:scan --all
OwnCloud хранит уникально-зашифрованные blob'ы по <owner>/files/<path>. Достать конкретный файл:
# 1. Найти путь в file-tree (через DB-dump или прямо на mounted source)
ssh root@10.19.1.13
ls /mnt/storage/firefly-backup/owncloud/files/<owner>/files/<sub-path>/
# 2. Скопировать обратно на firefly
scp /mnt/storage/firefly-backup/owncloud/files/<owner>/files/<path> \
root@firefly:/mnt/swarm/owncloud_data/files/<owner>/files/<path>
# 3. Reindex чтобы DB подхватила новый файл
docker exec $(docker ps -qf 'name=owncloud_owncloud' | head -1) \
occ files:scan <owner>
sm2c-cloud).owncloud.4gain.pro (+ multi-host) → новый public IP.service-owncloud-bootstrap.yml — поднимет чистый стек.| Симптом | Причина | Решение |
|---|---|---|
| После restore часть файлов "missing on disk" | DB старше files (rsync был после mysqldump в этом запуске; либо backup interrupted в середине) | occ files:scan --all — перерезайдинг, добавит существующие, удалит запись о пропавших |
MySQL ERROR 2002 (HY000): Can't connect ... при restore |
mysql container ещё инициализируется после первого старта | sleep 30, потом retry |
mysqldump: Got error: 1044: Access denied for user 'u_owncloud'@'%' to database 'information_schema' |
mysqldump default-includes information_schema | Скрипт уже передаёт конкретную БД (db_owncloud), эта ошибка не должна появляться |
Rsync падает на permission denied к <owner>/files/<dir> |
Files создавались внутри container'а как uid 33; root host может не иметь права read | Запускать backup-script под root (default; User=root в systemd unit) |
| Multi-host (owncloud.tatarnikov.org / nc13.ru) не отвечает | DNS не поправили / traefik labels не обновились | Проверить traefik labels — Host(\owncloud.4gain.pro`) |
| Backup-tar files-rsync долго / тормозит сеть | 684 GB через 1 Gbps LAN — теоретический минимум 90 мин в первый раз | Запускать в нерабочее время. Последующие incremental занимают минуты |
rsync --link-dest=<prev> + ротация. Но disk usage на annalium значительно вырастет.s3cmd / rclone. Files слишком большие — обычно не выгружают.Changelog