Когда сайт масштабируется горизонтально и у вас появляется несколько веб-нод, общий каталог с медиа (uploads) быстро превращается в «точку боли». Нужна синхронизация в почти реальном времени, чтобы пользователь, только что загрузивший файл на одну ноду, мог тут же получить его с другой. Классика жанра — lsyncd поверх rsync: мониторим изменения через inotify и дожимаем передачу файлов без ручных костылей и кронов.
Зачем именно lsyncd + rsync
rsync великолепен для начальной и периодической синхронизации, но не отслеживает изменения сам по себе. lsyncd следит за файловой системой с помощью inotify, агрегирует события и вызывает rsync только по делу — при реальных изменениях. В итоге получаем минимальные задержки, снижаем лишний IO и сетевой трафик, а также защищаемся от «штормов» событий за счёт короткой задержки на дебаунс.
Связка хорошо подходит для каталогов вроде uploads, где появляются новые медиафайлы, создаются превью и периодически происходят удаления или перемещения. Это не полноценная распределённая ФС и не блокирующее сетевое хранилище, но для одностороннего потока изменений — идеально.
Архитектура и ограничения
Ключевой момент — модель «один писатель, несколько читателей». То есть только одна нода является источником изменений, остальные получают реплику. Если дать право записи всем, получите гонки, конфликты и непредсказуемость. Обычно делают так: загрузки пишет «мастер»-нода (sticky-сессии или маршрутизация на один backend для POST), а остальные ноды читают уже синхронизированное содержимое.
Важно понимать: это «почти реальное время», а не строгое. Есть небольшая задержка: событие inotify + дебаунс lsyncd + время передачи rsync. В большинстве случаев укладывается в доли секунды — секунды, но пик большого пакета файлов может занять больше.
Для SSH-части и автоматизации ключей может пригодиться разбор Rsync: деплой и бэкап по SSH.

