С чем нам пришлось столкнуться при использовании утилиты Csync2
Csync2 — достаточно старая утилита, которая предназначена для синхронизации файлов между серверами. Она позволяет настроить синхронизацию файлов по приоритетам, либо по последней модификации в файле (проверяются не сами изменения в файле, а дата его модификации). Также данная утилита позволяет настроить выполнения какого-либо действия при изменении определённого файла. Например при изменении конфигурации nginx, выполнить перечитывание конфигурации. Мы начали её использовать достаточно давно на небольших проектах (кластер минимум из 3-х серверов) и всё было хорошо до того момента, пока эти маленькие кластера не начали развиваться, а именно:
- Заметно увеличился объем файлов, которые необходимо синхронизировать между серверами.
- Увеличилась интенсивность добавления этих файлов.
Сегодня мы поделимся нашим опытом использования такой утилиты как csync2 и о том, какие проблемы могут возникнуть при ее использовании.
Для наглядности рассмотрим один из проектов, который стоит у нас на обслуживании и на котором используется утилита csync2 версии 2.0–8-g175a01c-4. Данная версия является последней для Debian 9. В примере используется политика разрешения конфликтов auto younger — кто новее, тот и прав.
Ниже представлена схема использования Csync2 на данном проекте:
Тут ничего необычного. Происходит синхронизация файлов внутри каталогов между серверами по принципу какой новее, тот и синхронизируем. То же происходит и с удалением. Если после действия над файлом было его удаление на первом сервере, то он удалится и на втором). По данному принципу всё хорошо работает пока проект сравнительно небольшой, менее 1 000 000 файлов и менее 10G синхронизируемых данных, но после того как проект вырастет, то есть перейдет планку, указанную выше, есть вероятность столкнуться со следующими проблемами:
- После синхронизации, файлу присваивается некорректный владелец.
- Возникновение конфликтов при синхронизации файлов.
Отмечу, что проблема с присвоением некорректного владельца файла после синхронизации наблюдается вне зависимости от размера проекта.
Далее поговорим о каждой обозначенной проблеме более детально.
После синхронизации, файлу присваивается некорректный владелец
Для наглядности рассмотрим следующий пример, возникающий на практике. Допустим, у нас есть файл /var/www/index.php с правами 644 и владельцем www-data на первом сервере, но этого файла нет на втором сервере, а т.к. каталоги /var/www должны быть идентичными, запускается процесс синхронизации данного файла утилитой csync2. После завершения работы утилиты, на втором сервере файл получил корректные права (644), но изменился владелец с www-data на root.
Такая проблема может возникнуть в нескольких случаях:
- У пользователей, которым принадлежит файл, разные UID и GID на разных узлах кластера. Если на первом сервере UID у владельца файла, например, 1000, а на другом пользователя с этим UID не существует, то пользователь с таким же именем на втором сервере не сможет прочитать файл.
- Если во время синхронизации с первого сервера на второй запускается обратный процесс синхронизации на втором сервере, возможен баг, при котором владельцем файла становится root-пользователь.
Server 1
ls -l __page_0855c931430c4324f491ed2cf13401c3
drwxr-xr-x 2 test_user test_user 4096 июн 20 09:59 __page_0855c931430c4324f491ed2cf13401c3
Server 2
ls -l __page_0855c931430c4324f491ed2cf13401c3
drwx------ 2 root root 4096 июн 20 09:59 __page_0855c931430c4324f491ed2cf13401c3
Нами было найдено 2 варианта решения данной проблемы:
- Использование неофициальных патчей. Разработчики данных патчей не дают никаких гарантий и не несут какой-либо ответственности в случае потери данных.
- Отказ от auto younger и перенастройка csync2 таким образом, что один из серверов будет считаться мастером (все файлы будут считаться актуальными именно на мастер сервере), а второй слейвом.
Оба варианта не самые лучшие, т.к. первый ненадёжен, а в случае со вторым, мы теряем достаточно большую часть производительности, поскольку второй сервер простаивает и используется только в случае возникновения проблем на первом. У себя для решения этой проблемы мы выбираем второй вариант из выше предложенных, т.к. он более надежен.
Начали возникать конфликты в синхронизациях
Данная проблема возникает гораздо реже нежели предыдущая, но она также имеет место быть. В нашем случае она наблюдалась только когда происходило частое обновление одного и того же файла на всех серверах. Например, есть файл /var/www/tmp/index.php на всех узлах кластера и он обновляется 1 раз в секунду на каждом узле. При попытке его синхронизировать может возникнуть конфликт, т.к. во время синхронизации данный файл в базе csync2 на узле куда происходит синхронизация, окажется новее, что приведёт к возникновению конфликта, который csync2 не может решить в автоматическом режиме:
[12.02.2020 13:04:18.610567] (16038) INFO: child stderr (buffer size: 96 bytes): Updating /var/www/test.ru/data/index.php on Server-2 ...
[12.02.2020 13:04:18.612108] (16038) INFO: child stderr (buffer size: 103 bytes): Do not auto-resolve conflict: Lost 'younger/older' test.
File stays in dirty state. Try again later...
Для решения данной проблемы мы обычно вручную запускали принудительную синхронизацию, т.е. меняли статус файла в базе данных на одном из серверов с chary на force в зависимости о того, где файл актуальнее. Поскольку с политикой auto younger данная проблема возникает достаточно редко, то мы просто знаем о её наличии и при необходимости решаем в ручном режиме.
Итого, стоило ли использовать csync на тот момент когда он был актуален, или можно было использовать что-то другое?
Что ж, инструмент csync2 достаточно прост и хорошо себя показывает при работе с небольшим объемом данных. Но при росте проекта вы можете столкнуться с описанными выше проблемами и попытаться их решить предложенными способами. Но если есть возможность перейти на синхронную репликацию и использовать распределенные файловые системы, которых сейчас достаточно много и они весьма производительны, то мы бы рекомендовали это сделать.