Назад | Перейти на главную страницу

Скрипт работает на 50 серверах. Как я могу гарантировать, что только один выполнит определенный шаг?

У меня есть кое-что, что нужно сделать на 50+ серверах. Первым шагом является извлечение обновленной версии некоторого исходного кода в общий каталог (предположим, что у всех установлен общий диск). Второй - выполнить некоторую работу на каждом из серверов.

Я бы предпочел, чтобы эти два сценария работали на каждом из серверов. Все 50+ серверов клонированы с одного образа диска, и мне нецелесообразно настраивать какой-либо из них.

Когда 50 серверов запускают первый сценарий, я хочу, чтобы только первый, который пытается его запустить, действительно запускал его. Остальные я просто хочу выйти. Сервер, который фактически запускает сценарий, должен затем обновить общий каталог, а затем выйти. Затем, позже, второй сценарий будет запущен и выполнит работу на всех серверах на основе обновленного кода, полученного первым сервером.

Как лучше всего это сделать? Могу ли я надежно запустить первый скрипт на одном сервере и создать файл или что-то, что действует как «семафор» или «блокировка» какого-то рода, удерживая другие серверы подальше?

Это усложняется тем, что я думаю о том, чтобы сценарии запускались из идентичных файлов cron на каждом из серверов, то есть все сценарии могли бы попытаться запустить его одновременно, если все их часы установлены одинаково.

Я надеюсь, что они будут запускаться из сценариев bash. Имеет ли это смысл как подход?

РЕДАКТИРОВАТЬ: обновлено на основе вопросов:

Мы не хотим, чтобы каждый сервер пытался проверить свою собственную копию этих файлов - они находятся в репозитории исходного кода размером в несколько ГБ, и иметь 50+ одновременных проверок этого кода было бы сложно для нашего сервера управления версиями (и не масштабируемого до 100+ серверов).

Добавление cronjob к более чем 50 серверам - не такая уж большая проблема, но добавить еще один настроенный сервер с его собственной конфигурацией сложнее. Мы уже клонируем 50 серверов - поддержание отдельного сервера только для проверки последнего исходного кода для более чем 50 серверов кажется расточительным и добавит больше накладных расходов, чем просто добавление скрипта на наши текущие серверы.

Три решения.

  1. Выполните этап проверки вручную или в отдельном скрипте только на одном из серверов. Это кажется лучшим подходом - иначе вы можете попасть в состояние гонки.
  2. Если вы готовы принять шанс столкнуться с состоянием гонки, вы, безусловно, можете попробовать создать конкретный файл с датой при запуске первого скрипта. Или, если даты будут достаточно надежными, вы можете попробовать проверить дату последнего изменения извлеченных файлов.
  3. Если настройка действительно запрещена, пусть каждая виртуальная машина создаст свою собственную копию файлов для работы вместо того, чтобы пытаться использовать общий том.

У каждого из них есть компромиссы, но вы не совсем ясно дали понять, почему вы хотите спроектировать решение именно таким образом.

Могу я предложить следующее:

Назначьте один сервер репозиторием репликации кода. Затем вы можете cron обновлять этот репозиторий с любым интервалом. Остальные серверы могут проверить наличие локального репозитория, а затем выполнить синхронизацию файлов с назначенного сервера. Эта информация может храниться в общем пространстве файлового сервера. Это будет довольно легко автоматизировать, и оно должно быть достаточно надежным.

Другое радикальное решение -> - использовать синхронизацию BitTorrent. Сервер репозитория будет доступен для чтения и записи, в то время как другие будут иметь общий доступ только для чтения. Может быть быстрее, поскольку сетевая нагрузка будет распределена между серверами. btsync можно настроить через файл конфигурации, и клиент linux работает довольно хорошо.

РЕДАКТИРОВАТЬ: вы можете пропустить сервер репозитория для радикального решения и придерживаться btsync.

Ура! :)

Дэни

Настоящая атомарность в сети невозможна без большого количества инженерных решений, обеспечивающих ее, и чем больше требуется инженерных решений, тем сложнее она будет.

Есть серьезные компромиссы, которые следует учитывать. Этот ответ не дает вам представления о том, что делать, когда работа сделана наполовину.

NFSv3 поддерживает механизм атомарной блокировки в новых ядрах (ну, честно говоря, довольно старый) http://nfs.sourceforge.net/#faq_d10 . Итак, некоторый механизм семафора теоретически может быть реализован следующим образом.

  1. Готовый файл уже существует на хосте. (это сигнал только для скрипта 2)
  2. Откройте файл "получить" на хосте, используя O_EXCL.
  3. Переименуйте "готово" в "готово. Старый".
  4. Сделайте здесь свою особую работу.
  5. Откройте готовый файл на хосте, используя O_EXCL.
  6. Отключите ссылку "done.old".
  7. Отменить связь с словом "получить"

Вот некоторые сценарии оболочки шаблона, которые пытаются это сделать.

#!/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 который служит двум целям.

  • Это гарантирует, что в случае сбоя какой-либо команды сценарий завершится.
  • Скрипт не будет перезаписывать файлы (открывает в O_EXCL).

Учитывая set критериев наиболее важной функциональной частью является echo > acquire который автоматически откроет полученный файл. Если это не удается (потому что это есть у кого-то другого, даже если происходит ДВА открытия одновременно, только один выиграет) -e вариант set гарантирует, что мы выходим из сценария.

Никогда не должно быть двух таких скриптов, работающих параллельно. Однако этот сценарий не предложить решение, в котором два сценария запускаются один за другим (что было бы разрешено в его текущей форме). Я полагаю, что лучшим средством для этого было бы изменить файл «готово» на некоторый именованный файл с меткой времени, наличие которого вы ищете до начала процесса. Таким образом, предполагается, что «безопасно» полагаться на время как на среду для определения безопасности критичности кода.

Я упоминаю, что это не конкретно. На данный момент это дает вам гарантию, что два процесса не могут потребовать файл одновременно. Как уже упоминалось, необходима дополнительная модификация, чтобы он не запускался при наличии готового файла.

Другие вещи не покрываются:

  • Что, если процесс начнется, но не завершится?
  • Если общий каталог недоступен до или в середине, как с этим справиться.
  • Если хост слишком долго выполняет «безопасные» действия на шаге 4, как это повлияет на его следующий запуск? Должны ли мы использовать старый экземпляр после его завершения или новый?

Чтобы решить эти проблемы, нужен механизм «ограждения» (частое изменение инфраструктуры), чтобы действительно гарантировать, что повторное получение блокировки на другом хосте является безопасной операцией.

Вам придется использовать какой-то файл блокировки (прежде чем что-либо делать), который показывает владельца первого скрипта и время выполнения. Когда кто-то другой пытается выполнить сценарий, он должен найти файл блокировки и выйти. В конце скрипта (если он запущен) удалите указанный файл блокировки.