Health checks e alertas em scripts de monitoramento
1. Fundamentos de Health Checks em Shell Script
1.1. O que são health checks e por que implementá-los no Bash
Health checks são verificações periódicas que determinam se um sistema, serviço ou aplicação está operando dentro dos parâmetros esperados. Implementá-los em Bash oferece vantagens significativas: baixo overhead, simplicidade de deploy e integração nativa com o sistema operacional Unix-like. Scripts de monitoramento em Bash são ideais para ambientes onde instalar agentes complexos não é viável ou desejável.
1.2. Tipos de verificações
As verificações mais comuns incluem:
- Conectividade: teste de porta com
/dev/tcpounc, ping para latência - Processos: verificação se um daemon está rodando com
pgrep - Recursos do sistema: CPU, memória, disco e inodes
1.3. Estrutura básica de um script de monitoramento
#!/bin/bash
INTERVALO=60
HOST="exemplo.com"
while true; do
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
# Coleta de métricas
HTTP_STATUS=$(curl -o /dev/null -s -w "%{http_code}" "https://$HOST/health")
CPU_USO=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
# Lógica de alerta
if [ "$HTTP_STATUS" -ne 200 ]; then
echo "$TIMESTAMP - ALERTA: HTTP status $HTTP_STATUS"
fi
sleep "$INTERVALO"
done
2. Coleta de Métricas do Sistema
2.1. CPU, memória e disco
# CPU usage (percentual)
CPU_IDLE=$(top -bn1 | grep "Cpu(s)" | awk '{print $8}' | cut -d',' -f1)
CPU_USO=$(echo "100 - $CPU_IDLE" | bc)
# Memória (percentual de uso)
MEM_TOTAL=$(free -m | awk '/^Mem:/ {print $2}')
MEM_USADO=$(free -m | awk '/^Mem:/ {print $3}')
MEM_PCT=$(echo "scale=2; $MEM_USADO * 100 / $MEM_TOTAL" | bc)
# Disco (percentual de uso)
DISCO_USO=$(df -h / | awk 'NR==2 {print $5}' | tr -d '%')
2.2. Verificação de processos críticos
PROCESSO="nginx"
if pgrep -x "$PROCESSO" > /dev/null; then
echo "Processo $PROCESSO está rodando"
else
echo "CRITICAL: Processo $PROCESSO não encontrado"
fi
# Health endpoint via curl
HEALTH_CHECK=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 5 http://localhost:8080/health)
2.3. Métricas de rede
# Latência e perda de pacotes
PING_RESULT=$(ping -c 5 -q 8.8.8.8 2>&1 | tail -1)
PERDA=$(echo "$PING_RESULT" | awk -F'/' '{print $6}' | tr -d '%')
LATENCIA=$(echo "$PING_RESULT" | awk -F'/' '{print $4}')
# Tempo de resposta HTTP
TEMPO_RESPOSTA=$(curl -o /dev/null -s -w "%{time_total}" https://api.exemplo.com)
3. Lógica de Thresholds e Condições de Alerta
3.1. Definição de limites
# Configuração via variáveis de ambiente
WARN_CPU=80
CRIT_CPU=95
WARN_DISCO=85
CRIT_DISCO=95
# Ou arquivo de configuração
source /etc/monitoramento.conf
3.2. Comparações numéricas e com floats
# Comparação de inteiros
if [ "$DISCO_USO" -ge "$CRIT_DISCO" ]; then
echo "CRITICAL: Disco em $DISCO_USO%"
elif [ "$DISCO_USO" -ge "$WARN_DISCO" ]; then
echo "WARNING: Disco em $DISCO_USO%"
fi
# Comparação com floats usando bc
if (( $(echo "$CPU_USO > $CRIT_CPU" | bc -l) )); then
echo "CRITICAL: CPU em $CPU_USO%"
fi
# Uso de awk para cálculos
MEM_CRITICO=$(echo "$MEM_PCT $CRIT_MEM" | awk '{if ($1 > $2) print 1; else print 0}')
3.3. Estados de saúde e códigos de saída
# Mapeamento padrão Nagios/Icinga
OK=0
WARNING=1
CRITICAL=2
UNKNOWN=3
verifica_servico() {
local status=$1
case $status in
200) return $OK ;;
301|302) return $WARNING ;;
500|502|503) return $CRITICAL ;;
*) return $UNKNOWN ;;
esac
}
4. Mecanismos de Notificação e Alertas
4.1. Envio de alertas via linha de comando
# Email via mail
echo "ALERTA: Servidor $HOSTNAME - CPU em $CPU_USO%" | mail -s "[CRITICAL] Monitoramento" admin@exemplo.com
# Slack via webhook
curl -s -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"🚨 ALERTA: Servidor $HOSTNAME\\nCPU: $CPU_USO%\\nDisco: $DISCO_USO%\"}" \
https://hooks.slack.com/services/TOKEN
# Telegram
curl -s -X POST "https://api.telegram.org/botTOKEN/sendMessage" \
-d "chat_id=CHAT_ID&text=ALERTA: $HOSTNAME - CPU $CPU_USO%"
4.2. Acumuladores e debounce
ARQUIVO_ESTADO="/tmp/monitor_estado_$HOSTNAME"
CONTAGEM_ALERTAS=0
LIMITE_ALERTAS=3
if [ "$DISCO_USO" -ge "$CRIT_DISCO" ]; then
CONTAGEM_ALERTAS=$((CONTAGEM_ALERTAS + 1))
echo "$CONTAGEM_ALERTAS" > "$ARQUIVO_ESTADO"
if [ "$CONTAGEM_ALERTAS" -ge "$LIMITE_ALERTAS" ]; then
enviar_alerta "Disco crítico por $CONTAGEM_ALERTAS verificações consecutivas"
echo 0 > "$ARQUIVO_ESTADO" # Reset após alerta
fi
else
echo 0 > "$ARQUIVO_ESTADO"
fi
4.3. Formatação de mensagens
formatar_alerta() {
local nivel=$1
local mensagem=$2
local metrica=$3
local valor=$4
local limite=$5
cat <<EOF
=== ALERTA DE MONITORAMENTO ===
Timestamp: $(date -u +"%Y-%m-%dT%H:%M:%SZ")
Hostname: $(hostname)
Nível: $nivel
Métrica: $metrica
Valor atual: $valor
Limite: $limite
Mensagem: $mensagem
===============================
EOF
}
5. Logging e Rastreabilidade
5.1. Logs locais com logger e logrotate
# Envio para syslog
logger -t "monitor" "CRITICAL: Disco em $DISCO_USO% no servidor $(hostname)"
# Log para arquivo com rotação
echo "$(date -u +"%Y-%m-%dT%H:%M:%SZ")|$HOSTNAME|CPU|$CPU_USO|$ESTADO" >> /var/log/monitoramento.log
5.2. JSON logs estruturados
gerar_json_log() {
local nivel=$1
local metrica=$2
local valor=$3
cat <<EOF
{
"timestamp": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")",
"hostname": "$(hostname)",
"nivel": "$nivel",
"metrica": "$metrica",
"valor": $valor,
"servico": "healthcheck"
}
EOF
}
# Exemplo de uso
gerar_json_log "WARNING" "cpu" 85.5 >> /var/log/monitoramento.json
5.3. Timestamps e persistência
# Timestamp ISO 8601
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
# Arquivo de histórico
HISTORICO="/var/log/monitor_historico.csv"
echo "$TIMESTAMP,$HOSTNAME,$CPU_USO,$MEM_PCT,$DISCO_USO,$HTTP_STATUS" >> "$HISTORICO"
6. Resiliência e Tratamento de Falhas
6.1. Timeout e retry
executar_com_timeout() {
local comando=$1
local timeout=$2
local tentativas=$3
local contador=0
while [ $contador -lt $tentativas ]; do
if timeout "$timeout" bash -c "$comando" 2>/dev/null; then
return 0
fi
contador=$((contador + 1))
sleep 2
done
return 1
}
# Uso
if ! executar_com_timeout "curl -s https://api.exemplo.com" 5 3; then
echo "Falha após 3 tentativas"
fi
6.2. Captura de sinais com trap
cleanup() {
echo "Finalizando monitoramento graciosamente..."
rm -f /tmp/monitor_*.lock
exit 0
}
trap cleanup SIGINT SIGTERM SIGHUP
# Loop principal com proteção contra interrupções
while true; do
# ... lógica de monitoramento ...
sleep 60
done
6.3. Lockfiles para evitar concorrência
LOCKFILE="/var/run/monitoramento.lock"
exec 200>"$LOCKFILE"
if ! flock -n 200; then
echo "Script já está em execução"
exit 1
fi
# ... lógica do script ...
flock -u 200
rm -f "$LOCKFILE"
7. Exemplo Prático: Script de Monitoramento de Serviço Web
7.1. Script completo
#!/bin/bash
# Configurações
URL="https://meuservico.com/health"
ALERTA_EMAIL="admin@exemplo.com"
LIMITE_CPU=90
LIMITE_MEM=85
INTERVALO=300
ARQUIVO_ESTADO="/tmp/health_estado.txt"
# Funções
enviar_alerta() {
local nivel=$1
local mensagem=$2
echo "$mensagem" | mail -s "[$nivel] Monitoramento - $(hostname)" "$ALERTA_EMAIL"
}
verificar_servico() {
local status=$(curl -o /dev/null -s -w "%{http_code}" --connect-timeout 10 "$URL")
local tempo=$(curl -o /dev/null -s -w "%{time_total}" "$URL")
if [ "$status" -ne 200 ]; then
enviar_alerta "CRITICAL" "HTTP $status - Tempo: ${tempo}s - URL: $URL"
return 2
elif (( $(echo "$tempo > 3" | bc -l) )); then
enviar_alerta "WARNING" "Resposta lenta: ${tempo}s - URL: $URL"
return 1
fi
return 0
}
verificar_recursos() {
local cpu=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
local mem=$(free -m | awk '/^Mem:/ {printf "%.1f", $3/$2 * 100}')
if (( $(echo "$cpu > $LIMITE_CPU" | bc -l) )); then
enviar_alerta "CRITICAL" "CPU em ${cpu}% (limite: ${LIMITE_CPU}%)"
fi
if (( $(echo "$mem > $LIMITE_MEM" | bc -l) )); then
enviar_alerta "WARNING" "Memória em ${mem}% (limite: ${LIMITE_MEM}%)"
fi
}
# Loop principal
while true; do
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
echo "[$TIMESTAMP] Iniciando verificação..."
verificar_servico
verificar_recursos
# Log estruturado
echo "{\"timestamp\":\"$TIMESTAMP\",\"host\":\"$(hostname)\",\"status\":\"ok\"}" >> /var/log/health.json
sleep "$INTERVALO"
done
7.2. Integração com systemd/timer
# /etc/systemd/system/healthcheck.service
[Unit]
Description=Health Check Service
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/healthcheck.sh
Restart=always
RestartSec=30
[Install]
WantedBy=multi-user.target
# Timer para execução periódica
# /etc/systemd/system/healthcheck.timer
[Unit]
Description=Executa health check a cada 5 minutos
[Timer]
OnCalendar=*:0/5
Persistent=true
[Install]
WantedBy=timers.target
7.3. Testes e validação
# Teste de falha simulada
# Parar o serviço web
systemctl stop nginx
# Verificar se o alerta é gerado
./healthcheck.sh
# Deve gerar alerta: HTTP 000 ou conexão recusada
# Teste de recuperação
systemctl start nginx
./healthcheck.sh
# Deve retornar status OK
# Teste de limite de CPU
stress --cpu 4 --timeout 30 &
./healthcheck.sh
# Deve gerar alerta de CPU acima do limite
Referências
- Bash Guide for Beginners - Monitoring Scripts — Guia introdutório sobre scripts de monitoramento em Bash, incluindo loops e condições
- Linux Documentation Project - Advanced Bash Scripting: System Monitoring — Exemplos avançados de monitoramento de sistema com shell script
- Nagios Plugins Documentation — Documentação oficial dos plugins Nagios, referência para padrões de health check
- Curl Manual - Monitoring with Curl — Uso avançado do curl para health checks HTTP, incluindo métricas de tempo
- Systemd Service Monitoring — Documentação oficial do systemd para configuração de serviços e timers de monitoramento
- Logrotate Man Page — Gerenciamento de rotação de logs para scripts de monitoramento
- Bash Trap Command — Tratamento de sinais em scripts Bash para finalização graciosa