- Less 48.6%
- Rust 33.1%
- Vue 12.9%
- JavaScript 4.7%
- Dockerfile 0.5%
- Other 0.1%
| backend | ||
| deploy/grafana | ||
| frontend | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| config.toml | ||
| Containerfile | ||
| Justfile | ||
| README.md | ||
CloudStack RBD Backup
Внешний сервис резервного копирования для CloudStack VM с дисками в Ceph/RBD.
Зависимости
- CloudStack — auth/catalog/control-plane: логин,
sessionkey, пользователи, проекты, ВМ, тома, stop/start VM. - Ceph/RBD — data-plane для снапшотов, экспортов и rollback.
- KVM/libvirt — только short-lived consistency guard при создании снапшота:
suspend/resumeилиdomfsfreeze/domfsthaw. - MariaDB — jobs, snapshots, exports, schedules, job_events.
Браузер хранит настоящий CloudStack sessionkey. Бекенд не выпускает собственные токены. Прозрачные CloudStack proxy-команды используют пользовательский ключ сессии.
API
Одна ручка:
GET /client/api
POST /client/api
Проксируемые CloudStack-команды включают login, logout, listUsers, listProjects, listVirtualMachines, listVolumes, listStoragePools, listApis, listCapabilities и др. Поведение некоторых проксируемых команд модифицировано, также добавлены локальные команды:
createRbdSnapshotlistRbdSnapshotsdeleteRbdSnapshotcreateRbdSnapshotExportlistRbdSnapshotExportsdownloadRbdSnapshotExportrollbackRbdVolumeToSnapshotlistRbdJobscreateRbdSnapshotSchedulelistRbdSnapshotSchedulesupdateRbdSnapshotScheduledeleteRbdSnapshotSchedule
Основные сценарии
Создание снапшота
createRbdSnapshot volumeid=<volumeid>
Алгоритм:
- Проверка пользовательской CloudStack-сессии.
- Проверка доступа к тому через CloudStack.
- Сохранение backup_jobs=CREATE_SNAPSHOT и rbd_snapshots=CREATING.
- Worker забирает job.
- Если VM Running и есть host/domain:
- suspend/resume или fsfreeze/fsthaw через KVM/libvirt.
- rbd snap create pool/image@snap.
- SUCCESS/FAILED + job_events.
Экспорт снапшота
createRbdSnapshotExport snapshotid=<id> exporttype=FULL|DIFF
FULLвыполняетrbd export pool/image@snap file.raw.DIFFищет предыдущий готоый снапшот этого же тома и выполняетrbd export-diff --from-snap previous pool/image@current file.diff.
Скачивание:
downloadRbdSnapshotExport id=<exportid>&sessionkey=<sessionkey>
Перед выдачей файла backend проверяет доступ пользователя к исходному тому.
In-place rollback / восстановление ВМ из снапшота
rollbackRbdVolumeToSnapshot snapshotid=<id> confirm=true autostopvm=true|false
Если autostopvm=false:
- VM должна быть уже Stopped.
- Worker дополнительно проверяет RBD watchers.
- Safety snapshot: rbd snap create pool/image@pre-rollback-.
- rbd snap rollback pool/image@snap.
Если autostopvm=true:
- CloudStack service API: stopVirtualMachine forced=true.
- Ожидание VM state=Stopped.
- RBD watchers check.
- Safety snapshot.
- rbd snap rollback.
- CloudStack service API: startVirtualMachine.
- Ожидание VM state=Running.
Важно: остановка/запуск при restore выполняются через CloudStack control-plane, не через virsh shutdown/start.
Расписания
Расписание хранится в snapshot_schedules и реально исполняется schedule_worker.
Поддержанные действия:
CREATE_SNAPSHOT— создать снапшот;EXPORT_LAST_SNAPSHOT— экспортировать последний READY-снапшот volume;RESTORE_VM_FROM_SNAPSHOT— in-place rollback к указанному snapshot.
Для описания интервалов поддерживается стандартный cron-синтаксис из 5 полей:
minute hour day-of-month month day-of-week
Поддерживаются *, */N, списки A,B,C и точные числа.
Конфигурация
Бекенд читает TOML из CONFIG_PATH или config.toml.
Переменные окружения:
LISTEN_ADDRпереопределяет[server].listen;CLOUDSTACK_URLпереопределяет[cloudstack].api_url;CLOUDSTACK_USER— логин для выполнения административных действий;CLOUDSTACK_KEY— ключ API для выполнения административных действий;CLOUDSTACK_SECRET— секретный ключ CloudStack;DB_URLпереопределяет[database].url;CONFIG_PATH— путь к toml;FRONTEND_DIR— путь к собранному фронтенду;LOG_DIR— директория файловых логов;RUST_LOG— уровень трассировки Rust, например info или debug.
Ключи CLOUDSTACK_ используются только внутренними воркерами бекенда после пользовательской авторизации операции. Сейчас они нужны для:
stopVirtualMachine/startVirtualMachineприautostopvm=true;- запланированных задач;
- инфраструктурного enrichment, если user API не отдаёт storage/host details.
Пример конфигурации:
[server]
listen = "0.0.0.0:8081"
[cloudstack]
api_url = "http://cloudstack.example/client/api"
timeout_seconds = 30
validate_session_ttl_seconds = 60
[ceph]
cluster = "ceph"
config_path = "/secrets/ceph.conf"
user = "client.cloudstack-rbd-backup"
keyring_path = "/secrets/ceph.client.cloudstack-rbd-backup.keyring"
rbd_bin = "/usr/bin/rbd"
export_dir = "/exports"
[kvm.ssh]
user = "root"
private_key_path = "/secrets/id_ed25519"
known_hosts_path = "/secrets/known_hosts"
connect_timeout_seconds = 10
command_timeout_seconds = 60
[kvm.libvirt]
virsh_bin = "/usr/bin/virsh"
suspend_method = "suspend" # suspend | fsfreeze | domfsfreeze | none
resume_on_failure = true
[database]
url = "mysql://backup:backup@mariadb:3306/cloudstack_rbd_backup"
max_connections = 10
[scheduler]
enabled = true
tick_seconds = 30
worker_concurrency = 2
Логи и наблюдаемость
Логи пишутся одновременно:
stdout/stderr
$LOG_DIR/backend.log.YYYY-MM-DD
В MariaDB пишется timeline jobs в job_events, например:
QUEUED
CLAIMED
KVM_SUSPEND / KVM_FSFREEZE
CEPH_SNAPSHOT_CREATE
RBD_EXPORT
CLOUDSTACK_STOP_VM
RBD_SNAPSHOT_ROLLBACK
CLOUDSTACK_START_VM
SUCCESS / FAILED
Чувствительные параметры (sessionkey, password, apikey, signature, secret) маскируются в request logs.
Container
Containerfile собирает единый контейнер на базе ALT Linux:
Сборка:
podman build -f Containerfile -t cloudstack-rbd-backup:latest .
Пример запуска:
podman run --rm \
-p 8081:8081 \
-e CONFIG_PATH=/app/config.toml \
-e CLOUDSTACK_URL=http://10.0.19.67/client/api \
-e CLOUDSTACK_USER=backup-service \
-e CLOUDSTACK_KEY=... \
-e CLOUDSTACK_SECRET=... \
-e DB_URL=mysql://backup:backup@mariadb:3306/cloudstack_rbd_backup \
-v ./secrets:/secrets:ro \
-v ./exports:/exports:Z \
-v ./logs:/logs:Z \
cloudstack-rbd-backup:latest
Если нужно использовать внешний конфиг:
-v ./config.toml:/app/config.toml:ro
Разработка
just backend-run
just frontend-dev
just check