SSH Bash скрипт для повторных команд в сетевом кластере
У меня есть сетевой кластер около 100 машин, часть программного обеспечения и большой список различных параметров для программного обеспечения.
Я хочу использовать кластер для ускорения вычислений, поэтому предполагается, что на каждой машине запущено программное обеспечение с другим набором параметров из списка. Когда машина заканчивает свои вычисления, она должна получить следующий набор параметров и снова запустить программное обеспечение, пока все параметры не будут использованы.
Есть ли способ сделать это в скрипте bash через ssh? Я полагаю, что pssh - это путь, но я не мог понять, как это сделать.
1 ответ
Я написал сценарий bash, который делает нечто похожее, истощая локальные ядра процессора. Когда ядро освобождается, оно вызывает новый расчет, пока вычисления не будут завершены. У меня также есть небольшой опыт написания сценариев bash с использованием ssh (для этого требуется пароль ssh без пароля, если вас устраивает этот риск безопасности). Это личный пример вне контекста, но идея в том, что это bash-скрипт, который динамически зацикливается в зависимости от времени обработки и изменяет параметры. Переменная $CORES, в вашем случае, должна быть заполнена доступными серверами, и нам нужно найти способ отследить их, чтобы знать, что вызвать дальше.
Loop () {
# looping function over all runs with the same header, multi-threaded per core.
CMDINIT="$CMD"
for i in "$TREEIN"/"$NAME"*.root # loop over all the existing raw runs of that name
do
# name of the output file and path, eg Tree/30s_production/30s_production-1001.root
OUTPUT=`echo $i | sed "s#$TREEIN/##" |sed "s#$NAME#$TREEOUT/$NAME/$NAME-#" `
INFILE=`echo $i | sed 's$.*/$$'` # name of the input file name, eg 30s_production1001.root
if [ ! -e $OUTPUT ];then # only run if the output file does not exist (won't overwrite existing data)
if [ ! $Cflag ];then # only call the program if we aren't cleaning
echo "Outputting to $OUTPUT..."
RUNNO=`echo $INFILE | sed "s/$NAME//" | sed 's/\..*//'` # get the run number
# there is a way to do this using the Run function? Seems trickier with backgrounding, getting PID, and so on
CMD="$CMDINIT -R $RUNNO"
printf "Executing run with the command:\n\t$CMD\n"
$CMD & PIDS="$PIDS $!" # call run on the run number in background w/o renice
#$CMD & PIDS="$PIDS $!" && sudo renice -n 0 -p $! # call run on the run number in background, renice to -10
while [ `jobs | wc -l` -eq $CORES ] # only run one run command per core
do
jobs > /dev/null # without this the while loop doesn't seem to refresh?
sleep 1 # keep waiting until run is not running on a core
done
fi
else # the output file exists -- should never happen as we check NeedClean first, but anyway, safer
echo "$OUTPUT exists, please run clean!"
exit 1
fi
done
}
Здесь есть две "умные" части (или "хаки", если хотите). Одним из них является цикл while, который проверяет количество jobs
и ждет, пока что-то не освободится. (Мой нынешний цикл do основан на одном параметре, но его легко настроить.) jobs
находятся ли они внутри скрипта bash, и именно так достигается концепция повторного цикла при условии, что задание завершено; имейте в виду, что это будет идентично, независимо от того, является ли задание локальным или удаленным: команда для вызова команды SSH будет такой же, как и локальное задание (хотя нам может потребоваться собрать все результаты позже со всех серверов или серверы, записывающие данные локально и т. д. по мере необходимости). Другой важный для меня аспект - как при вызове $CMD он также привязывает номер процесса к счетчику с именем $PIDS. Это позволяет ловушке control_c локального сценария bash иметь возможность уничтожить все дочерние процессы, которые будут включать порожденные процессы на всех ваших 100 серверах, если вы решите прекратить работу раньше; последствия не отслеживания этого ужасны, как вы можете себе представить!
Если вы хотите проверить основной скрипт, он находится здесь: https://github.com/goatface/crabat/blob/master/crabat
Нам нужно изменить определение переменной $CMD, чтобы она была чем-то порядка
CMD=ssh '$USER@$HOST' /path/to/executable
После этого мы должны динамически прикрепить флаги к исполняемому файлу для управления различными параметрами (мы также можем отправить их в текстовом файле на каждый сервер через scp, но, в конце концов, мы должны отслеживать их в любом случае, и для меня это несущественно). Мой случай устанавливает большинство параметров один раз, но нет никаких причин, по которым мы не можем вызывать это каждый раз, когда открывается сервер. Это выглядит так, где я использую флаги, но это тривиально, чтобы установить буферизацию наборов текстовых файлов с параметрами. Увеличивайте счетную переменную в поле awk для каждого поля по порядку, пока не будет исчерпан и т. Д. И сбрасывайте ее в функции Loop каждый раз, когда следующая переменная счетчика увеличивается последовательно через все перестановки параметров.
SetFlags () {
# base command
CMD="./run"
# add options based on final flags
[ $Rflag ] && CMD="$CMD -R $Rval"
[ $Bflag ] && CMD="$CMD -B"
[ $Hflag ] && CMD="$CMD -H $Hval"
[ $Iflag ] && CMD="$CMD -I $Ival"
[ $rflag ] && CMD="$CMD -r"
[ $dflag ] && CMD="$CMD -d"
[ $sflag ] && CMD="$CMD -s"
[ $xflag ] && CMD="$CMD -x"
[ $pflag ] && CMD="$CMD -p"
[ $tflag ] && CMD="$CMD -t"
}
Я получил возможность получать электронные письма после ответов, так как считаю, что это интересный вопрос, но мне нужно больше времени, чтобы подумать о том, как отслеживать хосты, когда они освобождаются. Извините, я еще не ответил на весь вопрос!