
Я написал скрипт, который запускает команды на 1000+ серверах в фоновом режиме. Иногда скрипт зависает на одном из серверов. Если/когда сервер зависает (из-за высокой средней нагрузки) при запуске скрипта, команда также может зависнуть на этом сервере. Есть ли способ пропустить этот хост, чтобы скрипт мог перейти на следующий хост и продолжить работу?.
Я выделяю две основные функции моего скрипта, но не могу указать ключевые слова «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
which явно будет ждать завершения фоновой задачи. В случае, если вы описываете сервер с высокой нагрузкой, это означает, что ваша 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
По мере того, как ваши локальные и удаленные команды становятся все более сложными, вы быстро перегружаетесь попытками втиснуть все это в один связный сценарий, а при наличии сотен или тысяч фоновых процессов вы, скорее всего, столкнетесь с проблемами конкуренции за ресурсы даже на мощной локальной машине.
Вы можете взять это под контроль с помощью xargs -P
. Обычно я разбиваю такие задачи на два скрипта.
локальный.ш
Обычно этот скрипт имеет один аргумент — имя хоста — и выполняет все необходимые проверки, предполетные задачи, ведение журнала и т. д. Например:
#!/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
будет передавать только один аргумент на команду-I {}
подставит аргумент вместо{}
[здесь это не обязательно, но может быть полезно для построения более сложных вызовов xargs.
Таким образом, даже если один из ваших локальных или удаленных скриптов зависнет, остальные 15 продолжат беспрепятственно работать.