У меня есть кое-что, что нужно сделать на 50+ серверах. Первым шагом является извлечение обновленной версии некоторого исходного кода в общий каталог (предположим, что у всех установлен общий диск). Второй - выполнить некоторую работу на каждом из серверов.
Я бы предпочел, чтобы эти два сценария работали на каждом из серверов. Все 50+ серверов клонированы с одного образа диска, и мне нецелесообразно настраивать какой-либо из них.
Когда 50 серверов запускают первый сценарий, я хочу, чтобы только первый, который пытается его запустить, действительно запускал его. Остальные я просто хочу выйти. Сервер, который фактически запускает сценарий, должен затем обновить общий каталог, а затем выйти. Затем, позже, второй сценарий будет запущен и выполнит работу на всех серверах на основе обновленного кода, полученного первым сервером.
Как лучше всего это сделать? Могу ли я надежно запустить первый скрипт на одном сервере и создать файл или что-то, что действует как «семафор» или «блокировка» какого-то рода, удерживая другие серверы подальше?
Это усложняется тем, что я думаю о том, чтобы сценарии запускались из идентичных файлов cron на каждом из серверов, то есть все сценарии могли бы попытаться запустить его одновременно, если все их часы установлены одинаково.
Я надеюсь, что они будут запускаться из сценариев bash. Имеет ли это смысл как подход?
РЕДАКТИРОВАТЬ: обновлено на основе вопросов:
Мы не хотим, чтобы каждый сервер пытался проверить свою собственную копию этих файлов - они находятся в репозитории исходного кода размером в несколько ГБ, и иметь 50+ одновременных проверок этого кода было бы сложно для нашего сервера управления версиями (и не масштабируемого до 100+ серверов).
Добавление cronjob к более чем 50 серверам - не такая уж большая проблема, но добавить еще один настроенный сервер с его собственной конфигурацией сложнее. Мы уже клонируем 50 серверов - поддержание отдельного сервера только для проверки последнего исходного кода для более чем 50 серверов кажется расточительным и добавит больше накладных расходов, чем просто добавление скрипта на наши текущие серверы.
Три решения.
У каждого из них есть компромиссы, но вы не совсем ясно дали понять, почему вы хотите спроектировать решение именно таким образом.
Могу я предложить следующее:
Назначьте один сервер репозиторием репликации кода. Затем вы можете cron обновлять этот репозиторий с любым интервалом. Остальные серверы могут проверить наличие локального репозитория, а затем выполнить синхронизацию файлов с назначенного сервера. Эта информация может храниться в общем пространстве файлового сервера. Это будет довольно легко автоматизировать, и оно должно быть достаточно надежным.
Другое радикальное решение -> - использовать синхронизацию BitTorrent. Сервер репозитория будет доступен для чтения и записи, в то время как другие будут иметь общий доступ только для чтения. Может быть быстрее, поскольку сетевая нагрузка будет распределена между серверами. btsync можно настроить через файл конфигурации, и клиент linux работает довольно хорошо.
РЕДАКТИРОВАТЬ: вы можете пропустить сервер репозитория для радикального решения и придерживаться btsync.
Ура! :)
Дэни
Настоящая атомарность в сети невозможна без большого количества инженерных решений, обеспечивающих ее, и чем больше требуется инженерных решений, тем сложнее она будет.
Есть серьезные компромиссы, которые следует учитывать. Этот ответ не дает вам представления о том, что делать, когда работа сделана наполовину.
NFSv3 поддерживает механизм атомарной блокировки в новых ядрах (ну, честно говоря, довольно старый) http://nfs.sourceforge.net/#faq_d10 . Итак, некоторый механизм семафора теоретически может быть реализован следующим образом.
O_EXCL
.O_EXCL
.Вот некоторые сценарии оболочки шаблона, которые пытаются это сделать.
#!/bin/bash
# WARNING: This is a cricital line! NEVER EDIT THIS
set -e -o noclobber
BASEPATH=/tmp
cd "${BASEPATH}"
# 1. A done file exists on the host already (this is a signal for script 2 only)
# 2. Open an 'acquire' file on the host using `O_EXCL`.
echo > 'acquire'
# 3. Rename 'done' to 'done.old'.
mv 'done' 'done.old' 2>/dev/null || :
# 4. Do your special work here.
echo "How much wood could a woodchuck chuck if a woodchuck could chuck wood?"
# 5. Open a 'done' file using O_EXCL
echo > 'done'
# 6. Unlink 'done.old'.
unlink 'done.old' || :
# 7. Unlink 'acquire'.
unlink 'acquire'
Самая важная линия - это set -e -o noclobber
который служит двум целям.
Учитывая set
критериев наиболее важной функциональной частью является echo > acquire
который автоматически откроет полученный файл. Если это не удается (потому что это есть у кого-то другого, даже если происходит ДВА открытия одновременно, только один выиграет) -e
вариант set
гарантирует, что мы выходим из сценария.
Никогда не должно быть двух таких скриптов, работающих параллельно. Однако этот сценарий не предложить решение, в котором два сценария запускаются один за другим (что было бы разрешено в его текущей форме). Я полагаю, что лучшим средством для этого было бы изменить файл «готово» на некоторый именованный файл с меткой времени, наличие которого вы ищете до начала процесса. Таким образом, предполагается, что «безопасно» полагаться на время как на среду для определения безопасности критичности кода.
Я упоминаю, что это не конкретно. На данный момент это дает вам гарантию, что два процесса не могут потребовать файл одновременно. Как уже упоминалось, необходима дополнительная модификация, чтобы он не запускался при наличии готового файла.
Другие вещи не покрываются:
Чтобы решить эти проблемы, нужен механизм «ограждения» (частое изменение инфраструктуры), чтобы действительно гарантировать, что повторное получение блокировки на другом хосте является безопасной операцией.
Вам придется использовать какой-то файл блокировки (прежде чем что-либо делать), который показывает владельца первого скрипта и время выполнения. Когда кто-то другой пытается выполнить сценарий, он должен найти файл блокировки и выйти. В конце скрипта (если он запущен) удалите указанный файл блокировки.