MAATRIX / Блог / Ежемесячный прогноз свободного места: когда диск кончится, если ничего не менять

Ежемесячный прогноз свободного места: когда диск кончится, если ничего не менять

MAATRIX

Мониторинг честно показывает 10% свободного места на диске — и непонятно, паниковать или нет. У одного сервера это стабильный остаток, который держится месяцами, у другого — три недели до полной остановки сервисов. Разница не в проценте, а в скорости, с которой он тает. Регулярный прогноз по тренду роста закрывает именно этот пробел: он превращает голое число в дату, к которой нужно успеть.

Почему один процент свободного места ничего не говорит без контекста

Представьте два сервера, у каждого 10% свободного места на разделе /var.

Первый — это статичный проект: диск заполнялся полгода назад при разворачивании, с тех пор растёт на доли процента в месяц за счёт логов и временных файлов, которые исправно ротируются. При такой динамике 10% продержатся ещё год с лишним. Тревожный на вид процент здесь — просто особенность изначального выбора размера диска, а не проблема.

Второй сервер копит данные от растущего числа клиентов, база разрастается на несколько процентов в неделю. Те же формальные 10% здесь означают три-четыре недели до заполнения — и это уже не наблюдение, а срок, к которому нужно успеть с решением.

Разница видна только в динамике, а её обычный дашборд мониторинга не показывает. Типичный алерт по порогу («меньше 15% свободного места — сообщить») реагирует на факт, а не на скорость, и одинаково молчит что у медленно растущего сервера, что у быстро растущего — пока оба не пересекут порог. К этому моменту у быстрорастущего сервера времени на реакцию может уже не остаться. Похожая ловушка разобрана в материале о том, как дашборд показывал вроде бы спокойные 30% свободного места, а сервер всё равно встал — порог сработал слишком поздно относительно скорости, с которой место реально уходило.

Отсюда практический вывод: процент свободного места — это моментальный снимок, а не прогноз. Чтобы отвечать на вопрос «когда именно кончится место, если ничего не менять» — нужна не точка, а как минимум две точки во времени и наклон прямой между ними.

Что мерить: откуда брать данные о росте занятого места

Прежде чем считать скорость роста, нужен ряд наблюдений за использованием диска во времени — желательно по каждому разделу отдельно, потому что растут они не синхронно.

Если на сервере уже стоит система мониторинга (Zabbix, Prometheus/Grafana, Netdata), проще всего достать историю из неё: метрика вида node_filesystem_avail_bytes или процент занятости диска обычно и так собирается с интервалом в несколько минут и хранится неделями или месяцами. В этом случае отдельный сборщик данных не нужен — прогноз строится поверх уже существующей истории. Если мониторинг диска ещё не настроен, это стоит сделать в первую очередь.

Если полноценного мониторинга нет и заводить его целиком ради этой задачи избыточно, минимальный вариант — простой cron-скрипт, который раз в день дописывает строку в CSV-файл:

#!/bin/bash
# /usr/local/bin/disk-usage-log.sh
DATE=$(date +%Y-%m-%d)
df -B1 --output=target,used,avail /var /home /var/lib/docker 2>/dev/null | tail -n +2 | while read -r mount used avail; do
    echo "$DATE,$mount,$used,$avail" >> /var/log/disk-usage-history.csv
done
# crontab -e
0 3 * * * /usr/local/bin/disk-usage-log.sh

Важный нюанс: df показывает занятое место с точки зрения файловой системы, а не всегда совпадает с тем, что покажет du по конкретному каталогу — из-за открытых удалённых файлов и особенностей файловой системы эти числа расходятся. Для прогноза заполнения диска ориентируйтесь на df — это то, что реально видит ядро и на что среагирует ошибка "no space left on device".

Копите историю минимум 4–6 недель до первого содержательного прогноза: на коротком ряду одна аномальная неделя (разовая загрузка бэкапов, тестовые данные, временный всплеск логов при инциденте) может сильно исказить наклон прямой.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Простая линейная экстраполяция: как считать дату исчерпания

Метод, которого достаточно в подавляющем большинстве случаев — не машинное обучение и не сложные модели временных рядов, а обычная линейная экстраполяция по МНК (метод наименьших квадратов) или даже просто по двум точкам.

Идея: берём занятое место в начале периода наблюдения и в конце, считаем среднюю скорость роста в байтах (или процентах) в сутки, дальше линейно продолжаем эту прямую до момента, когда свободное место дойдёт до нуля (или до выбранного порога безопасности, о котором ниже).

Формула по двум точкам:

скорость_роста = (занято_сейчас - занято_period_назад) / дней_в_периоде
дней_до_заполнения = свободно_сейчас / скорость_роста

Простой скрипт на Python, который читает CSV из предыдущего раздела и считает прогноз по каждому разделу отдельно через линейную регрессию (устойчивее к разовым выбросам, чем расчёт по двум крайним точкам):

