Projeto: monitor de sistema
LidoNesta aula, você construirá um monitor de sistema completo em Shell Script, cobrindo coleta de métricas, geração de relatórios, alertas e execução periódica via cron. O projeto integra conceitos de variáveis, comandos de sistema, estruturas de controle e agendamento, resultando em uma ferramenta prática e útil para administração de servidores.
Bem-vindo à aula 93 do curso de Shell Script! Nesta aula, vamos aplicar tudo o que você aprendeu até agora para construir um monitor de sistema completo. Você vai criar um script que coleta métricas importantes do sistema (CPU, memória, disco, rede), gera relatórios legíveis, envia alertas quando algo está fora do normal e pode ser agendado para rodar periodicamente. Este é um projeto prático que demonstra como o Shell Script pode ser usado para automação de tarefas de administração de sistemas.
Vamos dividir o projeto em quatro partes principais: coleta de métricas, relatório, alertas e execução periódica. Em cada etapa, você verá exemplos de código e explicações detalhadas. Ao final, você terá um script robusto que pode ser adaptado para suas próprias necessidades. Vamos começar!
Coleta de métricas
A primeira etapa do nosso monitor é coletar métricas do sistema. Precisamos obter informações sobre o uso da CPU, memória, disco e rede. O Linux nos fornece várias ferramentas para isso, como top, free, df e cat /proc/stat. Vamos usar essas ferramentas para extrair os dados e armazená-los em variáveis para uso posterior.
Para a CPU, podemos usar o comando top em modo batch (top -bn1) para obter uma única amostra. A linha que começa com %Cpu(s) contém os percentuais de uso. Para simplificar, vamos extrair o valor de uso da CPU do usuário e do sistema. Para a memória, free -m nos dá valores em megabytes, e podemos extrair o total e o usado. Para o disco, df -h mostra o uso das partições, e podemos pegar a porcentagem de uso da partição raiz. Para a rede, podemos usar cat /proc/net/dev para obter bytes transmitidos e recebidos, mas vamos simplificar usando o comando ip ou ifconfig para obter estatísticas básicas.
Vamos escrever um script que coleta essas métricas e as armazena em variáveis. Veja o exemplo abaixo:
#!/bin/bash
# Coleta de métricas do sistema
# CPU - uso total (usuário + sistema)
cpu_line=$(top -bn1 | grep "Cpu(s)")
cpu_user=$(echo "$cpu_line" | awk -F' ' '{print $2}' | cut -d'%' -f1)
cpu_sys=$(echo "$cpu_line" | awk -F' ' '{print $4}' | cut -d'%' -f1)
cpu_total=$(echo "$cpu_user + $cpu_sys" | bc)
# Memória - total e usada em MB
mem_info=$(free -m | grep "Mem:")
mem_total=$(echo "$mem_info" | awk '{print $2}')
mem_used=$(echo "$mem_info" | awk '{print $3}')
mem_percent=$(echo "scale=2; ($mem_used/$mem_total)*100" | bc)
# Disco - uso da partição raiz (em %)
disk_percent=$(df -h / | awk 'NR==2 {print $5}' | cut -d'%' -f1)
# Rede - bytes recebidos e transmitidos (interface eth0)
net_rx=$(cat /proc/net/dev | grep eth0 | awk '{print $2}')
net_tx=$(cat /proc/net/dev | grep eth0 | awk '{print $10}')
# Exibir valores para verificação
echo "CPU: $cpu_total%"
echo "Memória: $mem_used/${mem_total}MB ($mem_percent%)"
echo "Disco: $disk_percent%"
echo "Rede RX: $net_rx bytes"
echo "Rede TX: $net_tx bytes"
No script acima, usamos comandos como grep, awk, cut e bc para processar a saída dos comandos do sistema. É importante notar que bc é usado para cálculos com ponto flutuante; se não estiver instalado, você pode usar awk para cálculos simples. A interface de rede pode variar (ex.: ens33, wlan0), então ajuste o grep conforme necessário.
Relatório
Depois de coletar as métricas, o próximo passo é gerar um relatório claro e organizado. O relatório pode ser exibido na tela ou salvo em um arquivo de log. Vamos criar uma função que formata as informações em uma saída amigável, incluindo data e hora. Também podemos adicionar um cabeçalho e separadores para facilitar a leitura.
No relatório, vamos incluir as métricas coletadas, além de informações como o nome do host e a data. Podemos usar $(hostname) para obter o nome do sistema e $(date) para a data/hora. Vamos criar um arquivo de log que pode ser consultado posteriormente.
Exemplo de função para gerar relatório:
#!/bin/bash
gerar_relatorio() {
local data=$(date "+%Y-%m-%d %H:%M:%S")
local host=$(hostname)
local relatorio=""
relatorio+="=============================\n"
relatorio+="Relatório do Sistema - $host\n"
relatorio+="Data: $data\n"
relatorio+="=============================\n"
relatorio+="CPU: $cpu_total%\n"
relatorio+="Memória: $mem_used/${mem_total}MB ($mem_percent%)\n"
relatorio+="Disco: $disk_percent%\n"
relatorio+="Rede RX: $net_rx bytes\n"
relatorio+="Rede TX: $net_tx bytes\n"
relatorio+="=============================\n"
echo -e "$relatorio"
}
# Chamar a função
gerar_relatorio
Para salvar o relatório em um arquivo, podemos redirecionar a saída: gerar_relatorio >> /var/log/monitor.log. Isso é útil para manter um histórico das métricas. Lembre-se de que o diretório de log deve ter permissões adequadas.
Alertas
Alertas são essenciais para um monitor de sistema: eles notificam o administrador quando uma métrica ultrapassa um limite pré-definido. Vamos definir limites para CPU, memória e disco (por exemplo, 80% de uso) e, caso sejam excedidos, enviar um alerta. O alerta pode ser um simples eco no terminal, um envio de e-mail ou até uma mensagem para um serviço de mensageria como Slack. Neste projeto, vamos implementar um alerta simples que exibe uma mensagem e pode ser facilmente adaptado para enviar e-mail.
Para o alerta, vamos usar if para verificar se o valor está acima do limite. Se estiver, chamamos uma função de alerta que pode exibir uma mensagem ou enviar um e-mail. Vamos criar uma função enviar_alerta que usa mail para enviar e-mail, mas você pode substituir por outro método. Vamos também registrar o alerta no log.
Exemplo de alerta:
#!/bin/bash
# Limites (em %)
LIMITE_CPU=80
LIMITE_MEM=80
LIMITE_DISCO=80
enviar_alerta() {
local assunto="[ALERTA] Monitoramento - $(hostname)"
local mensagem="$1"
echo "$mensagem" | mail -s "$assunto" admin@example.com
echo "ALERTA: $mensagem"
}
# Verificar limites
if (( $(echo "$cpu_total > $LIMITE_CPU" | bc -l) )); then
enviar_alerta "CPU acima de $LIMITE_CPU%: $cpu_total%"
fi
if (( $(echo "$mem_percent > $LIMITE_MEM" | bc -l) )); then
enviar_alerta "Memória acima de $LIMITE_MEM%: $mem_percent%"
fi
if (( disk_percent > LIMITE_DISCO )); then
enviar_alerta "Disco acima de $LIMITE_DISCO%: $disk_percent%"
fi
Neste exemplo, usamos bc para comparações com ponto flutuante. Para valores inteiros, a sintaxe (( ... )) funciona. Lembre-se de ajustar o e-mail para o seu destinatário.
Execução periódica
Para que o monitor funcione automaticamente, precisamos agendá-lo para rodar em intervalos regulares. A ferramenta padrão no Linux para isso é o cron. Podemos adicionar uma entrada no crontab para executar nosso script a cada minuto, a cada hora, etc. Vamos também garantir que o script tenha permissão de execução e que o caminho esteja correto.
Primeiro, vamos tornar o script executável: chmod +x monitor.sh. Depois, podemos editar o crontab com crontab -e e adicionar uma linha como:
*/5 * * * * /caminho/para/monitor.sh
Isso executa o script a cada 5 minutos. Para executar a cada minuto, use * * * * *. Vamos também garantir que o script tenha um shebang e que as variáveis de ambiente estejam definidas, pois o cron executa com um ambiente limitado.
Além do cron, podemos usar o watch para executar periodicamente em tempo real, mas o cron é mais adequado para automação. Vamos criar um script que, ao ser executado, coleta métricas, gera relatório, verifica alertas e, opcionalmente, envia o relatório por e-mail. Vamos combinar tudo em um script final.
Exemplo de script final integrado:
#!/bin/bash
# Configurações
LOG_FILE="/var/log/monitor.log"
EMAIL="admin@example.com"
LIMITE_CPU=80
LIMITE_MEM=80
LIMITE_DISCO=80
# Função para coletar métricas
coletar_metricas() {
cpu_line=$(top -bn1 | grep "Cpu(s)")
cpu_user=$(echo "$cpu_line" | awk -F' ' '{print $2}' | cut -d'%' -f1)
cpu_sys=$(echo "$cpu_line" | awk -F' ' '{print $4}' | cut -d'%' -f1)
cpu_total=$(echo "$cpu_user + $cpu_sys" | bc)
mem_info=$(free -m | grep "Mem:")
mem_total=$(echo "$mem_info" | awk '{print $2}')
mem_used=$(echo "$mem_info" | awk '{print $3}')
mem_percent=$(echo "scale=2; ($mem_used/$mem_total)*100" | bc)
disk_percent=$(df -h / | awk 'NR==2 {print $5}' | cut -d'%' -f1)
net_rx=$(cat /proc/net/dev | grep eth0 | awk '{print $2}')
net_tx=$(cat /proc/net/dev | grep eth0 | awk '{print $10}')
}
# Função para gerar relatório
gerar_relatorio() {
local data=$(date "+%Y-%m-%d %H:%M:%S")
local host=$(hostname)
local relatorio=""
relatorio+="=============================\n"
relatorio+="Relatório do Sistema - $host\n"
relatorio+="Data: $data\n"
relatorio+="=============================\n"
relatorio+="CPU: $cpu_total%\n"
relatorio+="Memória: $mem_used/${mem_total}MB ($mem_percent%)\n"
relatorio+="Disco: $disk_percent%\n"
relatorio+="Rede RX: $net_rx bytes\n"
relatorio+="Rede TX: $net_tx bytes\n"
relatorio+="=============================\n"
echo -e "$relatorio"
}
# Função para enviar alerta
enviar_alerta() {
local assunto="[ALERTA] Monitoramento - $(hostname)"
local mensagem="$1"
echo "$mensagem" | mail -s "$assunto" "$EMAIL"
echo "ALERTA: $mensagem" >> "$LOG_FILE"
}
# Função para verificar limites
verificar_alertas() {
if (( $(echo "$cpu_total > $LIMITE_CPU" | bc -l) )); then
enviar_alerta "CPU acima de $LIMITE_CPU%: $cpu_total%"
fi
if (( $(echo "$mem_percent > $LIMITE_MEM" | bc -l) )); then
enviar_alerta "Memória acima de $LIMITE_MEM%: $mem_percent%"
fi
if (( disk_percent > LIMITE_DISCO )); then
enviar_alerta "Disco acima de $LIMITE_DISCO%: $disk_percent%"
fi
}
# Execução principal
coletar_metricas
relatorio=$(gerar_relatorio)
echo "$relatorio" >> "$LOG_FILE"
verificar_alertas
Este script pode ser agendado no cron. Lembre-se de ajustar o caminho do script e as permissões. Para testar, execute manualmente e veja o resultado no log.
Boas práticas e observações finais
Ao desenvolver scripts de monitoramento, é importante seguir algumas boas práticas: sempre usar aspas em variáveis para evitar problemas com espaços, tratar erros de comandos (por exemplo, verificar se a interface de rede existe), e usar caminhos absolutos no cron. Além disso, é recomendável configurar um log rotativo para não crescer indefinidamente. Você pode usar logrotate para isso.
Outra dica: use set -e para sair em caso de erro, mas cuidado com comandos que podem falhar em condições normais. Adapte os limites de alerta conforme a necessidade do seu ambiente. E lembre-se de testar o script em um ambiente controlado antes de colocá-lo em produção.
Exercícios
- Modifique o script para usar a interface de rede correta do seu sistema (ex.: ens33, wlan0). Teste a coleta de rede e ajuste o
grep. - Adicione uma métrica de temperatura da CPU (se disponível) usando
sensorsoucat /sys/class/thermal/thermal_zone0/temp. Inclua essa métrica no relatório. - Implemente um limite para a rede (por exemplo, se RX exceder 1GB em um intervalo, envie alerta). Você precisará armazenar o valor anterior para calcular a diferença.
- Altere o script para que o relatório seja enviado por e-mail diariamente, mesmo sem alertas. Use o cron para agendar uma execução especial.
- Adicione um mecanismo de bloqueio para evitar que o script seja executado simultaneamente (ex.: usando
flock).
ip link show para listar as interfaces e identifique a interface ativa (geralmente a que tem estado UP). No script, substitua eth0 pelo nome da sua interface. Por exemplo, se a interface for ens33, use grep ens33. Você pode também usar grep -v lo para ignorar a interface de loopback.
temp=$(cat /sys/class/thermal/thermal_zone0/temp 2>/dev/null) e divida por 1000 para obter graus Celsius: temp_c=$(echo "scale=2; $temp/1000" | bc). Adicione a variável ao relatório: echo "Temperatura: $temp_c°C". Se sensors estiver instalado, você pode usar sensors | grep Package.
prev_rx=$(cat /tmp/rx_anterior 2>/dev/null || echo 0), depois calcule a diferença: rx_diff=$((net_rx - prev_rx)). Se rx_diff exceder o limite, envie alerta. Salve o valor atual: echo "$net_rx" > /tmp/rx_anterior. Lembre-se de inicializar o arquivo na primeira execução.
--email. No script, verifique se o argumento é --email e, se for, envie o relatório por e-mail usando mail. Por exemplo: if [[ "$1" == "--email" ]]; then echo "$relatorio" | mail -s "Relatório diário" "$EMAIL"; fi. Agende no cron: 0 8 * * * /caminho/monitor.sh --email.
exec 9>/var/lock/monitor.lock e flock -n 9 para tentar obter o lock. Se não conseguir, saia. Exemplo: if ! flock -n 9; then echo "Script já em execução"; exit 1; fi. Isso evita que múltiplas instâncias rodem ao mesmo tempo.