🪞 madWebMirrorMagick — «магическое зеркало» для сайта Linux
Внимание! Утилита немного недоделана, у нее проблема с переключалкой серверов. Потом исправлю, если руки дойдут.
Программа-демон, которая сама следит за сайтом, делает бэкапы и при падении основного вебсервера автоматически включает его на резервном. Экономит энергию мозга™ — настроил один раз и больше не паришься.
- Health-check локального веба (через
curl
по URL/порту). - Автопереключение на зеркале: remote ⇄ local для nginx на хосте-зеркале.
- Ежедневные бэкапы сайта и БД по расписанию.
- Работает как systemd-сервис и ставит второй watchdog-сервис на зеркало автоматом.
🧰 Предварительные требования (чек-лист)
- 🐧 На обоих серверах (в на шем случае это «198» — основной и «202» — зеркало) установлена операционная система Linux с systemd, веб-сервер nginx или apache2, mysql-сервер.
- 🔑 Есть рабочий доступ по SSH с основного на зеркало (логин и пароль известны).
- 📦 На основном сервере (где запускается наша программа) должны быть установлены пакеты:
sudo apt-get install libssh-dev curl tar gzip pv
- 🗝️ Под рукой должны быть учётные данные:
- Пароль от SSH-пользователя на зеркале (в моем случае это
madmentat
). - Пароль
root
на зеркале (для установки systemd-юнитов). - Пароль sudo на зеркале (если отличается от SSH/Root).
- Пользователь / пароль для базы данных (MySQL/MariaDB).
- Пароль от SSH-пользователя на зеркале (в моем случае это
- ⚙️ Всё остальное — скрипты переключения nginx, systemd-юниты и watchdog — программа устанавливает автоматически, руками ничего создавать не нужно.
📁 Структура исходников проекта
Репозиторий madWebMirrorMagick организован просто: всё, что исполняется, находится в src/
, заголовки — в include/mad/
, а наследованный «легаси»-код подключаем единым файлом из legacy/
. Сборкой управляет Makefile
, итоговый бинарь — madbackuper
.
madWebMirrorMagick/
├─ Makefile
├─ src/
│ ├─ main.cpp # входная точка + адаптер к legacy
│ └─ modules/
│ ├─ core.cpp # конфиг, валидация, утилиты (run_local и пр.)
│ ├─ net.cpp # SSH/SFTP обёртки (libssh), прогресс загрузок
│ ├─ deploy.cpp # построение команд деплоя (nginx/apache, БД, права)
│ └─ daemon.cpp # демонический цикл, systemd install/uninstall,
│ # удалённый watchdog-скрипт и его unit на зеркале
├─ include/
│ └─ mad/
│ ├─ core.hpp # структура Config, константы путей, прототипы
│ ├─ net.hpp # объявления SSH/SFTP функций
│ ├─ deploy.hpp # интерфейсы builder'ов команд деплоя
│ └─ daemon.hpp # API демона (run_daemon_loop, daemon_install,…)
├─ legacy/
│ └─ madbackuper.cpp # старый монолит, подключается из main.cpp
└─ (build) madbackuper # собранный бинарь (после make)
run_daemon_loop
при --daemon
Config
)На машине при установке демона автоматически появляются:
-
/usr/local/bin/madbackuper
— бинарь (основной демон на 198). -
/etc/systemd/system/madbackuper.service
— локальный юнит (198). -
/usr/local/bin/mad_watchdog_198.sh
— скрипт на зеркале (202), пингует 198 и переключает nginx. -
/etc/systemd/system/madbackuper-watchdog.service
— юнит на зеркале (202). -
/etc/madbackuper.conf
— конфигурация (пароли в статье заменяй на XXX).
🔩 Сборка и установка
# собрать
make clean && make
# установить и запустить как демон
sudo ./madWebMirrorMagick --daemon-install
# проверить статусы
systemctl status madbackuper # основной демон на 198
# после инсталла он сам создаст watchdog на 202:
# madbackuper-watchdog.service
В качестве исполняемого файла используется бинарь, устанавливаемый в /usr/local/bin/madbackuper
. Название системного юнита оставлено историческим: madbackuper.service.
🧪 Быстрый тест
- Открой
journalctl -u madbackuper -f
на 198 иjournalctl -u madbackuper-watchdog -f
на 202. - Останови локальный веб на 198 на минутку — увидишь автопереключение 202 → local.
- Верни веб — зеркало вернётся в remote.
⚙️ Конфигурация (/etc/madbackuper.conf
)
Все параметры — простые key=value
. CLI-ключи могут их переопределять.
target_server=nginx # nginx | apache2 (логика переключения сейчас для nginx)
remote_host=192.168.88.202 # Зеркало (куда ставим watchdog)
ssh_port=22
remote_user=madmentat
remote_pass=XXX # SSH-пароль (замените!)
remote_root_pass=XXX # Не обязателен, зарезервировано под будущие сценарии
remote_sudo_pass=XXX # Пароль для sudo на Зеркале; если пусто — пробуем без пароля
local_site_dir=/webserver/madmentat.ru
remote_site_dir=/webserver/madmentat.ru
remote_backup_base=/webserver/.backup
server_name=madmentat.ru # влияет на имя setup-скрипта на Зеркале: /root/setup_<server_name>_nginx.sh
php_version=8.3
php_fpm_sock= # можно оставить пустым, если используем php_version
db_user=madmentat
db_pass=XXX
db_name=mad
proxy_target=192.168.88.198 # Основной хост (198), куда указывает REMOTE в конфигурации nginx на Зеркале
local_http_port=8081 # Локальный порт приложения на 198, когда зеркало в режиме LOCAL
local_https_port=0 # 0 — не используем https локально (иначе укажи порт и ssl_cert/ssl_key)
health_url=http://127.0.0.1:80/ # URL для health-check на 198 (можно оставить 127.0.0.1:80)
health_host_header= # Если нужен Host для проверки (виртуальный хост) — впиши домен
health_interval_sec=60 # Период проверок
switch_to_local=true # Разрешено ли переключать зеркало в LOCAL при падении 198
ssl_cert=
ssl_key=
schedule_hhmm=04:00 # Будущее: время суточного бэкапа (legacy-часть уже унаследована)
🧭 Как это интерпретируется
- Health-check идёт по
health_url
. Если он пуст, берётсяhttp://127.0.0.1:<local_http_port>/
. - Если
health_host_header
задан — добавляем-H "Host: ..."
кcurl
. - На Зеркале (202) устанавливается watchdog-скрипт, который пингует
http://<proxy_target>:<HTTP_PORT>/
и вызывает/root/setup_<server_name>_nginx.sh
с аргументомremote
илиlocal
. - Важный нюанс портов:
health_url
проверяет «внешний» фронт 198 (часто это 80). Для локального режима nginx на 202 проксирует наlocal_http_port
(например, 8081) — это уже внутренняя сцепка.
🖥️ Управление (CLI)
--daemon
— запуск в цикле демона (обычно делает systemd).--daemon-install
— установить локальный сервис и удалённый watchdog.--daemon-uninstall
— удалить локальный сервис и удалённый watchdog.- Параметры конфигурации можно переопределить ключами вида
--health-url=...
,--proxy-target=...
и т. п.
🧪 Примеры «граблей» и как мы их обошли
Тут под "мы" - подразумеваюсь я и нейросетка.
SFTP: «Permission denied» при укладке на /usr/local/bin
Сначала пытались заливать файл сразу в /usr/local/bin
через SFTP — упирались в права. Решение:
- Заливаем на Зеркало во
/tmp
по SFTP; - Дальше перемещаем через
sudo install
с передачей пароля поstdin
:
# было (ошибка прав)
sftp: put watchdog.sh /usr/local/bin/mad_watchdog_198.sh
# стало (работает)
put watchdog.sh /tmp/mad_watchdog_198.sh
printf %s "$SUDOPASS" | sudo -S -p '' install -m 0755 /tmp/mad_watchdog_198.sh /usr/local/bin/mad_watchdog_198.sh
/bin/sh и «local: not in a function»
Watchdog-скрипт писали под bash
, а unit стартовал через /bin/sh
. Исправили шебанг и ExecStart:
#!/bin/sh
local var=1 # <-- bash-синтаксис, sh ругается
Работает
#!/usr/bin/env bash
set -Eeuo pipefail
# ...
# в unit:
# ExecStart=/usr/bin/env bash /usr/local/bin/mad_watchdog_198.sh
sudo требует пароль в non-interactive
Ловили «a password is required». Решение — всегда формируем команду вида:
printf %s "$SUDOPASS" | sudo -S -p '' <команда>
В madWebMirrorMagick это уже встроено: берём remote_sudo_pass
(или remote_pass
, если первое пусто) и прокладываем его в команду.
📁 Что раскладывается на Зеркале автоматически
/usr/local/bin/mad_watchdog_198.sh
— bash-скрипт, пингует 198 и дёргает setup-скрипт./etc/systemd/system/madbackuper-watchdog.service
— unit, запускающий скрипт.
Их не нужно править руками — всё обновляется/удаляется командами --daemon-install
и --daemon-uninstall
.
🧭 Троттлинг логов и диагностика
# основной демон (198)
sudo journalctl -u madbackuper -f
# watchdog на зеркале (202)
sudo journalctl -u madbackuper-watchdog -f
# ручная проверка health-check
curl -v -m 5 http://127.0.0.1:80/ # подставь свой health_url
/root/setup_<server_name>_nginx.sh
или он без +x
. Добавь и проверь вручную:
ssh 202 "ls -l /root/setup_<server_name>_nginx.sh && /root/setup_... remote"
🔐 Безопасность
- Пароли в примерах заменены на XXX. В бою — используйте секрет-хранилище или хотя бы права
600
на конфиг. - Ограничьте SSH доступ к зеркалу по сети/ключам.
- Следите, чтобы watchdog не открывал наружу ничего лишнего — он локальный systemd-сервис.
🧩 Сырцы
Архив можно скачать ЗДЕСЬ.
А это ссылочка на Гитхаб.
🎉 Итог
madWebMirrorMagick — это «поставил и забыл»: следит за сайтом, бэкапит, ловко тумблерит зеркало при проблемах. Как мы и хотели — минимум внимания, максимум спокойствия.