Подготовка окружения
Понадобится lsyncd и rsync на исходном сервере и rsync на принимающем. Доступ по SSH лучше оформить через отдельного системного пользователя с ключами, чтобы ограничить права.
# Debian/Ubuntu
sudo apt-get update
sudo apt-get install -y lsyncd rsync
# AlmaLinux/RHEL/CentOS
sudo dnf install -y lsyncd rsync
Создадим пользователя для синхронизации и ключи:
sudo useradd -m -s /usr/sbin/nologin sync
sudo -u sync ssh-keygen -t ed25519 -N "" -f /home/sync/.ssh/id_ed25519
Добавьте публичный ключ на целевой сервер в authorized_keys пользователя, под которым будет выполняться приём (это может быть sync или, например, www-data, если политика безопасности и владельцы файлов позволяют). Убедитесь, что доступ по SSH работает:
ssh -i /home/sync/.ssh/id_ed25519 sync@10.0.0.12 id
Позаботьтесь о владельцах и правах. Проще всего, когда UID/GID веб-пользователя совпадают на всех нодах, тогда rsync --numeric-ids сохранит владельцев корректно. В альтернативном сценарии используйте современный rsync с --chown=www-data:www-data на целевой стороне.
Настройка лимитов inotify
Большие каталоги медиа быстро упираются в лимиты наблюдений. Поднимем значения, чтобы lsyncd не терял события при пике активности:
# /etc/sysctl.d/99-inotify.conf
fs.inotify.max_user_watches=524288
fs.inotify.max_user_instances=1024
fs.inotify.max_queued_events=65536
sudo sysctl --system
После изменения перезапустить сервис lsyncd будет полезно.
Базовая конфигурация lsyncd
Создадим конфиг /etc/lsyncd/lsyncd.conf.lua. Сценарий: исходная нода синхронизирует /var/www/site.com/uploads на целевую ноду по SSH, аккуратно обрабатывает удаления и защищается от частично записанных файлов.
settings {
logfile = "/var/log/lsyncd/lsyncd.log",
statusFile = "/var/log/lsyncd/lsyncd.status",
statusInterval = 10,
nodaemon = false
}
sync {
default.rsyncssh,
source = "/var/www/site.com/uploads",
host = "10.0.0.12",
targetdir = "/var/www/site.com/uploads",
delay = 5,
maxProcesses = 2,
init = false,
insist = true,
excludeFrom = "/etc/lsyncd/exclude_uploads.txt",
rsync = {
archive = true,
compress = true,
verbose = true,
rsh = "ssh -i /home/sync/.ssh/id_ed25519 -o StrictHostKeyChecking=yes",
_extra = {
"--delete-delay",
"--partial",
"--delay-updates",
"--numeric-ids"
-- при необходимости: "--chown=www-data:www-data"
-- при ограниченном канале: "--bwlimit=20m"
}
}
}
Пояснения:
delay = 5— дебаунс, чтобы сгладить бурст создания миниатюр/превью.--delete-delay— удаления применяются в безопасном режиме, минимизируя окна несогласованности.--partialи--delay-updates— уменьшают риск увидеть на целевой ноде «недописанные» файлы.rsyncпишет во временный файл, затем переименовывает.--numeric-ids— сохраняет владельцев по UID/GID, не полагаясь на имена пользователей.
Фильтры исключений
Создайте файл исключений /etc/lsyncd/exclude_uploads.txt. Имеет смысл отбрасывать временные расширения редакторов, кэш, служебные файлы:
.*.swp
.*.swx
.*.tmp
.*.part
cache/
.thumbs/
Если приложение пишет временные файлы с собственным суффиксом, добавьте и его, чтобы rsync не стартовал преждевременно.
Многоточечная репликация
Хотите расшарить медиа на несколько нод сразу? Просто добавьте ещё один блок sync с другим host, targetdir и, при необходимости, отдельным ключом SSH. lsyncd запустит независимые пайплайны, каждый со своими maxProcesses и очередями.
Сервис и логирование
В большинстве дистрибутивов lsyncd уже поставляется с unit-файлом для systemd. Если нет — создайте /etc/systemd/system/lsyncd.service:
[Unit]
Description=Live Syncing Daemon
After=network-online.target
[Service]
Type=simple
ExecStart=/usr/bin/lsyncd -nodaemon /etc/lsyncd/lsyncd.conf.lua
Restart=always
RestartSec=3
User=root
[Install]
WantedBy=multi-user.target
Запуск и проверка:
sudo systemctl daemon-reload
sudo systemctl enable --now lsyncd
sudo systemctl status lsyncd
sudo tail -f /var/log/lsyncd/lsyncd.log
Для интерактивной отладки удобно поднять lsyncd в не-демонском режиме и с подробным логом:
sudo systemctl stop lsyncd
sudo lsyncd -nodaemon -log all /etc/lsyncd/lsyncd.conf.lua

