Script SSH Bash para comandos repetidos em um cluster de rede

1

Eu tenho um cluster de rede com cerca de 100 máquinas, um software e uma grande lista de diferentes parâmetros para o software.

Eu quero usar o cluster para acelerar o cálculo, então cada máquina deve executar o software com um conjunto diferente de parâmetros da lista. Quando uma máquina termina seus cálculos, ela deve receber o próximo conjunto de parâmetros e executar o software novamente até que todos os parâmetros tenham sido usados.

Existe uma maneira de fazer isso em um script bash via ssh? Acho que pssh é o caminho a seguir, mas não consegui descobrir como fazer isso.

    
por Briensturm 23.07.2015 / 12:01

1 resposta

2

Eu escrevi um script bash que faz algo similar, esgotando os núcleos da CPU local. Quando um núcleo é liberado, ele chama um novo cálculo, até que os cálculos sejam concluídos. Eu também tenho um pouco de experiência de script bash com ssh (requer um ssh-key sem senha, se você estiver confortável com esse risco de segurança). Este é um exemplo pessoal fora do contexto, mas a idéia é que é um script bash que faz um loop dinamicamente com base no tempo de processamento e varia os parâmetros. A variável $ CORES, no seu caso, precisa ser preenchida com os servidores disponíveis, e precisamos encontrar uma maneira de rastreá-los para saber para qual ligar em seguida.

    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
    }

Existem duas partes 'inteligentes' aqui (ou 'hacks', se você preferir). Um é o loop while que verifica o número de jobs e aguarda até que algo esteja livre. (Meu presente do loop é baseado em um parâmetro, mas isso é fácil de ajustar.) Aqueles jobs são os que estão dentro do script bash, e é assim que ele alcança o conceito para repetir novamente em uma condição em que um job é concluído ; lembre-se de que isso será idêntico se o trabalho for local ou remoto: um comando para chamar um comando SSH será o mesmo que um trabalho local (embora, talvez seja necessário coletar todos os resultados posteriormente de todos os servidores ou servidores gravando os dados localmente, etc, conforme necessário). Outro aspecto crítico para mim é como quando $ CMD é chamado, ele também coloca o número do processo em um contador chamado $ PIDS. Isso permite que uma armadilha control_c no script bash local consiga eliminar todos os processos filhos, o que incluiria processos gerados em todos os seus 100 servidores, caso você decida encerrar antes; a conseqüência de não rastrear isso é tão horrível quanto você pode imaginar!

Se você quiser verificar o script principal, está aqui: link

Precisamos modificar a definição da variável $ CMD para algo na ordem de

CMD=ssh '$USER@$HOST' /path/to/executable

Depois disso, devemos aplicar dinamicamente os sinalizadores ao executável para controlar os diferentes parâmetros (também podemos enviá-los em um arquivo de texto para cada servidor via scp, mas, finalmente, precisamos rastreá-los de qualquer maneira e a diferença não é significativo para mim). Meu caso define a maioria dos parâmetros uma vez, mas não há motivo para que não possamos chamar isso toda vez que um servidor for aberto. Parece com isso, onde eu uso sinalizadores, mas isso é trivial para definir conjuntos de spool de arquivos de texto com parâmetros. Incrementar uma variável de contagem em um campo awk para cada campo, em ordem, até que seja esgotado, etc, e redefini-la na função Loop toda vez que a próxima variável de contagem for incrementada, seqüencialmente através de todas as permutações de parâmetros.

    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"
    }

Tenho permissão para receber e-mails após respostas, pois considero essa uma pergunta interessante, mas preciso de mais tempo para pensar em acompanhar os hosts quando eles são desalocados. Desculpe eu não respondi a questão toda ainda!

    
por 23.07.2015 / 19:57