リモートコマンドの並列実行

リモートコマンドの並列実行

設定:

Linuxサーバーのリストを含むファイルを処理し、次のようにsshを介してそのリモートサーバーでbashまたはperlスクリプトを実行するPHPスクリプト(現在PHP 5.5用に作成されていますが、実行中のサーバーには7.4があります)があります。

exec("ssh -o StrictHostKeyChecking=no -p $connectivity_port $user@$server \"bash -s\" -- < $file $scriptargs 2>&1", $result, $exit_code);

これらの操作はすべてうまくいきますが、実行されるコードによっては時間がかかります。ローカルなので、スクリプト出力以外に処理することはほとんどありません(PHPを実行しているサーバー上のローカルファイルへのロギングといくつかのスクリプト出力がたくさんあります)。

ターゲット

bashで並行してPHPスクリプトを実行するための最良の/最も簡単な方法/ツールが何であるか疑問に思います。すべての出力がリスト内のサーバーの順序であることを確認してください(例:一度にxサーバー、おそらく10、実行時間を短縮)

私の研究とバージョンの制約で見ると、PHP自体は行く道ではないようですが、bashはその声明に合わないようですが、私は喜んで間違っている可能性があり、他の方法を学ぶことに興味があります。

答え1

あなたは次のことをしますbash

declare -r MAX_PARALLEL='5' WAITSEC='0.1'

i=0
server[i]=...
port[i]=...
user[i]=...
command_file[i]=...
scriptargs[i]=...
((i++))
server[i]=...
port[i]=...
user[i]=...
command_file[i]=...
scriptargs[i]=...
((i++))

count=$i
for((i=0;i<count;i++)); do
    while [ $(jobs -r | wc -l) -gt "$MAX_PARALLEL" ]; do
        sleep "$WAITSEC"
    done
    ( ssh -o StrictHostKeyChecking=no -p "${port[i]}" "${user[i]}@${server[i]}" \
"bash -s" <"${file[i]}" "${scriptargs[i]}" >output_file.$i 2>&1
      echo $? >exit_code.$i ) &
done

残念ながら、正しい作業数を得る簡単な方法はないようです。したがって、この方法はコマンドラインに改行文字が含まれていない場合にのみ正しく機能します。

答え2

Synology DS218で同様のことを実行しました。

私の場合、PHPスクリプトはさまざまなコマンドを使用してbashスクリプトを準備し、スクリプトを実行します。

これは次のように動作します。私のものケース

  • すべてのサーバーは分離されています。 (サーバーに過負荷がかかりません。)
  • サーバー12のエラーは、12以降のサーバーを停止してスキップすることを意味しません。

これらの要件が満たされない場合は、他の作業を実行する必要があります。

しかし、彼らがいる限りはい

#!/bin/bash

ssh server1 "command1" > output1 2> error1 &
ssh server2 "command2" > output2 2> error2 &
...
ssh serverN "commandN" > outputN 2> errorN &
# wait for all SSHs to complete
wait

最後に、すべての出力ファイルは番号順に収集され削除されます。

答え3

パールを使用できますパラレル::ForkManagerそしてIPC::オープン2

使用法:

cat list_of_servers.txt | perl para.pl /path/to/script.sh ARG1 ARG2

コードpara.pl:

#!/usr/bin/env perl
use v5.20;
use IPC::Open2 qw(open2);
use Parallel::ForkManager qw();
sub run_script_on_server {
    my ( $server, $script, @args ) = @_;
    say "$$ running script: $script on server: $server with args: @args";
    # TODO: replace with ssh invocation
    my $pid = open2( my $chld_out, my $chld_in, "bash", $script, @args );
    local $/ = undef;
    return <$chld_out>;
}
my $pm = Parallel::ForkManager->new(10);    
while ( my $server = <STDIN> ) {
    $pm->start and next;
    chomp $server;
    my $result = run_script_on_server( $server, @ARGV );
    say "$$ result from $server: $result";
    $pm->finish;
}

答え4

これを行う2つの方法を提案できます。

パラメータ

改行で区切られたホスト名のリストを含むファイルがあり、使用しているすべての接続userに対して。portxargs

xargs -I '{}' -P <max-procs> --arg-file <INPUTFILE> bash -c "ssh -o StrictHostKeyChecking=no -p $connectivity_port $user@{} 'bash -s' < $file $scriptargs > $OUT_FOLDER/{}.log 2>&1"

or

cat <INPUTFILE> | xargs -I '{}' -P <max-procs> bash -c "ssh -o StrictHostKeyChecking=no -p $connectivity_port $user@{} 'bash -s' < $file $scriptargs > $OUT_FOLDER/{}.log 2>&1"

このフラグを使用して並行性を設定できます-P

       --max-procs=max-procs
       -P max-procs
              Run up to max-procs processes at a time; the default is  1.   If
              max-procs  is 0, xargs will run as many processes as possible at
              a time.  Use the -n option with -P; otherwise chances  are  that
              only one exec will be done.

各コマンドの出力を記録します$OUT_FOLDER/$HOST.log

user異なるマシンがあり、port各マシンで引き続き使用できますが、xargs少し複雑になります。

PDSH

別のオプションは、次のものを使用することです。pdsh「並列にホストグループにコマンドを実行できます。」

pdsh -R exec -w^<INPUT FILE> -f <max-procs> bash -c "ssh -o StrictHostKeyChecking=no -p $connectivity_port %u@%h 'bash -s' < $file $scriptargs 2>&1"

これはxargsのフラグと似ています-f-P

exec    Executes an arbitrary command for each target host. The first of the pdsh remote arguments is the local command
        to execute, followed by any further arguments. Some simple parameters  are  substitued  on  the  command  line,
        including  %h  for  the target hostname, %u for the remote username, and %n for the remote rank [0-n] (To get a
        literal % use %%).  For example, the following would duplicate using the ssh module to run  hostname(1)  across
        the hosts foo[0-10]:

          pdsh -R exec -w foo[0-10] ssh -x -l %u %h hostname

       and this command line would run grep(1) in parallel across the files console.foo[0-10]:

          pdsh -R exec -w foo[0-10] grep BUG console.%h

-f number
       Set the maximum number of simultaneous remote commands to number.  The default is 32.

プレフィックスが付いたコマンドの出力をダンプする場合HOSTNAME:

ここに例があります。

$ pdsh -R exec -w host1,host2 bash -c "ssh  -o StrictHostKeyChecking=no -p 22 %u@%h 'bash -s' <<< 'echo Running script on %h with arguments: \${@}' arg1 arg2 arg3"
host1: Running script on host1 with arguments: arg1 arg2 arg3
host2: Running script on host2 with arguments: arg1 arg2 arg3

関連情報