1

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

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

Есть ли способ сделать это в скрипте bash через ssh? Я полагаю, что pssh - это путь, но я не мог понять, как это сделать.

1 ответ1

2

Я написал 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"
    }

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

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .