O monitoramento proativo da saúde dos discos rígidos (HDDs) e unidades de estado sólido (SSDs) é uma das responsabilidades mais críticas para qualquer administrador de sistemas. A falha de armazenamento não é apenas um inconveniente; ela representa risco direto à disponibilidade dos serviços, integridade dos dados e continuidade do negócio. Enquanto ferramentas gráficas e soluções enterprise oferecem dashboards completos, a automação via linha de comando com smartctl e scripts em Python proporciona uma camada leve, eficiente e altamente customizável para garantir que alertas sejam disparados antes que o hardware falhe catastróficamente.
Neste tutorial técnico, demonstraremos como integrar a ferramenta smartctl (parte do pacote smartmontools) com um script em Python para criar um sistema de monitoramento contínuo. O objetivo é extrair atributos SMART (Self-Monitoring, Analysis and Reporting Technology), interpretar os dados brutos e gerar alertas baseados em thresholds configuráveis.
1. Preparação do Ambiente e Dependências
Antes de escrever qualquer código, é fundamental garantir que o sistema operacional possua as ferramentas necessárias instaladas. O smartctl comunica-se diretamente com a interface SATA/SCSI/NVMe do disco para ler seus registros internos de saúde.
Instalação do smartmontools
A maioria das distribuições Linux modernas já traz o smartmontools nos repositórios padrão. Execute os comandos abaixo conforme sua distribuição:
# Para sistemas baseados em Debian/Ubuntu
sudo apt update
sudo apt install smartmontools -y
# Para sistemas baseados em RHEL/CentOS/Rocky
sudo yum install smartmontools -y
Após a instalação, verifique se o serviço está ativo e configurado para iniciar com o boot. Em muitos casos, o daemon smartd já monitora os discos em segundo plano, mas para nosso script customizado, executaremos o smartctl sob demanda.
Identificação dos Discos
Utilize o comando lsblk ou fdisk -l para identificar os dispositivos de bloco que você deseja monitorar. Geralmente, eles são nomeados como /dev/sda, /dev/nvme0n1, etc.
lsblk -d -o NAME,TYPE,SIZE,MODEL
Anote os nomes dos dispositivos alvo. Note que é necessário ter privilégios de root (ou usuário com permissões SUDO) para acessar esses dispositivos.
2. Entendendo a Saída do smartctl
Antes de automatizar, é crucial entender o formato de saída da ferramenta. O comando smartctl -a /dev/sdX retorna uma grande quantidade de informações. Para nosso script, focaremos em três áreas principais:
- Status Geral: Indicador booleano de se o disco está "PASSED" (Aprovado) ou falhou nos testes self-test.
- Atributos Críticos: Valores como
Reallocated_Sector_Ct,Current_Pending_SectoreOffline_Uncorrectable. - Temperatura: O atributo de temperatura é vital para prevenir degradação prematura.
Vamos testar a saída em formato JSON, que é muito mais fácil de parsear programaticamente do que o texto bruto padrão:
sudo smartctl -j /dev/sda
A opção -j força a saída em JSON. Isso elimina a necessidade de regex complexas para parsing de texto, reduzindo erros e aumentando a robustez do script.
3. Estruturação do Script Python
Criaremos um script Python modular que utiliza a biblioteca padrão subprocess para chamar o smartctl e a biblioteca json para interpretar os dados. Para simplificar, não utilizaremos frameworks externos pesados.
Bibliotecas Necessárias
O script utilizará apenas módulos padrão do Python 3:
subprocess: Para execução de comandos shell.json: Para parsing da saída JSON.logging: Para registrar eventos e erros de forma estruturada.time: Para controle de intervalos entre verificações.
Código Fonte do Monitor
Crie um arquivo chamado disk_monitor.py e insira o seguinte código. Este script é projetado para rodar em loop, verificando a saúde dos discos definidos na lista DISKS_TO_MONITOR.
import subprocess
import json
import logging
import time
import sys
# Configuração de Logging
logging.basicConfig(
filename='/var/log/disk_monitor.log',
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
# Configurações
DISKS_TO_MONITOR = ['/dev/sda', '/dev/nvme0n1']
CHECK_INTERVAL_SECONDS = 300 # Verifica a cada 5 minutos
ALERT_EMAIL = "[email protected]" # Placeholder para futura integração
# Thresholds de Alerta (Valores ABSOLUTOS ou atributos específicos)
THRESHOLDS = {
"temperature_max_celsius": 60,
"critical_warning": 1, # NVMe specific
"reallocated_sector_count_threshold": 0 # Se > 0, alerta
}
def get_smart_data(device):
"""
Executa smartctl e retorna os dados parseados em JSON.
"""
try:
# Utiliza sudo para garantir permissão de leitura
cmd = ["sudo", "smartctl", "-j", device]
# Executa o comando e captura a saída
result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
if result.returncode != 0:
logging.error(f"Falha ao executar smartctl em {device}: {result.stderr}")
return None
data = json.loads(result.stdout)
return data
except Exception as e:
logging.error(f"Erro inesperado ao obter dados de {device}: {str(e)}")
return None
def check_nvme_health(data):
"""
Verifica a saúde específica para discos NVMe.
"""
alerts = []
try:
critical_warning = data.get("nvme_smart_health_info_log", {}).get("critical_warning", 0)
if critical_warning >= THRESHOLDS["critical_warning"]:
alerts.append(f"ALERTA CRÍTICO NVMe em {device}: Critical Warning flag ativa.")
except KeyError:
pass
return alerts
def check_ata_health(data, device):
"""
Verifica a saúde específica para discos SATA/SAS (ATA).
"""
alerts = []
# 1. Verificar Status Global
global_health = data.get("smart_status", {}).get("passed", False)
if not global_health:
alerts.append(f"FALHA SMART GLOBAL em {device}: O disco passou no auto-teste? Não.")
# 2. Verificar Atributos Críticos
attributes = data.get("ata_smart_attributes", {}).get("table", [])
for attr in attributes:
name = attr.get("name", "")
value = attr.get("value")
# Foco em setores realocados
if "Reallocated_Sector_Ct" in name:
if value > THRESHOLDS["reallocated_sector_count_threshold"]:
alerts.append(f"ALERTA em {device}: Setores realocados ({name}) = {value}. Isso indica defeitos físicos.")
# Foco em setores pendentes (iminentes falhas)
if "Current_Pending_Sector" in name:
if value > 0:
alerts.append(f"ALERTA em {device}: Setores pendentes ({name}) = {value}. Leitura/Falha iminente.")
# 3. Verificar Temperatura
try:
# A estrutura do JSON varia ligeiramente entre fabricantes, mas geralmente está em temperature_sensor_1
temp_data = data.get("temperature", {}).get("current_temperature_celsius") or \
data.get("data_sets").get(2, {}).get("host_read_recovery") # Fallback genérico
# Tentativa mais robusta de pegar temperatura
if "temperature" in data:
temp = data["temperature"].get("current_temperature_celsius", 0)
if temp > THRESHOLDS["temperature_max_celsius"]:
alerts.append(f"ALERTA em {device}: Temperatura alta ({temp}°C). Limite é {THRESHOLDS['temperature_max_celsius']}°C.")
except Exception:
logging.warning("Não foi possível determinar a temperatura do disco %s", device)
return alerts
def main():
logging.info("Iniciando monitoramento de discos...")
while True:
for device in DISKS_TO_MONITOR:
print(f"Verificando {device}...")
data = get_smart_data(device)
if data is None:
continue
alerts = []
# Verificar se é NVMe ou ATA para usar o verificador correto
device_type = data.get("device", {}).get("name", "")
if "nvme" in device_type.lower():
alerts.extend(check_nvme_health(data))
else:
alerts.extend(check_ata_health(data, device))
if alerts:
alert_message = f"\n--- ALERTAS DE DISCO ---\nDisco: {device}\n" + "\n".join(alerts) + "\n------------------------"
print(alert_message)
logging.warning(alert_message)
# Aqui você integraria com seu sistema de notificação (Email, Slack, PagerDuty)
# send_notification(ALERT_EMAIL, alert_message)
print(f"Aguardando {CHECK_INTERVAL_SECONDS} segundos...")
time.sleep(CHECK_INTERVAL_SECONDS)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
logging.info("Monitoramento interrompido pelo usuário.")
sys.exit(0)
4. Explicação Técnica do Código
O script acima segue uma lógica clara de extração, validação e reporte.
Geração de JSON: A função get_smart_data executa o comando sudo smartctl -j /dev/sdX. O uso de -j é intencional. Sem ele, o smartctl retorna texto formatado para humanos, que requer parsing complexo e frágil (regex). O JSON estruturado permite acessar dados aninhados como data["ata_smart_attributes"]["table"] de forma segura.
Diferenciação de Protocolos: Discos SATA (ATA) e NVMe possuem estruturas de atributos diferentes. O script verifica o nome do dispositivo para decidir qual função de verificação chamar (check_ata_health ou check_nvme_health). Isso é essencial porque atributos como Critical_Warning são exclusivos do modelo NVMe, enquanto Reallocated_Sector_Ct é clássico em SATA.
Thresholds Configuráveis: O dicionário THRESHOLDS no topo do script permite que você ajuste os limites de alerta sem alterar a lógica principal. Por exemplo, se seus discos NVMe tiverem um limite de temperatura diferente, basta atualizar o valor.
Logging: Utilizamos o módulo logging em vez de apenas print(). Isso permite que os logs sejam redirecionados para arquivos ou agregadores centralizados (como ELK Stack ou Graylog) e evita que o script polua o terminal se rodar em background.
5. Execução e Daemonização
Para executar o script manualmente em primeiro plano (para testes):
sudo python3 disk_monitor.py
Observe a saída no terminal e verifique o arquivo /var/log/disk_monitor.log para entradas detalhadas.
Criando um Serviço Systemd
Para garantir que o monitoramento rode persistentemente mesmo após reinicializações, crie um arquivo de serviço systemd. Crie o arquivo /etc/systemd/system/disk-monitor.service:
[Unit]
Description=Disk Health Monitor using Smartctl and Python
After=network.target
[Service]
Type=simple
User=root
ExecStart=/usr/bin/python3 /opt/scripts/disk_monitor.py
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
Em seguida, ative e inicie o serviço:
sudo systemctl daemon-reload
sudo systemctl enable disk-monitor.service
sudo systemctl start disk-monitor.service
sudo systemctl status disk-monitor.service
Verifique os logs do serviço com journalctl -u disk-monitor.service -f.
6. Integração com Alertas (Próximos Passos)
O script atual registra alertas no log. Para um ambiente de produção, é recomendável integrar notificações reais.
- Email: Utilize a biblioteca
smtplibdo Python para enviar emails SMTP diretos. - Webhooks: Se sua empresa utiliza Slack, Discord ou Microsoft Teams, crie uma função que envie um POST JSON para o Webhook do canal de #alertas-infra.
- Sistemas de Monitoramento: Você pode configurar o script para emitir métricas compatíveis com Prometheus (Exposition Format) e permitir que o Grafana visualize a saúde dos discos ao longo do tempo.
7. Boas Práticas e Considerações Finais
Segurança: O script exige sudo. Certifique-se de que as permissões do sudoers estejam configuradas corretamente para permitir a execução apenas do smartctl, e não de qualquer comando, para evitar riscos de segurança.
Falsos Positivos: Alguns discos podem reportar temperaturas altas ocasionalmente devido a picos de carga da CPU. Implemente lógica de "debounce" (ex: alertar apenas se a temperatura alta persistir por 3 verificações consecutivas) para evitar alarmes desnecessários.
NVMe vs SATA: Lembre-se que SSDs NVMe têm mecanismos de desgaste diferentes. O atributo User_Content_Read_Only ou contagem de bytes escritos (Media_Errors_Corrected) pode ser mais relevante do que setores realocados em unidades NVMe.
A automação do monitoramento de disco transforma a administração de infraestrutura de reativa para proativa. Ao implementar este script Python com smartctl, você garante visibilidade total sobre a integridade física do seu armazenamento, permitindo planos de substituição ordenados e evitando surpresas desagradáveis em produção.
Para dúvidas ou ajustes específicos de configuração para seu ambiente, consulte a documentação oficial do smartmontools e adapte os thresholds conforme a tolerância ao risco da sua organização.