- Ao fazer tentativas repetidas de conexão em scripts Bash para verificar o status de um servidor web, pode surgir o problema de o servidor entrar inesperadamente em um loop infinito
- A ferramenta
timeout, usada para resolver isso, define um limite de tempo para a execução de um comando e, ao excedê-lo, envia um sinal para tentar o encerramento do processo
- Ela não pode ser aplicada diretamente a shell built-ins como
until, então a solução é usar um wrapper com processo bash ou separar em outro script
Espera por servidor web em Bash e o problema de loop infinito
- No trabalho prático, scripts Bash são usados para fazer configuração de servidor web e verificação de status
- A estrutura adia a próxima etapa enquanto o servidor está subindo e, em geral, funciona sem problemas
- Porém, se ocorrer um crash durante a inicialização do servidor, o script pode cair em um loop infinito, o que exigiu uma solução
Exemplo de uso de until e suas limitações
Introdução ao utilitário timeout
- O comando
timeout encerra um comando enviando um sinal (como SIGTERM) se ele não for concluído dentro do tempo especificado
- Exemplo: em
timeout 1s sleep 5, após 1 segundo é feita uma tentativa de encerrar o processo sleep
- Ao encerrar, ele retorna um código de saída anormal (por exemplo, 124)
Tentativa de combinar timeout com until e o problema
Solução: wrapper com processo Bash ou uso de script externo
1 comentários
Comentários do Hacker News
Meu truque pouco conhecido favorito é usar fault injection do strace para testar falhas em várias system calls
Há uma explicação mais detalhada no link relacionado
Acho essa funcionalidade realmente impressionante e compartilho a sensação de que gostaria de tê-la conhecido antes
Como não havia como testar os ramos de falha, eu costumava substituir temporariamente partes da função por código provisório, mas esse truque sugere uma abordagem mais concisa
Opinião de que esse método parece realmente útil
Levanta a dúvida se existe algo parecido também no Windows
Sugestão de que, em health checks de serviço, a melhor abordagem é definir tanto o tempo máximo de timeout quanto o número máximo de tentativas
Em geral, tenta-se repetir até X vezes e considera-se falha dentro de um tempo máximo de Y
Enfatiza-se a necessidade de decidir a falha o mais rápido possível, em vez de esperar tempo demais
Em serviços padronizados, o health check só começa depois que as dependências do contêiner estão garantidas e prontas para operar
No Kubernetes, veja Init Container; no AWS ECS,
dependsOn; no Docker Compose, a configuraçãodepends_onÉ fornecido um exemplo em shell POSIX
Mas também é mencionado que o próprio curl já tem essa funcionalidade embutida, então ela pode ser usada assim, sem script separado
Relato de ter tentado várias vezes implementar timeout só com builtins do bash, já que no Mac o comando
timeoutnão é fornecido por padrãoExplica-se que o comando
sleepé padrão POSIX, então pode ser usadoÉ fornecido abaixo um exemplo de implementação da funcionalidade de timeout
Uma função chamada
times_upfaz o tratamento do timeoutÉ fornecido um exemplo de teste com timeout de 10 segundos repetindo um
for20 vezesRelato de que, há 12 anos, foi implementado um método parecido seguindo um conselho do Stack Overflow
Os detalhes podem ser vistos no link de referência
Enfatiza-se que o código usava apenas shell builtins e
sleep, e que a compatibilidade com POSIX era obrigatóriaTambém se menciona que, no exemplo, a sintaxe
{1..20}do bash não é POSIX, então é preciso cuidadoMinha melhoria foi fazer a função retornar true quando não há timeout e false quando ele ocorre, para simplificar o tratamento de erro dentro do script
Compartilha-se um método bem simples de executar o comando e
sleepem paralelo e, depois do tempo especificado, encerrar o comando com um sinalCompartilha-se um exemplo de script de 13 anos atrás que implementava timeout com
read -tLink
Informa-se que o curl já tem a flag
--retry-connrefused, então essa funcionalidade pode ser usada diretamente sem loop em shellAo usar
bash -c, se for necessário passar variáveis, recomenda-se adicionar os argumentos assimExplica-se por que usar
"--"e qual é o papel de argv[0]Também se menciona que
printf %qpode ser usado, mas há preferência pela abordagem compatível com BourneExplica-se que
"--"tem um significado muito claro como sinal de fim de opções no bash e na maioria das CLIs Unix/LinuxReferência relacionada
O Busybox decide qual programa executar com base no valor de argv[0], então ele pode ser definido como o comando desejado, como
ls,mvoucpQuando preciso de lógica de repetição, o método que costumo usar é o seguinte
Não é muito elegante, mas em geral é correto, e em um nível mais avançado também se pode aplicar backoff exponencial
Há vantagens também em termos de extensibilidade
O shellcheck recomenda tratar esse caso usando a variável
_Link de referência
Ressalta-se que a função
eventually_succeeds, dependendo da situação, pode precisar de timeout ou de código defensivo adicionalRelembra-se a importância de sempre escrever código defensivo em POSIX/processos/IO
Relato de que, quando os filhos eram pequenos, isso foi usado como uma espécie de controle parental para permitir assistir apenas um programa de 30 minutos com o comando abaixo
Avaliação de que a ideia foi aplicada de forma muito útil
Afirma-se que não se gosta muito de usar comandos inline ou arquivos de script temporários quando é necessário enviar sinais para subprocessos
O método preferido é transformar a lógica desejada em uma função, exportá-la e depois envolvê-la com
timeout bash -cIsso se relaciona com o método seguro de passagem de argumentos mencionado por aidenn0
"$@"no finalCaso contrário, argumentos com espaços não são passados corretamente
Compartilha-se um exemplo de
long_fnpara verificar esse pontoRelembra-se um post de blog antigo que mencionava timeout
Recomenda-se o blog relacionado para quem tiver curiosidade sobre linguagens de programação gerais, e não shell, ou sobre o funcionamento interno
Relato de experiência adicionando timeout de comandos em um ambiente Kubernetes
Informa-se que scripts shell POSIX como
await-cmd.sh,await-http.sheawait-tcp.shestão maduros e podem ser bastante úteis em certas situaçõesLink do projeto relacionado