Начальная полная синхронизация
Перед тем как запускать «живой» режим, сделайте стартовую копию каталогов. Это снизит нагрузку и ускорит последующие дельты:
rsync -aHAXvz --numeric-ids --delete --progress /var/www/site.com/uploads/ sync@10.0.0.12:/var/www/site.com/uploads/
Когда директории выровнены, запускайте lsyncd и следите за логом. Если готовите перенос на новую площадку, посмотрите материал миграция сайта без простоя.
Безопасная запись и задержки
Главная забота при синхронизации медиа — не отдать пользователю «полуфайл». Большинство приложений пишут во временное имя и делают атомарный rename, что хорошо. На уровне rsync мы усиливаем это --delay-updates: копирование идёт во временное имя и финализируется переименованием только после завершения передачи. Параметр delay в lsyncd помогает сгладить бурст генерации множества файлов-превью: лучше передать их одним пакетом и не гонять сотни коротких сеансов.
Тюнинг rsync под медиа
- Сжатие:
compress = trueэкономит трафик на текстовых и некоторых бинарных форматах, но JPEG/PNG/WebP сжимаются плохо. Тестируйте. - Контроль полосы:
--bwlimitпригодится, чтобы не забить канал при бэкенд-задачах. - Хеши: для крупных файлов редактируйте только по необходимости;
rsyncпо умолчанию сравнивает размер и время модификации, что быстрее. --inplace: используйте осторожно. Он обновляет файл на месте, что иногда быстрее для огромных объектов, но повышает риск «окна» неконсистентности. Для медиа обычно лучше оставаться на--delay-updates.
Масштабирование и несколько получателей
Для трёх и более целевых нод добавляйте новые секции sync. Если сеть звездообразная, следите за суммарной нагрузкой на исходную ноду и сеть. При большом трафике стоит поднять maxProcesses или наоборот уменьшить, чтобы избежать лишней конкуренции за диск и CPU.
Топологии:
- Hub-and-spoke: одна мастер-нода пушит на все рабочие.
- Chain: A → B → C. Проще сеть, но увеличивает суммарную задержку и риски накопления ошибок.
- Fanout через несколько источников: лучше избегать многописателей; если неизбежно — пересмотрите архитектуру в сторону общего хранилища или объектов.
Если запускаете несколько приёмников, удобнее управлять ими на VDS с едиными пользователями и ключами SSH.
Отказоустойчивость и восстановление
insist = true заставляет lsyncd упорно пытаться переподключаться при сбоях на старте. Во время работы, если соединение оборвётся, задания будут повторяться. В долгих простоях накопятся очереди, поэтому после восстановления связи следите за логом и метриками диска.
Если каталоги разошлись (например, целевая нода долго была офлайн), безопаснее повторить форсированную rsync --delete перед повторным включением живой синхронизации, чтобы не тянуть «мусорные» расхождения событиями.
Тесты: замеряем задержки
Быстрый способ оценить задержку: создать файл с меткой времени и посмотреть, через сколько он появляется на целевой ноде.
date +%s > /var/www/site.com/uploads/ping.txt
ssh sync@10.0.0.12 stat -c %Y /var/www/site.com/uploads/ping.txt
Разница во времени даст ориентир. Проведите серию испытаний на загруженной системе и при массовом создании миниатюр, чтобы подобрать delay и maxProcesses.
Типичные проблемы и их решения
- Права и владельцы: убедитесь, что веб-процесс может читать/писать, а
rsyncне ломает владельцев. Либо синхронизируйте под тем же пользователем, либо используйте--chown. - SELinux/AppArmor: проверьте профили. Возможно, потребуется разрешить демону доступ к каталогу
uploadsи к SSH-клиенту. - SSH-ключи и known_hosts: заранее «познакомьте» хосты, чтобы не было интерактивных запросов подтверждения ключа.
- inotify overflow: если в
lsyncd.logвидите переполнение очереди, увеличьте лимиты и пересмотритеexcludeдля временных файлов. - Системные ресурсы: следите за IO, особенно на HDD. Пакетная отправка и компрессия иногда спасают, но могут грузить CPU.
- Инициализация: первый запуск с пустой целевой стороной займёт время. Обязательно делайте предварительный
rsync. - Разные часовые пояса и часы: рассинхрон времени увеличивает ложные дельты. Держите NTP в порядке.
Варианты конфигурации
Иногда вместо default.rsyncssh используют default.rsync с rsync-демоном на целевой стороне. Это ускоряет коннект и снижает накладные расходы SSH, но усложняет безопасность. Для большинства веб-проектов SSH-компонент проще и безопаснее.
Если у вас одна нода с приёмом, а чтение медиа происходит с CDN, можно синхронизировать только из мастера в сборочную ноду, а дальше уже поставлять статику из кэша. Важно лишь обеспечить неизменяемость URL и своевременную инвалидацию.
Итоги
lsyncd в паре с rsync — практичный способ держать каталог uploads согласованным между несколькими веб-нодами почти в реальном времени. inotify даёт быстрый триггер, дебаунс режет «шум», а правильные флаги rsync обеспечивают атомарность и аккуратные удаления. Внимание к правам, лимитам и отладочным логам — и связка будет работать годами, не требуя постоянного вмешательства.