#!/usr/bin/env python3
import csv
from collections import defaultdict
from datetime import datetime, timedelta

rows_by_mount = defaultdict(list)

with open('/var/log/disk-usage-history.csv') as f:
    for date_str, mount, used, avail in csv.reader(f):
        date = datetime.strptime(date_str, '%Y-%m-%d')
        rows_by_mount[mount].append((date, int(used), int(avail)))

for mount, rows in rows_by_mount.items():
    rows.sort()
    if len(rows) < 7:
        print(f"{mount}: мало данных для прогноза ({len(rows)} точек)")
        continue

    t0 = rows[0][0]
    xs = [(r[0] - t0).days for r in rows]
    ys = [r[1] for r in rows]  # занято байт
    n = len(xs)
    mean_x = sum(xs) / n
    mean_y = sum(ys) / n
    num = sum((x - mean_x) * (y - mean_y) for x, y in zip(xs, ys))
    den = sum((x - mean_x) ** 2 for x in xs) or 1
    slope = num / den  # байт в сутки

    used_now = rows[-1][1]
    avail_now = rows[-1][2]
    total = used_now + avail_now

    if slope <= 0:
        print(f"{mount}: место не растёт или сокращается, прогноз не требуется")
        continue

    days_left = avail_now / slope
    eta = datetime.now() + timedelta(days=days_left)
    print(f"{mount}: рост {slope/1024/1024:.1f} МБ/сутки, "
          f"свободно {avail_now/1024/1024/1024:.1f} ГБ, "
          f"закончится примерно {eta.date()} ({days_left:.0f} дней)")

Пример вывода:

/var: рост 42.3 МБ/сутки, свободно 8.1 ГБ, закончится примерно 2026-11-14 (196 дней)
/var/lib/docker: рост 310.7 МБ/сутки, свободно 3.4 ГБ, закончится примерно 2026-09-19 (11 дней)

Здесь видна вся ценность подхода: оба раздела в моменте выглядят "не критично" (8 ГБ и 3.4 ГБ свободно — не 0), но у одного в запасе полгода, а у другого — меньше двух недель. Без прогноза оба выглядели бы одинаково тревожно или одинаково спокойно в зависимости от выбранного порога.

Оговорка по методу: линейная экстраполяция предполагает, что рост останется линейным. Это разумное приближение на горизонте недель-месяцев, но не работает для взрывного роста (вирусный трафик, единовременная миграция данных) и не учитывает плановые события (запуск фичи, которая начнёт писать логи в разы активнее). Прогноз — ориентир для планирования, а не гарантия с точностью до дня.

Разделы, которые считать по отдельности, и почему нельзя усреднять по всему серверу

Прогноз, построенный по общему проценту занятости всего сервера, почти всегда вводит в заблуждение. Разные разделы растут с разной скоростью и по разным причинам, а объединение их в одну цифру размывает самый быстрорастущий и потому самый опасный раздел.

Разделы, за которыми обычно стоит следить отдельно:

Раздел / точка монтированияТипичная причина ростаНа что смотреть дополнительно
/var/logЛоги приложений и системыНастроена ли ротация — см. ниже
/var/lib/dockerОбразы, слои, volume’ы контейнеровЧисло неиспользуемых образов, docker system df
/var/lib/mysql, /var/lib/postgresqlРост таблиц, WAL/binlogПрогноз по базе отдельно от прогноза по диску целиком
/home или каталог данных приложенияПользовательские файлы, аплодыПрогноз тесно связан с ростом числа пользователей
/tmp, /var/tmpВременные файлыОбычно не должен расти линейно — рост здесь чаще баг
Каталог бэкаповЛокальные копии перед выгрузкойСовпадает с ростом основных данных, но с лагом

Раздел с базой данных особенно важен вынести отдельно: у СУБД есть свои внутренние механизмы разрастания (WAL-файлы в PostgreSQL, binlog в MySQL), которые могут расти рывками, не совпадающими с трендом остального диска.

Стоит также проверить, что рост линейный, а не ступенчатый: если места становится меньше ровным темпом день за днём — модель работает хорошо. Редкие резкие скачки (раз в неделю минус 2 ГБ, в остальные дни без изменений) — обычно разовые задачи вроде выгрузки бэкапов, и усреднённый наклон всё равно покажет корректный долгосрочный тренд.

Когда планировать расширение диска заранее, а не по факту аварии

