Bash スクリプト - ハングしたサーバーを無視する方法

Bash スクリプト - ハングしたサーバーを無視する方法

バックグラウンドで 1000 台以上のサーバーでコマンドを実行するスクリプトを作成しました。スクリプトがサーバーの 1 つでハングすることがあります。スクリプトの実行中にサーバーがハングした場合 (平均負荷が高いため)、コマンドもそのサーバーでハングする可能性があります。そのホストをスキップして、スクリプトが次のホストに移動して実行を継続できるようにする方法はありますか?

スクリプトの 2 つの主要な機能を強調していますが、「ConnectTimeout」と wait キーワードを指定できません。

exec_ssh()
{
for i in `cat $file`
do 
    ssh -q -o "StrictHostKeyChecking no" -o "NumberOfPasswordPrompts 0" -o ConnectTimeout=2 $i $command  2>>/dev/null &
        if wait $!; then
                echo "" >> /dev/null
        else
                echo "$i is not reachable over SSH or passwordless authentication is not setup on the server" >> /tmp/not_reachable
        fi

done >/tmp/output.csv &


run_command()
{
                        export -f exec_ssh
                        export command
                        nohup bash -c exec_ssh &>>$log_file &
}

答え1

書かれたスクリプトだろうすべてのリモート コマンドを同時に実行し続けますが、 を使用する場合は、waitバックグラウンド タスクが完了するまで明示的に待機します。高負荷サーバーの場合、これはコマンドがsshタイムアウトしているのではなく、完了するまでに時間がかかっているだけであり、スクリプトは要求どおりに動作していることを意味します。 接続をConnectTimeout正常に確立できる場合は、これは意味がありません。ssh

分散リモート実行用に設計されたツールではなく、このようなスクリプトを使用したい場合は、アンシブルスクリプトを次のように変更するかもしれません:

exec_ssh() {
  while read file; do
    if ! ssh -q -o BatchMode=yes -o ConnectTimeout=2 "$i" "$command"  2>>/dev/null & then
      echo "$i is not reachable via non-interactive SSH or remote command threw error - exit code $?" >> /tmp/not_reachable
    fi
  done < "$file" > /tmp/output.csv &
}

run_command() {
    export -f exec_ssh
    export command
    nohup bash -c exec_ssh &>> "$log_file" &
}

また、「ホストに SSH 接続できるか」というテストと「ジョブを完了できるか」というテストを分離することを検討する価値があるかもしれません。

if ssh -q -o BatchMode=yes -o ConnectTimeout=2 "$host" true; then
    # connection succeeded
  if ! ssh -q -o BatchMode=yes -o ConnectTimeout=2 "$host" "$command" & then
    echo "Remote command threw $?"
  fi
else
    echo "SSH threw $?"
fi

答え2

ローカル コマンドとリモート コマンドが複雑になるにつれて、これらすべてを 1 つの一貫したスクリプトに詰め込もうとするとすぐに圧倒されてしまいます。また、バックグラウンド プロセスが数百または数千あると、強力なローカル マシンであってもリソース競合の問題が発生する可能性があります。

これを で制御できますxargs -P。私は通常、このようなタスクを 2 つのスクリプトに分割します。

ローカル

通常、このスクリプトにはホスト名という単一の引数があり、必要な検証、事前タスク、ログ記録などを実行します。例:

#!/bin/bash
hostname=$1
# simple
cat remote.sh | ssh user@$hostname
# sudo the whole thing
cat remote.sh | ssh user@$hostname sudo
# log to files
cat remote.sh | ssh user@$hostname &> logs/$hostname.log
# or log to stdout with the hostname prefixed
cat remote.sh | ssh user@$hostname 2>&1 | sed "s/^/$hostname:/"

リモート.sh

スクリプトをリモートで実行したいのですが、引用符付きのワンライナーに詰め込んで引用符エスケープの苦労をする必要がなくなりました。

実際のコマンド

cat host_list.txt | xargs -P 16 -n 1 -I {} bash local.sh {}

どこ:

  • -P 16最大16個のサブプロセスをフォークします
  • -n 1コマンドごとに正確に1つの引数を渡します
  • -I {}は、[の代わりに引数を置き換えます。{}ここでは必要ありませんが、より複雑な xargs 呼び出しを構築する場合に役立ちます。

こうすることで、ローカル スクリプトまたはリモート スクリプトの 1 つが停止した場合でも、他の 15 個のスクリプトは問題なく実行され続けます。

関連情報