Monitoramento de serviços com scripts
1. Fundamentos do Monitoramento de Serviços no Bash
O monitoramento de serviços é uma prática essencial para garantir a disponibilidade e o funcionamento adequado de sistemas Linux. No contexto do Bash, isso envolve verificar periodicamente se processos, daemons ou aplicações estão rodando conforme o esperado. Os conceitos fundamentais incluem:
- Serviço: um processo que roda em background (daemon) ou uma aplicação gerenciada pelo systemd/init.
- Health check: verificação que confirma se o serviço está respondendo corretamente.
- Health check ativo: teste que simula uma requisição real ao serviço.
Comandos essenciais para esse trabalho são systemctl, service, pgrep e ps. A estrutura básica de um script de monitoramento consiste em um loop infinito ou execução periódica, uma verificação de status e uma ação (alerta, reinicialização, log).
#!/bin/bash
# Estrutura básica de monitoramento
SERVICE="nginx"
while true; do
if systemctl is-active --quiet "$SERVICE"; then
echo "$(date): $SERVICE está rodando."
else
echo "$(date): $SERVICE PAROU! Reiniciando..."
systemctl restart "$SERVICE"
fi
sleep 60
done
2. Verificando Status de Serviços do Sistema
O systemd oferece comandos diretos para consultar o estado de serviços. O comando systemctl is-active retorna active ou inactive, e seu código de retorno ($?) pode ser usado em condicionais. Exemplo prático:
#!/bin/bash
check_service() {
local service="$1"
if systemctl is-active --quiet "$service"; then
echo "OK: $service está ativo"
return 0
else
echo "FALHA: $service não está ativo"
return 1
fi
}
check_service "sshd"
check_service "apache2"
Para parsing mais detalhado, podemos usar systemctl status com grep e awk:
#!/bin/bash
get_service_status() {
local service="$1"
local status=$(systemctl status "$service" 2>/dev/null | grep "Active:" | awk '{print $2}')
echo "$service: $status"
}
get_service_status "postgresql"
get_service_status "redis-server"
O código de retorno ($?) é particularmente útil para decisões condicionais:
systemctl is-active --quiet "mysql"
if [ $? -ne 0 ]; then
echo "MySQL está inativo. Iniciando..."
systemctl start mysql
fi
3. Health Checks de Aplicações Web e TCP
Para serviços web, o curl é a ferramenta padrão para health checks HTTP/HTTPS. Podemos verificar códigos de status, tempos de resposta e conteúdo da resposta:
#!/bin/bash
URL="http://localhost:8080/health"
EXPECTED_CODE=200
TIMEOUT=10
http_code=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout "$TIMEOUT" "$URL")
if [ "$http_code" -eq "$EXPECTED_CODE" ]; then
echo "Health check OK: código $http_code"
else
echo "Health check FALHOU: código $http_code (esperado $EXPECTED_CODE)"
fi
Para verificações de porta TCP, podemos usar nc (netcat) ou o pseudo-dispositivo /dev/tcp do Bash:
#!/bin/bash
HOST="127.0.0.1"
PORT=3306
TIMEOUT=5
# Usando /dev/tcp
if timeout "$TIMEOUT" bash -c "echo > /dev/tcp/$HOST/$PORT" 2>/dev/null; then
echo "Porta $PORT está aberta"
else
echo "Porta $PORT está fechada ou inacessível"
fi
# Alternativa com nc
if nc -z -w "$TIMEOUT" "$HOST" "$PORT"; then
echo "Porta $PORT respondendo (nc)"
fi
Health checks mais robustos incluem retentativas e timeouts:
#!/bin/bash
health_check_with_retry() {
local url="$1"
local max_retries=3
local retry_delay=5
local attempt=1
while [ $attempt -le $max_retries ]; do
if curl -s -o /dev/null -w "%{http_code}" --connect-timeout 5 "$url" | grep -q "200"; then
echo "Health check bem-sucedido na tentativa $attempt"
return 0
fi
echo "Tentativa $attempt falhou. Aguardando $retry_delay segundos..."
sleep "$retry_delay"
attempt=$((attempt + 1))
done
echo "Health check falhou após $max_retries tentativas"
return 1
}
health_check_with_retry "http://localhost:3000/health"
4. Notificações e Alertas Automáticos
Notificações são cruciais para que administradores saibam de problemas imediatamente. O envio de e-mail pode ser feito com mail ou sendmail:
#!/bin/bash
send_email_alert() {
local subject="$1"
local body="$2"
local recipient="admin@exemplo.com"
echo "$body" | mail -s "$subject" "$recipient"
}
# Exemplo de uso
send_email_alert "ALERTA: Servidor Web Parou" "O serviço nginx parou inesperadamente em $(date)."
Para integração com Slack via webhook:
#!/bin/bash
WEBHOOK_URL="https://hooks.slack.com/services/SEU/WEBHOOK/AQUI"
send_slack_alert() {
local message="$1"
local payload="{\"text\": \"$message\"}"
curl -s -X POST -H "Content-Type: application/json" -d "$payload" "$WEBHOOK_URL"
}
send_slack_alert "🚨 *ALERTA CRÍTICO*: Serviço PostgreSQL parou em $(hostname) às $(date)"
Logging com timestamps e níveis de severidade:
#!/bin/bash
LOG_FILE="/var/log/monitoramento.log"
LOG_LEVEL=("DEBUG" "INFO" "WARN" "ERROR")
log_event() {
local level="$1"
local message="$2"
local timestamp=$(date "+%Y-%m-%d %H:%M:%S")
echo "[$timestamp] [$level] $message" >> "$LOG_FILE"
}
log_event "INFO" "Monitoramento iniciado"
log_event "WARN" "Uso de memória acima de 80%"
log_event "ERROR" "Serviço mysql não responde"
5. Estratégias de Reinicialização e Recuperação
Quando um serviço falha, o script pode tentar reiniciá-lo automaticamente. É importante implementar um backoff exponencial para evitar loops de reinicialização:
#!/bin/bash
SERVICE="apache2"
MAX_RETRIES=5
BASE_DELAY=10
restart_with_backoff() {
local attempt=1
local delay=$BASE_DELAY
while [ $attempt -le $MAX_RETRIES ]; do
echo "Tentativa $attempt de reiniciar $SERVICE..."
systemctl restart "$SERVICE"
sleep 5
if systemctl is-active --quiet "$SERVICE"; then
echo "$SERVICE reiniciado com sucesso na tentativa $attempt"
return 0
fi
echo "Falha na tentativa $attempt. Aguardando $delay segundos..."
sleep "$delay"
delay=$((delay * 2)) # Backoff exponencial
attempt=$((attempt + 1))
done
echo "Falha ao reiniciar $SERVICE após $MAX_RETRIES tentativas. Escalonando alerta..."
send_slack_alert "🔥 $SERVICE falhou após $MAX_RETRIES tentativas de reinicialização em $(hostname)"
return 1
}
restart_with_backoff
6. Monitoramento de Recursos (CPU, Memória, Disco)
O monitoramento de recursos complementa a verificação de serviços. Um serviço pode estar rodando mas travado por falta de memória:
#!/bin/bash
THRESHOLD_CPU=90
THRESHOLD_MEM=85
THRESHOLD_DISK=90
check_cpu() {
local usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
if (( $(echo "$usage > $THRESHOLD_CPU" | bc -l) )); then
log_event "WARN" "CPU em $usage% (threshold: $THRESHOLD_CPU%)"
return 1
fi
return 0
}
check_memory() {
local usage=$(free | grep Mem | awk '{print $3/$2 * 100.0}' | cut -d'.' -f1)
if [ "$usage" -gt "$THRESHOLD_MEM" ]; then
log_event "WARN" "Memória em $usage% (threshold: $THRESHOLD_MEM%)"
return 1
fi
return 0
}
check_disk() {
local usage=$(df / | tail -1 | awk '{print $5}' | sed 's/%//')
if [ "$usage" -gt "$THRESHOLD_DISK" ]; then
log_event "WARN" "Disco em $usage% (threshold: $THRESHOLD_DISK%)"
return 1
fi
return 0
}
# Integração com monitoramento de serviço
if ! check_memory; then
echo "Alta utilização de memória pode afetar serviços. Verificando serviços..."
if systemctl is-active --quiet "mysql"; then
log_event "INFO" "MySQL ainda está ativo, mas monitorando memória"
fi
fi
7. Automatização com Cron e Daemons
Para monitoramento contínuo sem loops infinitos, o cron é a solução mais simples:
# crontab -e
# Executar a cada 5 minutos
*/5 * * * * /usr/local/bin/monitor_servicos.sh >> /var/log/monitor_cron.log 2>&1
# Executar a cada hora no minuto 0
0 * * * * /usr/local/bin/health_check_completo.sh
Para scripts que precisam rodar como serviço systemd, crie um arquivo de unit:
[Unit]
Description=Monitor de Serviços Customizado
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/monitor_daemon.sh
Restart=always
RestartSec=30
User=root
Group=root
[Install]
WantedBy=multi-user.target
Salve em /etc/systemd/system/monitor.service e ative:
sudo systemctl daemon-reload
sudo systemctl enable monitor.service
sudo systemctl start monitor.service
Boas práticas importantes:
- Logs rotativos: use
logrotatepara evitar que logs cresçam infinitamente. - Lockfiles: evite execução simultânea do mesmo script:
#!/bin/bash
LOCKFILE="/var/run/monitor.pid"
if [ -f "$LOCKFILE" ] && kill -0 $(cat "$LOCKFILE") 2>/dev/null; then
echo "Script já está em execução"
exit 1
fi
echo $$ > "$LOCKFILE"
trap "rm -f $LOCKFILE" EXIT
# Código de monitoramento aqui
- Execução em background: para scripts longos, use
nohupou&:
nohup /usr/local/bin/monitor_servicos.sh &
Referências
- Guia do systemd para Administradores — Documentação oficial do systemctl com exemplos de uso para gerenciamento de serviços.
- Bash Scripting: Check if Service is Running — Tutorial prático sobre verificação de serviços em scripts Bash.
- Health Checks com curl — Documentação completa do curl, incluindo opções para health checks HTTP.
- Monitoramento com netcat (nc) — Guia de uso do netcat para verificações de porta e conectividade TCP.
- Cron How-To no Linux — Tutorial detalhado sobre agendamento de scripts com crontab.
- Criando serviços systemd customizados — Guia da DigitalOcean sobre criação de unidades systemd para scripts personalizados.
- Logrotate para gerenciamento de logs — Documentação do logrotate para rotação automática de arquivos de log.