Como Construí um Robô de Monitoramento que Funciona Enquanto Você Dorme — Com Shell Script e GitOps
Como Construí um Robô de Monitoramento que Funciona Enquanto Você Dorme — Com Shell Script e GitOps
Eu perdi uma madrugada inteira debugando um servidor que caiu às 3 da manhã. Não porque eu não tinha ferramenta de monitoramento. Eu tinha. O problema era que a ferramenta estava configurada no laptop do meu antigo sysadmin, e ele tinha saído da empresa há três meses.
Cenário comum? Absurdamente comum.
Hoje vou te mostrar como construirei — do zero — um robô de monitoramento robusto usando apenas Shell Script, Cron e GitOps. Sem ferramentas mágicas. Sem dashboards caríssimos. Sem aquela feeling de “espero que ninguém perceba que meu Nagios está quebrado há duas semanas”.
O barato funciona. E quando quebra, você sabe exatamente por quê.
Por que Shell Script Ainda é a Melhor Ferramenta para Monitoramento
Vamos ser honestos: a maioria das ferramentas de monitoramento modernos são construídas sobre camadas sobre camadas de abstrações. Você configura um agente, conecta a um dashboard, define alertas no painel administrativo, e quando algo quebra, você abre outro painel para ver os logs do painel que mostra o erro.
Shell Script não tem isso. Quando um script falha, você vê o erro. Ponto.
Outros motivos:
- Portabilidade: Funciona em qualquer sistema Unix-like. Aquele servidor RHEL 6 que ninguém quer atualizar? shell script roda lá sem drama.
- Debugabilidade: Adicione um
set -xno topo e você vê cada comando sendo executado. - Simplicidade operacional: Sem agentes para instalar, sem repositórios para configurar. Apenas um arquivo
.shque você versiona com Git. - Velocidade: Um script de monitoramento básico leva 5 minutos para escrever e 30 segundos para rodar.
A desvantagem? Você precisa pensar como automação. Mas essa é uma skill que se paga — literalmente.
Arquitetura: Os Três Pilares do Sistema
Antes de abrir o editor, vamos definir a arquitetura. O sistema tem três partes:
- Scripts de verificação: O que checa o serviço.
- Scripts de notificação: O que faz quando algo está errado.
- Scheduler (Cron): O que decide quando rodar cada coisa.
Simples assim. Não complique.
Passo 1: O Script de Verificação — Checando Serviços como um Pro
Crie o diretório onde tudo vai morar:
mkdir -p /opt/monitor/{scripts,logs,config}
cd /opt/monitor
git init
echo "*.log" > .gitignore
echo "config/*.env" >> .gitignore
O script principal que verifica serviços — chame de check-services.sh:
#!/bin/bash
# check-services.sh - Monitora serviços e dispara alertas
# Uso: ./check-services.sh [servico|all]
set -euo pipefail
LOGDIR="/opt/monitor/logs"
CONFIGDIR="/opt/monitor/config"
ALERT_SCRIPT="/opt/monitor/scripts/alert.sh"
# Cores para output (se alguém rodar manualmente)
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOGDIR/checks.log"
}
check_http() {
local url=$1
local name=$2
local expected_code=${3:-200}
response=$(curl -s -o /dev/null -w "%{http_code}" --max-time 30 "$url" 2>/dev/null)
if [[ "$response" == "$expected_code" ]]; then
log "[OK] $name ($url) → HTTP $response"
return 0
else
log "[FAIL] $name ($url) → HTTP $response (esperado: $expected_code)"
return 1
fi
}
check_port() {
local host=$1
local port=$2
local name=$3
if timeout 5 bash -c "cat < /dev/null > /dev/tcp/$host/$port" 2>/dev/null; then
log "[OK] $name ($host:$port) → disponível"
return 0
else
log "[FAIL] $name ($host:$port) → indisponível"
return 1
fi
}
check_process() {
local process_name=$1
local service_name=$2
if pgrep -x "$process_name" > /dev/null; then
log "[OK] $service_name → processo ativo (PID: $(pgrep -x $process_name))"
return 0
else
log "[FAIL] $service_name → processo não encontrado"
return 1
fi
}
send_alert() {
local service=$1
local message=$2
local severity=${3:-warning}
bash "$ALERT_SCRIPT" "$service" "$message" "$severity"
}
# === EXECUÇÃO PRINCIPAL ===
if [[ $# -eq 0 ]]; then
echo "Uso: $0 [servico|all]"
echo "Serviços: http, port, process, all"
exit 1
fi
SERVICE=${1:-all}
FAILED=0
log "======== INICIANDO VERIFICAÇÃO ($SERVICE) ========"
case $SERVICE in
http)
source "$CONFIGDIR/http-services.env"
for entry in "${HTTP_CHECKS[@]}"; do
# Formato: "nome|url|expected_code"
IFS='|' read -r name url expected <<< "$entry"
check_http "$url" "$name" "$expected" || { send_alert "$name" "HTTP check failed"; ((FAILED++)); }
done
;;
port)
source "$CONFIGDIR/port-services.env"
for entry in "${PORT_CHECKS[@]}"; do
IFS='|' read -r name host port <<< "$entry"
check_port "$host" "$port" "$name" || { send_alert "$name" "Port check failed"; ((FAILED++)); }
done
;;
process)
source "$CONFIGDIR/process-services.env"
for entry in "${PROCESS_CHECKS[@]}"; do
IFS='|' read -r name process <<< "$entry"
check_process "$process" "$name" || { send_alert "$name" "Process died"; ((FAILED++)); }
done
;;
all)
$0 http || ((FAILED++))
$0 port || ((FAILED++))
$0 process || ((FAILED++))
;;
*)
echo "Serviço desconhecido: $SERVICE"
exit 1
;;
esac
log "======== VERIFICAÇÃO COMPLETA (falhas: $FAILED) ========"
if [[ $FAILED -gt 0 ]]; then
exit 2
fi
exit 0
Sim, eu uso arrays associativos e sourcing de arquivos de config. Isso faz o script ficar maior, mas mais fácil de manter. Guarde a versão verbose para quando precisar debugar às 2 da manhã.
Passo 2: O Script de Alerta — Notificações que Realmente Chegam
Este é o alert.sh:
#!/bin/bash
# alert.sh - Sistema de alertas multi-canal
# Uso: ./alert.sh [servico] [mensagem] [severity]
SERVICE="$1"
MESSAGE="$2"
SEVERITY="${3:-info}"
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
# Arquivo de log de alertas
ALERT_LOG="/opt/monitor/logs/alerts.log"
# Telegram (configure TOKEN e CHAT_ID no config)
TELEGRAM_BOT_TOKEN="${TELEGRAM_BOT_TOKEN:-}"
TELEGRAM_CHAT_ID="${TELEGRAM_CHAT_ID:-}"
# Email (configure no config)
SMTP_HOST="${SMTP_HOST:-}"
SMTP_TO="${SMTP_TO:-}"
log_alert() {
echo "[$SEVERITY] [$TIMESTAMP] $SERVICE: $MESSAGE" >> "$ALERT_LOG"
}
send_telegram() {
if [[ -n "$TELEGRAM_BOT_TOKEN" && -n "$TELEGRAM_CHAT_ID" ]]; then
emoji="ℹ️"
case $SEVERITY in
critical) emoji="🚨" ;;
error) emoji="❌" ;;
warning) emoji="⚠️" ;;
esac
payload=$(cat < /dev/null 2>&1
fi
}
send_email() {
if [[ -n "$SMTP_HOST" && -n "$SMTP_TO" ]]; then
echo -e "Subject: [$SEVERITY] $SERVICE - $TIMESTAMP\n\n$MESSAGE" | \
sendmail -f "$SMTP_FROM" "$SMTP_TO" 2>/dev/null || true
fi
}
send_webhook() {
if [[ -n "$WEBHOOK_URL" ]]; then
curl -s -X POST "$WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d "{\"service\":\"$SERVICE\",\"message\":\"$MESSAGE\",\"severity\":\"$SEVERITY\",\"timestamp\":\"$TIMESTAMP\"}" \
> /dev/null 2>&1 || true
fi
}
# Executa todos os canais
log_alert
send_telegram
send_email
send_webhook
Aqui está o pulo do gato: eu não escolho um canal. Eu tento todos. Se o Telegram estiver fora, o email entra. Se o email falhar, o webhook funciona. redundância não é overengineering — é engenharia de produção.
Passo 3: Configuração — O Arquivo .env que Você Versiona
Crie /opt/monitor/config/http-services.env:
#!/bin/bash
# Configuração de checks HTTP
declare -a HTTP_CHECKS=(
"Automente Homepage|https://automente.com.br|200"
"WordPress API|https://automente.com.br/wp-json/wp/v2/posts|200"
"Pexels API|https://api.pexels.com/v1/search?query=test|200"
)
/opt/monitor/config/port-services.env:
#!/bin/bash
# Checks de porta TCP
declare -a PORT_CHECKS=(
"MySQL Local|localhost|3306"
"Redis Local|localhost|6379"
"SSH Server|localhost|22"
)
/opt/monitor/config/process-services.env:
#!/bin/bash
# Checks de processos
declare -a PROCESS_CHECKS=(
"Nginx|nginx"
"PHP-FPM|php-fpm"
"MySQL|mysqld"
)
🛠 O Perrengue: Na primeira vez que rodei isso em produção, o Cron disparou o alerta mas o Telegram BOT tinha sido desativado por falta de uso. Passei 20 minutos achando que era problema no script até perceber que simplesmente não recebi a mensagem. Solução: agora eu faço um "heartbeat" semanal que testa todos os canais de comunicação, não só os serviços monitorados.
Passo 4: Cron — O Maestro Silencioso
Agora o scheduling. Edite o crontab com crontab -e:
# Monitoramento AutoMente - GitOps Style
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
MAILTO=""
# Verificações HTTP a cada 5 minutos
*/5 * * * * /opt/monitor/scripts/check-services.sh http >> /opt/monitor/logs/cron.log 2>&1
# Verificações de porta a cada 5 minutos
*/5 * * * * /opt/monitor/scripts/check-services.sh port >> /opt/monitor/logs/cron.log 2>&1
# Verificações de processo a cada 2 minutos (mais frequente pois processos morrem rápido)
*/2 * * * * /opt/monitor/scripts/check-services.sh process >> /opt/monitor/logs/cron.log 2>&1
# Heartbeat semanal (testa canais de alerta)
0 9 * * 1 /opt/monitor/scripts/heartbeat.sh >> /opt/monitor/logs/heartbeat.log 2>&1
Nota: eu coloco o caminho completo em todos os comandos. Nunca confie que o PATH do cron é igual ao seu PATH interativo. Essa lição me custou 3 horas de debugging uma vez.
Passo 5: GitOps — Versionando Sua Infraestrutura de Monitoramento
A beleza do GitOps não está em usar Kubernetes — está em versionar configuração. O script abaixo sincroniza sua pasta de monitoramento com um repositório Git:
#!/bin/bash
# gitops-sync.sh - Sincroniza configuração com repositório
# Coloque no cron ou como webhook
REPO_DIR="/opt/monitor"
GIT_REMOTE="${GIT_REMOTE:-origin}"
BACKUP_BRANCH="backup/$(date '+%Y%m%d-%H%M%S')"
cd "$REPO_DIR" || exit 1
# Adiciona scripts e configs (não logs)
git add scripts/ config/
# Commit automático se houver mudanças
if git diff --staged --quiet; then
echo "[$(date)] Nenhuma mudança para commitar"
exit 0
fi
git commit -m "Autocommit: $(date '+%Y-%m-%d %H:%M') - $(hostname)"
# Push
git push "$GIT_REMOTE" main || {
# Se falhar, faz backup em branch
git checkout -b "$BACKUP_BRANCH"
git push "$GIT_REMOTE" "$BACKUP_BRANCH"
git checkout main
}
Com isso, você pode:
- Ver quem mudou a configuração e quando.
- Fazer rollback se uma mudança quebrar tudo.
- Duplicar a configuração em outro servidor com um
git clone. - Dormir tranquilo sabendo que a configuração do monitoramento está versionada — não depende de documentação desatualizada.
Passo 6: O Heartbeat — Descobrindo Problemas Antes deles te Descobrirem
O problema com sistemas de monitoramento é que eles não monitoram a si mesmos. Você precisa de um "meta-monitoramento". O heartbeat verifica se o sistema de monitoramento está vivo:
#!/bin/bash
# heartbeat.sh - Testa se os sistemas de alerta estão funcionando
ALERT_LOG="/opt/monitor/logs/alerts.log"
LAST_ALERT_FILE="/tmp/last-alert-check"
log() { echo "[$(date '+%Y-%m-%d %H:%M')] $1"; }
# Verifica se o log de alertas foi modificado nas últimas 25 horas
if [[ -f "$ALERT_LOG" ]]; then
LAST_MOD=$(stat -c %Y "$ALERT_LOG" 2>/dev/null || stat -f %m "$ALERT_LOG" 2>/dev/null)
NOW=$(date +%s)
AGE=$(( (NOW - LAST_MOD) / 3600 ))
if [[ $AGE -gt 25 ]]; then
log "[WARN] Nenhum alerta nos últimos $AGE horas — verificando se o sistema está rodando"
# Testa manualmente os checks
if ! /opt/monitor/scripts/check-services.sh all > /dev/null 2>&1; then
log "[ERROR] Checks estão falhando mas nenhum alerta foi disparado!"
fi
fi
fi
# Verifica se o cron está ativo
if ! pgrep -f "check-services.sh" > /dev/null; then
log "[CRITICAL] Cron não está rodando os checks! Reiniciando..."
# Recarrega crontab
crontab /var/spool/cron/crontabs/root 2>/dev/null || true
fi
# Testa Telegram (envia mensagem de teste)
TOKEN="${TELEGRAM_BOT_TOKEN}"
CHAT="${TELEGRAM_CHAT_ID}"
if [[ -n "$TOKEN" && -n "$CHAT" ]]; then
RESP=$(curl -s "https://api.telegram.org/bot$TOKEN/sendMessage" \
-d "chat_id=$CHAT" \
-d "text=🫀 Heartbeat OK — Monitoramento ativo em $(hostname) ($(date '+%H:%M'))" \
-d "parse_mode=HTML")
if echo "$RESP" | grep -q '"ok":true'; then
log "[OK] Telegram funcionando"
else
log "[ERROR] Telegram falhou: $RESP"
fi
fi
log "Heartbeat completo — sistema saudável"
🔧 Debugging na Prática: Uma vez o heartbeat me mandou um alerta de "Telegram falhou" às 9h da manhã. O bot estava funcionando perfeitamente — o problema era que eu tinha alterado o token no script mas não tinha exportado a variável de ambiente. O
echo "$RESP"me salvou: vi que o Telegram retornavaForbidden: bot was blocked by the user. Eu tinha bloqueado o bot sem querer no meu próprio Telegram. Desbloqueie e problema resolvido. Moral: sempre prints de debug nos seus scripts de alerta.
Testando Tudo: O Script que Simula Falhas
Antes de colocar em produção, você precisa garantir que os alertas realmente funcionam. Este script simula falhas de propósito:
#!/bin/bash
# chaos-monkey.sh - Simula falhas para testar o sistema de monitoramento
set -euo pipefail
echo "⚠️ ATENÇÃO: Isso vai simular falhas no sistema"
echo "Pressione Ctrl+C em 5 segundos para cancelar..."
sleep 5
# Simula alerta
/opt/monitor/scripts/alert.sh "CHAOS-TEST" "Simulação de falha - ingnore este alerta" "warning"
# Simula falha de HTTP (vai falhar propositalmente)
curl -s -o /dev/null -w "%{http_code}" --max-time 5 "http://localhost:99999/nonexistent" || true
echo "✅ Teste completo. Verifique se recebeu os alertas."
Colocando em Produção: A Checklist Final
Antes de fazer o deploy, rode esta checklist:
- Permissões: Scripts são executáveis?
chmod +x /opt/monitor/scripts/*.sh - Logs: Diretórios existem?
mkdir -p /opt/monitor/logs - Variáveis: Tokens e credenciais estão no environment? Teste com
echo $TELEGRAM_BOT_TOKEN - Cron: Está rodando?
grep check-services /var/log/cron.log - Git: Repositório está sincronizado?
git status - Chaos: Rodou o teste de falhas? Este passo é obrigatório.
Escalando: Quando um Servidor Não é Enough
Esse sistema funciona para 1-5 servidores. Acima disso, você vai querer:
- Inventário centralizado: Um arquivo
servers.confque lista todos os servidores e seus checks específicos. - Agente ringan: Ao invés de cron em cada máquina, um único servidor central que faz ssh em todos e coleta resultados.
- Métricas históricas: Integre com InfluxDB para gráficos de uptime/downtime.
Mas para a maioria dos projetos pessoais e pequenos negócios, um servidormonitorando a si mesmo é mais que suficiente. Eu conheço empresas pagando R$500/mês em ferramentas de monitoramento que poderiam usar exatamente isso que mostrei — e dariam menos dor de cabeça.
Conclusão: Monitore como se Importasse
A diferença entre um sysadmin que dorme à noite e um que acorda às 3h com ligações do cliente não é a ferramenta — é a atitude. Ferramenta boa ajuda, mas cultura de monitoramento é o que realmente importa.
Esse sistema não é bonito. Não tem dashboard colorido. Não integra com Slack oficialmente. Mas ele funciona. E quando algo quebra, você sabe exatamente o que está errado porque você escreveu o script, você definiu os critérios de sucesso, e você versionou tudo no Git.
Isso é Infraestrutura como Código. Isso é GitOps. Isso funciona.
🔔 E você? Qual serviço você gostaria de automatizar o monitoramento? Me conta nos comentários — pode ser algo simples como verificar se seu site está no ar, ou algo mais complexo como monitorar uso de disco e espaço em servidores remotos. Se houver interesse, o próximo artigo pode ser exatamente sobre isso: como expandir esse sistema para monitorar múltiplos servidores com relatórios diários por email.
Enquanto isso, vá lá e coloca pelo menos o basicão pra rodar. Sua noite de sono futura vai agradecer.
