Учет дискового пространства ломается не в момент переполнения, а задолго до него: когда на тренды никто не смотрит. Рабочий ответ на эту проблему собирается из трех слоев: сбор данных (Bash или Python плюс REST API самой системы хранения), передача метрик в Zabbix или Prometheus и автоматические алерты с контролем того, что задание вообще запустилось.
Дальше разобраны готовые скрипты, примеры запросов к API TrueNAS и Synology, конфигурации экспортеров и планировщиков. Материал рассчитан на DevOps-инженеров и системных администраторов, у которых уже есть Linux-серверы, NAS или СХД, и которым нужно получать цифры о свободном месте без ручных обходов.
Минимальный набор, закрывающий большинство задач: скрипт инвентаризации, запуск по расписанию через systemd timer или cron, отправка метрик в Zabbix trapper либо в файл textfile collector для node_exporter, и два алерта - на заполнение выше порога и на отсутствие данных.
Зачем автоматизировать учет хранилищ и какие задачи это решает
Ручной обход хранилищ работает до первого роста инфраструктуры. Пока серверов пять, инженер помнит, что пул tank на NAS заполнен на 70%. Когда серверов становится пятьдесят, а пулов сто, память перестает быть инструментом, и данные о свободном месте устаревают быстрее, чем их успевают собрать. Автоматизация переводит учет в режим непрерывного процесса: метрики снимаются по расписанию, попадают в хранилище временных рядов, а отклонения превращаются в алерты без участия человека.
Конкретные задачи, которые закрывает автоматизация учета: контроль заполнения разделов и пулов, отслеживание inode, детект деградации дисков по SMART, аудит роста данных по проектам и датасетам, подготовка отчетов для руководства и заказчиков. Каждая из этих задач без автоматизации превращается в регулярную ручную работу, а регулярная ручная работа рано или поздно пропускается.
Риски ручного мониторинга и цена ошибки
Uptime Institute в ежегодных отчетах по авариям в дата-центрах устойчиво относит около 40% серьезных простоев к ошибкам персонала. Отказ оборудования предсказуем по метрикам, человеческий фактор в схеме ручных проверок нет.
Сценарий из практики: администратор проверял бэкап-сервер по пятницам, но раздел /var вырос из-за разросшихся логов PostgreSQL. Инкрементный бэкап упал в ночь на среду, следующая успешная копия появилась только через три дня, а восстановление после сбоя основного массива растянулось на двое суток поиска актуального среза данных. Ни одни из этих суток не потребовались бы, если бы триггер на заполнение /var и мониторинг кода возврата rsync работали автоматически.
Второй частый случай: диск в RAID-массиве вышел из строя вечером в пятницу, письмо от контроллера ушло на адрес сотрудника в отпуске, а к понедельнику деградировал второй диск. Массив из пяти дисков с одним горячим резервом переживает один отказ, два отказа подряд приводят к потере данных. Пятьдесят часов без реакции - это норма для схемы, где уведомления зависят от человека.
Три подхода: скрипты, REST API, агенты мониторинга
Скрипты на Bash и Python дают максимальную гибкость: они видят локальные диски, точки монтирования, ZFS-датасеты и любые команды вендора. Плата за гибкость - ручная интеграция с системами мониторинга и ответственность за обработку ошибок на своей стороне.
REST API систем хранения отдают то, что снаружи не видно: состояние пулов, износ SSD, статус ресинхронизации, очередь операций. TrueNAS, Synology, NetApp ONTAP, Dell PowerStore и большинство современных СХД предоставляют HTTP-интерфейс с JSON. Ограничение - привязка к вендору и версии прошивки: путь, работающий на TrueNAS 24.04, может измениться в следующем релизе.
Агенты мониторинга и экспортеры (Zabbix agent, node_exporter, smartctl_exporter, zfs_exporter) закрывают стандартные метрики без написания кода. Zabbix agent 2 с плагинами и node_exporter с набором коллекторов покрывают нагрузку, inode, температуру и SMART. Там, где нужны метрики конкретного пула или бизнес-показатель, экспортеры дополняют скриптами.
Отдельный канал - SNMP. Старые СХД, ленточные библиотеки и коммутаторы Storage Area Network отдают метрики по SNMP, и Zabbix умеет опрашивать их готовыми шаблонами. Для устройств без API и без возможности поставить агент SNMP остается единственным вариантом.
Практическое правило: локальные диски и файловые системы - скрипт плюс textfile collector; NAS и СХД - REST API или готовый экспортер; устаревшее оборудование - SNMP.
Сбор данных о дисковом пространстве с помощью Bash и Python
Сбор начинается с понимания, какие команды дают нужные цифры. df показывает заполнение файловых систем, df -i расход inode, du -x размер каталогов без выхода на другие ФС, lsblk структуру блочных устройств, smartctl -H здоровье диска, zpool list и zfs list состояние пулов и датасетов ZFS. Каждая из этих команд возвращает машиночитаемый формат при правильных ключах, парсить человекочитаемый вывод не требуется.
Bash-скрипт для инвентаризации дисков: df, du, lsblk
Скрипт собирает заполнение локальных ФС в JSON, пропускает виртуальные файловые системы и помечает точки монтирования выше порога. Ключи -P и -x дают стабильный формат вывода и убирают tmpfs.
#!/usr/bin/env bash
set -euo pipefail
THRESHOLD="${THRESHOLD:-80}"
OUT_DIR=/var/lib/storage-inventory
OUT="${OUT_DIR}/disk_$(date +%Y%m%d_%H%M).json"
mkdir -p "$OUT_DIR"
df -hP -x tmpfs -x devtmpfs -x overlay | awk -v t="$THRESHOLD" '
NR == 1 { next }
{
pct = $5; gsub(/%/, "", pct)
status = (pct >= t) ? "warn" : "ok"
printf "{\"device\":\"%s\",\"size\":\"%s\",\"used\":\"%s\",\"avail\":\"%s\",\"use_pct\":%s,\"mount\":\"%s\",\"status\":\"%s\"}\n", $1, $2, $3, $4, pct, $6, status
}' | paste -sd, - | sed 's/^/[/; s/$/]/' > "$OUT"
zpool list -H -o name,size,alloc,free,cap,health 2>/dev/null \
> "${OUT_DIR}/zpool_$(date +%Y%m%d_%H%M).txt" || true
echo "written: $OUT"
Для ZFS-пулов ключевые поля - CAP (заполнение в процентах) и HEALTH (ONLINE, DEGRADED, FAULTED). Датасеты удобнее снимать командой zfs list -H -p -o name,used,avail,refer,mountpoint: флаг -p отключает округление и дает байты. На системах с сотнями датасетов вывод в JSON лучше формировать через jq или Python, awk с экранированием имен начинает ошибаться на пробелах и кавычках.
SMART добавляется отдельной строкой на устройство. Проверка smartctl -H /dev/sda возвращает строку с результатом, но надежнее читать атрибуты через smartctl -A -j (JSON-вывод доступен в smartmontools 7.0 и новее): поля 5 (Reallocated_Sector_Ct), 197 (Current_Pending_Sector) и 198 (Offline_Uncorrectable) дают ранний сигнал деградации.
Python-скрипт для проверки места на дисках: psutil и subprocess
Python удобнее там, где нужна логика: сравнение с порогами по типу сервиса, отправка в несколько систем, фильтрация по списку исключений. Функции psutil.disk_partitions() и psutil.disk_usage() работают кроссплатформенно и не требуют парсинга.
#!/usr/bin/env python3
import json
import logging
from datetime import datetime, timezone
import psutil
THRESHOLD = 80
SKIP_FS = {"squashfs", "tmpfs", "devtmpfs", "overlay"}
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)s %(message)s",
)
log = logging.getLogger("storage")
def collect():
rows = []
for part in psutil.disk_partitions(all=False):
if part.fstype in SKIP_FS:
continue
try:
usage = psutil.disk_usage(part.mountpoint)
except (PermissionError, FileNotFoundError, OSError) as exc:
log.warning("skip %s: %s", part.mountpoint, exc)
continue
rows.append({
"device": part.device,
"mountpoint": part.mountpoint,
"fstype": part.fstype,
"total_gb": round(usage.total / 1024 ** 3, 2),
"used_pct": usage.percent,
"status": "warn" if usage.percent >= THRESHOLD else "ok",
})
return rows
if __name__ == "__main__":
payload = {
"collected_at": datetime.now(timezone.utc).isoformat(),
"mounts": collect(),
}
print(json.dumps(payload, ensure_ascii=False, indent=2))
Функция shutil.disk_usage(path) дает те же три числа (total, used, free) без зависимости от psutil, что полезно в минимальных контейнерах. Внешние команды вызывайте через subprocess.run с аргументом-списком и check=True, без shell=True: так исключаются проблемы с пробелами в путях и подстановка символов оболочки.
Обработка ошибок и логирование в скриптах
Скрипт, который падает молча, хуже отсутствия скрипта, потому что создает ложное чувство контроля. В Bash за это отвечает set -euo pipefail: -e останавливает выполнение при ошибке, -u ловит необъявленные переменные, -o pipefail переносит код возврата последней команды конвейера на весь конвейер. Без pipefail ошибка внутри awk в середине конвейера не остановит скрипт.
В Python исключения обрабатываются точечно. Перехватывать Exception целиком стоит только на верхнем уровне, где выполняется логирование и отправка алерта. Для критичных операций задавайте ненулевой код возврата: sys.exit(2) при превышении порога позволяет внешнему планировщику отличить нормальную работу от проблемы.
Логирование: в Bash добавьте exec > >(logger -t storage-inventory) 2>&1, и сообщения уйдут в syslog, откуда их заберет journald или rsyslog. В Python настройте RotatingFileHandler с лимитом 10 МБ и пятью файлами, чтобы логи не съели тот самый диск, состояние которого они контролируют. Ротацию через logrotate применяйте к отдельным файлам с параметрами size 10M и rotate 5.
Готовые примеры скриптов с проверкой кодов возврата, отправкой уведомлений и контролем целостности данных собраны в материале про автоматизацию резервного копирования и восстановления: там же разобран тестовый стенд для проверки процедур восстановления до реального инцидента.
Работа с REST API СХД и NAS для получения метрик
API систем хранения отдают метрики, которых нет в df: состояние пулов, счетчики ошибок чтения и записи, температуру, ресурс SSD, статус ресинхронизации, очереди операций. Опрос строится по одной схеме: получить токен или использовать Basic Auth, запросить эндпоинт, разобрать JSON, привести метрики к своему формату.
Аутентификация и выбор эндпоинтов в API СХД
TrueNAS SCALE использует API-ключи: ключ создается в интерфейсе и передается в заголовке Authorization: Bearer. Вход по логину и паролю через POST /api/v2.0/auth/login в свежих релизах отключен, поэтому новые скрипты сразу пишите под ключи. Ключи храните в переменных окружения или в секрет-хранилище, не в тексте скрипта и не в git.
Synology работает по другому принципу: сначала GET /webapi/auth.cgi?api=SYNO.API.Auth&version=6&method=login&account=...&passwd=...&session=Storage&format=sid возвращает sid, который передается в последующих запросах. Пароль в URL попадает в логи веб-сервера, поэтому используйте HTTPS, отдельную сервисную учетную запись с правами только на чтение и, где возможно, POST вместо GET.
NetApp ONTAP и Dell PowerStore предоставляют REST API с версионированием. У ONTAP метрики томов снимаются запросом GET /api/storage/volumes?fields=space, у Dell набор путей зависит от линейки (PowerStore, PowerScale, Unity) и версии прошивки: перед написанием скрипта открывайте встроенный API explorer на самой системе, он показывает актуальные пути и схемы ответов для вашей версии.
Примеры запросов к TrueNAS и Synology API
import os
import requests
TRUENAS = os.environ["TRUENAS_URL"]
TOKEN = os.environ["TRUENAS_TOKEN"]
session = requests.Session()
session.headers.update({"Authorization": f"Bearer {TOKEN}"})
session.verify = "/etc/ssl/certs/internal-ca.pem"
pools = session.get(f"{TRUENAS}/api/v2.0/pool", timeout=10).json()
for pool in pools:
print(pool["name"], pool.get("healthy"), pool["size"], pool["allocated"])
Датасеты в TrueNAS отдаются эндпоинтом /api/v2.0/pool/dataset с полями used и available по каждому датасету, отдельный запуск df внутри NAS не нужен. У Synology список томов возвращает /webapi/entry.cgi?api=SYNO.Core.Storage.Volume&version=1&method=list с sid в параметрах, ответ содержит total_size, used_size и статус тома в байтах.
Частота опроса зависит от вендора и нагрузки. Разумный старт: раз в 5 минут для заполнения и раз в час для тяжелых списков датасетов. Опрос раз в 10 секунд создает нагрузку на управляющий контроллер СХД и приводит к ответам 429.
Обработка ошибок API и повторные попытки
import logging
import time
import requests
log = logging.getLogger("storage-api")
def get_json(url, session, attempts=4, timeout=10):
delay = 2
for attempt in range(1, attempts + 1):
try:
resp = session.get(url, timeout=timeout)
if resp.status_code in (429, 502, 503, 504):
raise requests.HTTPError(f"retryable status {resp.status_code}")
resp.raise_for_status()
return resp.json()
except (requests.Timeout, requests.ConnectionError, requests.HTTPError) as exc:
if attempt == attempts:
log.error("giving up on %s after %d attempts: %s", url, attempt, exc)
raise
log.warning("attempt %d failed for %s: %s", attempt, url, exc)
time.sleep(delay)
delay *= 2
Библиотека tenacity делает то же самое декоратором с параметрами stop_after_attempt(4) и wait_exponential(multiplier=2, max=30). Ручной цикл предпочтительнее там, где нужно различать ошибки: 401 и 403 повторять бессмысленно, истекший токен надо перевыпустить, а 429 и 5xx требуют паузы.
Отдельное правило: отсутствие ответа API дольше 15 минут должно поднимать алерт. Система хранения с недоступным управляющим интерфейсом теряет управляемость, и молчание метрики в этом случае опаснее высокого значения.
Интеграция метрик хранилищ с Zabbix и Prometheus
Собранные цифры бесполезны, пока лежат в файле на сервере. Их нужно доставлять в систему мониторинга, где уже настроены дашборды, история и дежурства.
Передача метрик в Zabbix: zabbix_sender и UserParameter
Два способа доставить свои метрики в Zabbix: активная отправка через zabbix_sender в trapper-элементы и UserParameter в конфиге агента. Первый подходит скриптам по расписанию, второй - опросу по требованию.
zabbix_sender -z zbx.example.net -s nas01 \ -k 'disk.usage[/volume1]' -o 87 -vv
В интерфейсе Zabbix создается элемент данных типа Zabbix trapper с ключом disk.usage[/volume1] и числовым типом значения. Вариант с UserParameter выглядит так:
UserParameter=storage.disk.usage[*],/usr/local/bin/disk_usage.sh "$1"
Для десятков серверов активная отправка предпочтительнее: агент не опрашивается лишний раз, а скрипт сам контролирует таймаут. При отправке с нескольких источников добавляйте имя узла и метку времени, иначе значение привяжется к узлу, указанному в конфиге агента по умолчанию.
Экспорт метрик в Prometheus: textfile collector и pushgateway
node_exporter с флагом --collector.textfile.directory=/var/lib/node_exporter/textfile_collector читает все файлы .prom в каталоге и отдает их как обычные метрики. Скрипт пишет файл атомарно: сначала во временный, затем mv.
# HELP disk_usage_percent Заполнение точки монтирования, проценты
# TYPE disk_usage_percent gauge
disk_usage_percent{mountpoint="/",fstype="ext4",host="web01"} 62
disk_usage_percent{mountpoint="/var/log",fstype="xfs",host="web01"} 88
# HELP pool_capacity_percent Заполнение ZFS-пула, проценты
# TYPE pool_capacity_percent gauge
pool_capacity_percent{pool="tank",host="nas01"} 74
Имя метрики должно соответствовать регулярному выражению [a-zA-Z_:][a-zA-Z0-9_:]*, а значения меток не могут содержать кириллицу, точки и дефисы, заменяйте их на подчеркивания. Ошибка в одной строке делает невалидным весь файл, поэтому перед записью проверяйте результат утилитой promtool check metrics.
Для задач, которые выполняются минуты и завершаются, textfile collector неудобен: файл может устареть. Pushgateway принимает метрики от короткоживущих заданий:
curl --data-binary @/var/lib/storage-inventory/disk.prom \ http://pushgateway:9091/metrics/job/storage_inventory/instance/nas01
У pushgateway есть известное свойство: метрики хранятся до перезаписи или удаления, поэтому для gauge-значений заполнения он подходит, а для счетчиков времени работы нет. После успешной отправки удаляйте группу через DELETE на тот же путь, если задание разовое.
Настройка алертов на критические события
В Zabbix триггер на заполнение описывается выражением last(/nas01/disk.usage[/volume1])>90 с макросами {$DISK_WARN}=85 и {$DISK_CRIT}=92. Два уровня важности позволяют не будить дежурного на 86%.
groups:
- name: storage
rules:
- alert: DiskUsageHigh
expr: disk_usage_percent > 85
for: 15m
labels:
severity: warning
annotations:
summary: "Заполнение {{ $labels.mountpoint }} на {{ $labels.host }} выше 85%"
- alert: DiskUsageCritical
expr: disk_usage_percent > 92 or pool_capacity_percent > 90
for: 5m
labels:
severity: critical
Порог 85% для warning и 92% для critical выбран не случайно: ZFS заметно теряет скорость записи при заполнении выше 80%, а запас на миграцию данных нужен до 90%. Для разделов с логами пороги сдвигают вниз, до 75% и 85%, потому что рост там быстрый.
Маршрутизация уведомлений: в Zabbix действия (Actions) привязываются к уровню важности, в Prometheus маршруты описываются в Alertmanager через route и receiver. Схемы с уведомлениями в Telegram и email, настройкой smartd и порогами по температуре разобраны в руководстве про автоматический мониторинг дисков с уведомлениями.
Ложные срабатывания лечатся параметром for в Prometheus и условием длительности в Zabbix. Если алерт приходит на каждое краткое превышение, дежурный перестает на них реагировать, и это опаснее отсутствия алертов.
Планирование заданий и обеспечение отказоустойчивости
Скрипт без расписания не работает. Два стандартных механизма в Linux, cron и systemd timers, покрывают почти все сценарии.
Cron vs systemd timers: что выбрать
Cron живет одной строкой: */5 * * * * /usr/local/bin/disk_check.sh. Его слабые места - минимальное окружение (PATH, LANG, HOME отличаются от интерактивной оболочки) и отсутствие встроенного логирования. Указывайте абсолютные пути и прописывайте PATH в самом скрипте.
systemd timers дают журнал через journalctl, зависимости, ограничения по ресурсам и RandomizedDelaySec для разнесения запусков по десяткам серверов.
# /etc/systemd/system/storage-inventory.service [Unit] Description=Инвентаризация дискового пространства After=network-online.target [Service] Type=oneshot User=storage-mon ExecStart=/usr/local/bin/storage_inventory.py Nice=10 IOSchedulingClass=idle
# /etc/systemd/system/storage-inventory.timer [Unit] Description=Запуск инвентаризации каждые 5 минут [Timer] OnBootSec=2min OnUnitActiveSec=5min RandomizedDelaySec=30 Persistent=true [Install] WantedBy=timers.target
Флаг Persistent=true закрывает пропущенные запуски после перезагрузки или простоя. Активация выполняется командой systemctl enable --now storage-inventory.timer, проверка расписания - systemctl list-timers.
Предотвращение одновременного запуска и блокировки
Длительный опрос API или обход каталогов через du может идти дольше интервала расписания. Без блокировки второй экземпляр стартует поверх первого, и два процесса начинают писать в один файл.
flock -n /var/lock/storage_inventory.lock \ -c '/usr/local/bin/storage_inventory.py'
Флаг -n завершает процесс сразу, если блокировка занята, а код возврата 1 сигнализирует, что предыдущий запуск еще идет. systemd не запускает второй экземпляр того же юнита, поэтому для timer-схемы блокировка нужна только при вызове скрипта и по cron, и вручную. В Python ту же задачу решают fcntl.flock или библиотека filelock.
Мониторинг самого задания: как узнать, что скрипт не выполнился
Отсутствие метрики и нулевое значение - разные вещи. Если скрипт не запустился, Zabbix покажет последнее старое значение, а Prometheus продолжит отдавать устаревшую точку до истечения окна поиска (по умолчанию 5 минут).
В Zabbix узел получает heartbeat-элемент типа Zabbix trapper, скрипт отправляет его при каждом успешном запуске, а триггер nodata(15m)=1 поднимает проблему. В Prometheus применяют absent() по нужной серии или сравнение timestamp() с текущим временем.
- alert: StorageInventoryMissing
expr: absent(pool_capacity_percent{job="node"}) or (time() - timestamp(pool_capacity_percent{job="node"}) > 900)
for: 5m
labels:
severity: critical
annotations:
summary: "Метрики хранилищ не обновлялись более 15 минут"
Такой dead man's switch закрывает главный риск автоматизации: тихий отказ, при котором система мониторинга выглядит здоровой, а данные о хранилищах приходят из прошлой недели.
Сравнение подходов и рекомендации по выбору
Выбор между подходами сводится к трем параметрам: число серверов и хранилищ, требуемая детализация метрик и готовность поддерживать код.
| Подход | Плюсы | Минусы | Когда выбирать |
|---|---|---|---|
| Bash + cron | Нет зависимостей, работает на любом Linux, легко читается | Слабая обработка ошибок, сложный парсинг JSON | До 20-30 серверов, простые метрики заполнения |
| Python + REST API | Логика, обработка исключений, удобная работа с JSON | Нужен интерпретатор и зависимости, больше кода | Разнородная инфраструктура, отчеты, сложные пороги |
| REST API СХД и NAS | Детальные метрики пулов, дисков, ресурса SSD | Привязка к вендору и версии прошивки | NAS и СХД с HTTP-интерфейсом |
| SNMP | Работает на старом оборудовании | Устаревшие MIB, редкий набор метрик | Устройства без API и агента |
| Агенты и экспортеры | Готовые дашборды и шаблоны, минимум кода | Ограничены стандартным набором метрик | Базовый мониторинг, быстрый старт |
Для домашней лаборатории или компании с пятью серверами достаточно Bash-скрипта, cron и Zabbix agent: настройка занимает около часа и дает алерты на заполнение и SMART. Для парка из 50-200 серверов и нескольких NAS выгоднее Python с REST API и Prometheus: единый формат метрик, готовые дашборды Grafana, отсутствие ручной привязки узлов. Крупные инсталляции с СХД строят схему из вендорских экспортеров, долгого хранения метрик в VictoriaMetrics или Thanos и отдельного контура алертов.
TrueNAS SCALE, начиная с 24.04, отдает статистику через встроенный reporting API и поддерживает подключение сторонних экспортеров (zfs_exporter, node_exporter на самом NAS). Если разворачиваете стенд для проверки схемы, включая pushgateway и экспортеры, подойдет аренда облачного сервера: Timeweb Cloud предоставляет VDS, базы данных, объектное хранилище и Kubernetes, поэтому тестовый контур поднимается за несколько минут и так же быстро удаляется.
Прежде чем настраивать алерты, определите целевые значения: какие показатели считаются нормой для критичных, стандартных и второстепенных сервисов. Пять метрик качества хранения с готовыми запросами PromQL и шаблоном отчета приведены в материале про метрики и KPI системы качества хранения. Для объектных бэкендов и хранилищ медиафайлов набор шире: latency, throughput, IOPS и заполнение бакетов разобраны в статье про мониторинг хранилищ изображений.
Минимальный первый шаг: возьмите Bash-скрипт из этой статьи, поставьте его в cron с интервалом 5 минут, отправьте одну метрику через zabbix_sender и настройте два триггера - на 90% заполнения и на отсутствие данных. Через сутки появится история заполнения, через неделю станет видна реальная скорость роста данных по каждой точке монтирования. Опрос API для NAS и перенос метрик в Prometheus добавляйте, когда число серверов превысит три десятка.