
Escrevi um script que executa comandos em mais de 1.000 servidores em segundo plano. Às vezes, o script fica travado em um dos servidores. Se/quando um servidor travar (devido à alta carga média) ao executar um script, o comando também poderá travar nesse servidor. Existe uma maneira de pular esse host para que o script possa ir para o próximo host e continuar em execução?
Estou destacando duas funções principais do meu script, mas não tive sorte em fornecer as palavras-chave "ConnectTimeout" e 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 &
}
Responder1
Seu roteiro conforme escritoseriacontinue executando todos os seus comandos remotos simultaneamente, mas para seu uso wait
aguardará explicitamente a conclusão de uma tarefa em segundo plano. No caso que você descreve de um servidor de alta carga, isso significa que seu ssh
comando não está expirando, mas simplesmente está demorando muito para ser concluído, então o script está fazendo exatamente o que você pediu. ConnectTimeout
é discutível quando você consegue fazer a ssh
conexão com sucesso.
Se você quiser usar esse tipo de script em vez de uma ferramenta projetada para execução remota distribuída, comoAnsible, posso modificar seu script da seguinte maneira:
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" &
}
Também pode valer a pena considerar separar o teste "posso SSH para o host" e o teste "posso concluir o trabalho":
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
Responder2
À medida que seus comandos locais e remotos se tornam mais complexos, você rapidamente ficará sobrecarregado ao tentar agrupar tudo isso em um script coerente e, com centenas ou milhares de processos em segundo plano, é provável que você encontre problemas de contenção de recursos, mesmo com um robusto máquina local.
Você pode controlar isso com xargs -P
. Normalmente divido tarefas como essa em dois scripts.
local.sh
Geralmente este script tem um único argumento que é o nome do host e executa todas as validações necessárias, tarefas de pré-voo, registro, etc.
#!/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:/"
remoto.sh
O script que você deseja executar remotamente, mas agora você não precisa amontoá-lo em uma frase de uma linha e lidar com o inferno das citações.
O comando real
cat host_list.txt | xargs -P 16 -n 1 -I {} bash local.sh {}
Onde:
-P 16
irá bifurcar até 16 subprocessos-n 1
alimentará exatamente um argumento por comando-I {}
substituirá o argumento no lugar de{}
[não é necessário aqui, mas pode ser útil para construir chamadas xargs mais complexas.
Dessa forma, mesmo que um de seus scripts locais ou remotos seja interrompido, você ainda terá os outros 15 trabalhando sem impedimentos.