Прогноз ценен ровно настолько, насколько заранее по нему принимается решение. Ориентировочные пороги реакции (конкретные цифры нужно калибровать под свою инфраструктуру и скорость собственных процессов закупки/миграции):

  • Более 90 дней до заполнения — действие не требуется, достаточно пересчитывать прогноз раз в месяц вместе с общим регламентом.
  • 30–90 дней — время добавить задачу в бэклог: заказать увеличение диска, спланировать чистку данных, пересмотреть срок хранения логов и бэкапов. Ещё не аврал, но и не то, что можно отложить на «когда-нибудь».
  • Меньше 30 дней — конкретная дата в календаре, к которой должно быть готово решение: увеличенный диск, вынесенные на другой том данные или подтверждённый план очистки. Стоит заранее проверить, требует ли расширение диска простоя — см. как расширить диск на работающем сервере.
  • Меньше 7 дней, или срок ускоренно сокращается от прогноза к прогнозу — экстренная ситуация: сравнивайте не только с последним прогнозом, а с прогнозом месячной давности. Если было 60 дней месяц назад, а стало 10 сейчас (а не 30) — скорость роста сама растёт, и линейная модель уже занижает риск.

Ключевая причина считать именно так: заказ диска, миграция данных на больший том или согласование бюджета на апгрейд редко занимают меньше нескольких дней, а с учётом закупочных процедур — иногда недели. Прогноз меньше 30 дней означает, что окно для спокойного планового решения закрывается, а прогноз меньше 7 — что решение принимается в режиме инцидента. Сколько места закладывать с запасом при первичном планировании диска — см. сколько дискового пространства закладывать с запасом; прогноз по тренду — способ регулярно проверять, не устарел ли тот изначальный запас.

Отдельно стоит фиксировать не только факт пересечения порога, но и то, что решение принято: кто отвечает, что именно будет сделано и к какому сроку. Прогноз без владельца решения — просто ещё одна цифра в отчёте, которую никто не обязан замечать.

Как встроить прогноз в регулярный процесс, а не запускать вручную раз в год

Разовый расчёт полезен один раз, но ценность метода — именно в регулярности: тренд со временем меняется (новая фича начала писать больше логов, база выросла из-за нового клиента), и прогноз полугодовой давности устаревает без предупреждения.

Практичная схема: ежемесячный запуск скрипта прогноза как часть более широкого регламента обслуживания сервера, с отправкой результата туда, где его точно увидят — не просто в лог, а в чат или тикет. Похожая логика уже применяется для еженедельного техобслуживания; прогноз места на диске логично встроить туда же отдельным пунктом раз в месяц.

Пример cron-задачи с отправкой сводки, если прогноз для любого раздела ушёл ниже 60 дней:

#!/bin/bash
# /usr/local/bin/disk-forecast-alert.sh
OUTPUT=$(/usr/local/bin/disk-forecast.py)
echo "$OUTPUT" | while read -r line; do
    days=$(echo "$line" | grep -oP '\(\K[0-9]+(?= дней\))')
    if [[ -n "$days" && "$days" -lt 60 ]]; then
        curl -s -X POST "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
            -d chat_id="${TG_CHAT_ID}" \
            -d text="Прогноз диска: $line"
    fi
done
# crontab -e — первого числа каждого месяца в 9 утра
0 9 1 * * /usr/local/bin/disk-forecast-alert.sh

Сам скрипт прогноза стоит поставить под наблюдение на предмет "а точно ли он вообще отработал" — иначе можно молча остаться без прогнозов ровно в тот месяц, когда они особенно нужны. Способ проверки живости cron-задач без лишней инфраструктуры разобран в материале healthchecks.io для мониторинга cron-задач — добавить пинг в конец скрипта занимает пару строк.

Отдельно стоит вести историю самих прогнозов, а не только текущего значения: дата расчёта, раздел, скорость роста, прогнозная дата исчерпания. Это даёт возможность увидеть не только "сколько дней осталось сейчас", но и "ускоряется рост или нет" — более ранний и надёжный сигнал тревоги, чем единичный прогноз.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Можно ли обойтись без истории и посчитать прогноз по одному замеру?

Нет — по одной точке нельзя определить скорость изменения, только текущее значение. Минимум нужны два замера с известным интервалом, а для устойчивого прогноза — история за несколько недель.

Как быть, если рост явно нелинейный — то ускоряется, то замедляется?

Линейная модель всё равно полезна как консервативный ориентир на месяц-два, если пересчитывать её регулярно, а не полагаться на один расчёт полугодовой давности. Для явно нелинейного роста лучше отслеживать изменение самой скорости от прогноза к прогнозу, как описано в разделе про пороги реакции.

Стоит ли считать прогноз по inode, а не только по байтам?

Да, если на сервере много мелких файлов (почтовые очереди, кэши) — диск может формально не быть заполнен по объёму, но упереться в число доступных inode раньше. Проверяется через df -i тем же способом.

Нужен ли прогноз, если диск и так расширяется автоматически (тонкие тома, облако с авторасширением)?

Да — просто меняется вопрос с "когда кончится место" на "когда вырастет счёт за хранилище" и "не упрёмся ли в лимит квоты раньше, чем в физический предел". Механика расчёта та же.

Что делать, если для разных разделов получаются очень разные горизонты прогноза?

Это нормально — реагировать нужно на раздел с наименьшим сроком, а не усреднять. Именно поэтому прогноз стоит считать по каждой точке монтирования отдельно.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →