Прогноз выглядел разумно. Сервис хранит заявки, документы и вложения клиентов; за последние месяцы бизнес-данных прибавлялось примерно на гигабайт в неделю. Диск на 80 ГБ, занято чуть больше трети, до заполнения, по простой арифметике, больше года. А потом выясняется, что свободного места осталось на пару недель, хотя новых клиентов не стало больше и никто не загружал ничего необычного.
Ошибка почти всегда одна и та же: прогноз строили по линии, которая показывает только то, что видит бизнес. Заявки, договоры и фотографии растут медленно и предсказуемо. Но диск VPS принадлежит не только бизнесу. Его делят с ним журналы контейнеров, системный журнал, временные файлы базы данных, предыдущие версии приложения и дампы, которые лежат тут же, на соседнем каталоге. Эти источники растут с другой скоростью, живут разное время и иногда появляются одновременно. Одной общей линией их не описать.
Ниже мы разберём, как разложить диск по источникам, измерить темп и срок жизни каждого, отделить постоянный рост от временного пика и учесть момент, когда на диске лежат сразу старая и новая копия чего-либо. В конце останется прогноз, у которого есть дата пересмотра и условия, при которых его надо пересчитать раньше срока.
Почему одна линия обманывает
Общая линия заполнения диска — это сумма всего сразу, и у неё два свойства, которые мешают прогнозировать. Во-первых, она смешивает данные, которые накапливаются навсегда, с теми, которые должны исчезать. Если журнал растёт на 400 МБ в сутки, а вложения на 240 МБ, то по общему графику вы увидите 640 МБ в сутки и решите, что бизнес разросся. На деле одна часть этой суммы — необходимая, а другая — следствие того, что у журнала нет предела.
Во-вторых, линия сглаживает пики. Если раз в неделю тяжёлый отчёт на несколько минут занимает шесть гигабайт, а график строится по замерам раз в сутки, пик почти наверняка не попадёт в точки. Прогноз покажет аккуратный склон, а диск однажды заполнится именно в те минуты, когда отчёт запущен одновременно с созданием копии. Склон не врал, он просто не показывал вершину.
Поэтому сначала мы разделим диск на категории, а потом для каждой ответим на два вопроса: сколько она прибавляет за единицу времени и сколько времени живёт то, что она прибавила. Всё остальное — арифметика.
Кто живёт на диске, кроме ваших данных
Для типичного приложения на VPS хватает шести групп. Названия условные, у вашего проекта могут быть свои, но смысл сохранится.
- Система и пакеты. Растёт редко и ступеньками: при обновлениях и установке новых компонентов. Для прогноза это константа с ежеквартальной поправкой.
- База данных. Растёт вслед за бизнесом, но не строго линейно: индексы и служебные структуры тоже занимают место. Темп надо брать по реальному размеру каталога базы, а не по числу строк, умноженному на среднюю длину записи.
- Вложения и загруженные файлы. Накопительная категория без срока жизни: файл загрузили, он остался. Темп зависит от числа загрузок и среднего размера файла, и оба множителя могут меняться независимо.
- Журналы. Журналы контейнеров, системный журнал, журналы веб-сервера. Здесь всё определяется политикой ротации: либо есть потолок, либо журнал растёт, пока диск не закончится.
- Временные файлы. Сортировки в базе, распаковка архивов, превью, промежуточные результаты экспорта. Занимают много, но живут минуты. Опасны не объёмом за месяц, а вершиной в конкретный момент.
- Локальные копии и релизы. Дампы базы, архивы каталога вложений, образы предыдущих версий приложения. Это не рост, а запас, и размер запаса задаётся тем, сколько штук вы решили хранить.
Обратите внимание на различие. Первые три группы описывают, что у вас есть. Остальные три — что ваша инфраструктура производит как побочный продукт. Именно побочный продукт чаще всего и съедает прогноз.
Темп и срок жизни: две цифры вместо одной
Для каждой категории нужны два числа: темп прироста в единицу времени и срок, который прирост лежит на диске. От их сочетания зависит, как категория ведёт себя на горизонте прогноза.
У накопительных категорий срок жизни бесконечен, и вклад в диск равен темпу, умноженному на длину горизонта. Вложения, база и системные пакеты ведут себя именно так.
У категорий с ротацией вклад выходит на плато. Если журнал пишется со скоростью 400 МБ в сутки, а хранится семь суток, то в установившемся режиме он занимает около 2,8 ГБ и дальше не растёт. Формула проста: объём равен темпу, умноженному на срок хранения. Но она работает только тогда, когда ротация действительно настроена и действительно применяется. Если срок хранения не ограничен, формула превращается в тот же накопительный расчёт, только с гораздо более высоким темпом.
У временных файлов вклад вообще не растёт со временем. Важна их вершина: самый большой объём, который может существовать одновременно. Здесь надо считать не среднее, а максимум и ответить на вопрос, может ли он совпасть с вершинами других категорий.
Измерять темп лучше по двум точкам: размер каталога сейчас и размер неделю назад. Для этого хватает привычных df -h для общей картины и du -sh по каталогам групп. Один замер ничего не говорит, два дают темп, а серия недельных замеров показывает, ускоряется ли рост. Если график недельных приростов ровный, можно строить линейный прогноз. Если ступенчатый, ищите событие: выкладку новой версии, включённую отладку, новую функцию загрузки.
Журналы: категория, которая растёт без согласия бизнеса
Журналы контейнеров заслуживают отдельного внимания, потому что именно у них чаще всего нет предела по умолчанию. В документации Docker о драйвере json-file сказано, что параметры max-size и max-file управляют ротацией журналов контейнера, а значение max-size по умолчанию не ограничено (Docker json-file logging driver). Иначе говоря, если вы ничего не настраивали, журнал каждого контейнера будет расти, пока не упрётся в свободное место.
Есть и тонкость, которая нередко ломает уже проведённую настройку. По той же документации, изменения умолчаний логирования в конфигурации демона касаются новых контейнеров, а существующие автоматически не перенастраиваются. Практический вывод: прописать ограничение в конфигурации недостаточно, надо ещё пересоздать работающие контейнеры, чтобы они получили новые параметры. Пока этого не сделано, старый контейнер продолжает писать журнал по прежним правилам. В прогнозе такая категория должна считаться неограниченной до тех пор, пока вы не убедились, что ограничение действует именно на ваш контейнер.
Системный журнал устроен иначе, и ограничения там двойные. В описании настроек journald сказано, что SystemMaxUse ограничивает объём диска под журнал, а SystemKeepFree оставляет место для остальных нужд; применяется то из двух ограничений, которое жёстче (systemd journald.conf). Это удобно: можно сказать не только сколько журналу разрешено занять, но и сколько он обязан оставить свободным. Но есть оговорка, которую стоит учесть в расчёте: для соблюдения лимитов удаляются архивные файлы, а активные могут держать фактическое использование выше заданного предела. Поэтому при подсчёте закладывайте в категорию журнала не ровно значение лимита, а лимит с небольшим запасом на активный файл.
Разница между двумя подходами полезна для понимания. Журнал контейнера по умолчанию безразличен к диску, а журнал системы по умолчанию уже умеет оглядываться на свободное место, если его настроить. Но ни одно из этих свойств не заменяет прогноза: ротация защищает от бесконечного роста, но не отвечает на вопрос, сколько места под журналы вы готовы отдать.
Учебный расчёт на диске в 80 ГБ
Теперь сложим всё вместе. Следующие числа придуманы для примера, это не замеры чьего-то проекта и не типичные значения. Они нужны, чтобы показать механику: как складываются категории с разным сроком жизни. Диск 80 ГБ, горизонт прогноза 90 суток.
Исходные допущения: система и пакеты занимают 9 ГБ и в горизонте не меняются. База сейчас 3 ГБ и прибавляет 150 МБ в месяц. Вложений уже 8 ГБ; приложение получает около 60 файлов в сутки по 4 МБ в среднем, то есть 240 МБ в сутки. Журналы контейнеров пишутся со скоростью 400 МБ в сутки. Системный журнал ограничен 500 МБ. Дамп базы после сжатия занимает 1 ГБ, хранятся семь последних. Образ приложения 1,2 ГБ, хранятся три версии. Раз в неделю строится тяжёлый отчёт, который на несколько минут занимает до 6 ГБ временных файлов.
| Категория | Как считаем | Через 90 суток, ГБ |
|---|---|---|
| Система и пакеты | константа | 9,0 |
| База данных | 3 ГБ + 3 месяца по 0,15 | 3,5 |
| Вложения | 8 ГБ + 90 × 0,24 | 29,6 |
| Журналы контейнеров при ротации | потолок 5 × 50 МБ | 0,25 |
| Системный журнал | лимит | 0,5 |
| Дампы базы | 7 × 1 ГБ | 7,0 |
| Образы приложения | 3 × 1,2 ГБ | 3,6 |
| Постоянная часть | сумма | 53,45 |
Постоянная часть выходит около 53,5 ГБ из 80. Это 67 процентов, и выглядит спокойно. Но теперь добавим вершины, о которых линия молчит. Отчёт с временными файлами даёт до 6 ГБ. Во время создания нового дампа старые семь ещё лежат, значит, на диске на время существует восьмой, +1 ГБ. Во время выкладки новой версии рядом с тремя образами появляется четвёртый, +1,2 ГБ. Если все три события совпадут, вершина составит 53,45 + 6 + 1 + 1,2 = 61,65 ГБ, то есть около 77 процентов диска. Запас есть, но он уже не так огромен, как казалось по постоянной части.
Теперь сравните с тем, что было бы без ограничения журналов. Те же 400 МБ в сутки за 90 суток дают 36 ГБ вместо 0,25. Постоянная часть тогда составит 53,45 − 0,25 + 36 = 89,2 ГБ — больше диска. То есть расчёт показывает, что при этих допущениях диск закончится раньше горизонта, причём не из-за клиентов, а из-за одной настройки. Бизнес-категории (база и вложения) прибавили бы за те же 90 суток около 22 ГБ, журнал без потолка — 36. Вот и вся разгадка расхождения, с которого мы начали.
Заодно видно, как этот пример связан с выбором ресурсов. Тариф с 40 ГБ NVMe, как у Ba, такой набор не вместил бы даже по постоянной части, а тариф с 80 ГБ, как у Uab, вмещает с понятным запасом. Но это иллюстрация метода, а не рекомендация по тарифу: параметры плана — отправная точка для вашего собственного теста, а не обещание, что нужное вам число пользователей и файлов поместится. Если ваши темпы окажутся другими, ответ изменится, и именно поэтому расчёт стоит повторять на своих цифрах.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации Россия, США, Франция и Великобритания. Оплата картой РФ и по СБП.
Постоянный рост и временная вершина
Эти два вида нагрузки нельзя складывать по одному правилу. Постоянный рост отвечает на вопрос «когда диск закончится», вершина — на вопрос «не закончится ли он сегодня».
Для постоянного роста нужен горизонт и темп. Для вершины — максимальный объём одновременно существующих временных данных и ответ на вопрос, что ещё в этот момент лежит на диске. Практическое правило, которое мы используем в расчётах: свободное место должно быть не меньше самой большой реалистичной вершины, а сверх того — запаса на время вашей реакции. Если вы проверяете диск раз в неделю, а темп постоянного роста 0,3 ГБ в сутки, то между проверками прибавится больше двух гигабайт, и этот объём тоже надо оставить свободным. Единого процента свободного места, подходящего всем, из такой логики не следует: для одного проекта достаточно десяти процентов, для другого мало и тридцати, потому что у него вершина в четверть диска.
Особенно коварны временные файлы базы данных. В документации PostgreSQL о потреблении ресурсов указано, что параметр work_mem относится к отдельным операциям сортировки и хеширования, а в запросе и в параллельных сессиях таких операций может быть несколько; если памяти не хватает, операции могут обращаться к временным файлам, а параллельные работники расходуют ресурсы дополнительно (PostgreSQL resource consumption). Из этого следует неприятная вещь для прогноза: объём временных файлов нельзя оценить по размеру таблиц. Он зависит от запроса, числа одновременных сессий и от того, что решил планировщик. Один и тот же отчёт при росте таблицы может внезапно перестать помещаться в память и перейти на диск. Подробный пример того, как такая вершина закрывает диск за считаные минуты, разобран в статье о временных файлах сортировки, заполнивших диск базы.
Практически это значит, что вершину временных файлов надо не вычислять, а наблюдать: запустить самые тяжёлые отчёты и экспорты на реалистичном объёме данных и посмотреть, сколько места занимает рабочий каталог в пике. Результат записать в прогноз как отдельную строку, с датой измерения. Когда таблицы вырастут вдвое, измерение придётся повторить.
Две копии одного и того же: момент, о котором забывают
Самая неприятная вершина получается не от большого файла, а от того, что на диске на короткое время должны лежать и старое, и новое. Таких моментов несколько.
Дамп базы. Если политика хранения — семь последних дампов, то в установившемся режиме на диске их семь. Но если сначала создаётся новый, а лишний удаляется после успешного завершения, то на время создания их восемь. Это правильный порядок: удалять старую копию до того, как новая записана целиком, значит рисковать остаться вообще без копии. Поэтому в расчёте место под такую копию надо держать свободным всегда, а не только в среднем.
Выкладка новой версии. Пока вы не убедились, что новая версия работает, предыдущую образ удалять нельзя: она нужна для возврата. Значит, в момент выкладки образов на один больше, чем вы собираетесь хранить. Если на выкладке ещё и собираются слои, прибавка может оказаться больше размера итогового образа.
Проверка восстановления. Копия, которую ни разу не пробовали развернуть, остаётся предположением. Но развернуть её значит положить рядом с рабочими данными полный объём восстановленных. В документации restic о восстановлении сказано, что команда позволяет извлечь выбранный снимок в отдельную целевую директорию; параметр --path служит для выбора снимка, а --include и --exclude ограничивают извлекаемые файлы, и это не одно и то же; есть также фильтры, чтобы взять последний снимок конкретного хоста и пути, а восстановление в отдельный каталог позволяет сначала проверить результат (Restic: восстановление). Для прогноза из этого следует простое: пробное восстановление всего каталога вложений потребует места, равного этому каталогу. Если вложений 30 ГБ, а свободно 25, полное пробное восстановление на тот же диск не поместится, и придётся либо проверять избирательно, ограничив извлечение нужными путями, либо делать проверку на другой машине. Выбор между этими вариантами — часть планирования диска, а не вопрос на момент аварии.
Здесь же стоит сказать про локальные копии вообще. Дамп, лежащий на том же диске, что и база, освобождает от одних рисков, например от неудачной миграции, но не защищает от потери самого диска и расходует то же место, которое вы прогнозируете. Если такие копии есть, их надо учитывать отдельной строкой в расчёте и честно называть тем, чем они являются: запасом на случай ошибки в данных, а не полноценной резервной копией. Где хранить копии вне диска сервера, решайте отдельно; про автоматические резервные копии на тарифах мы не делаем никаких допущений, их наличие и условия нужно проверять при заказе.
Что ограничивать, а что трогать нельзя
Когда расчёт показывает нехватку, возникает соблазн быстро освободить место, и тут легко навредить. Красивый свободный остаток, достигнутый удалением активных данных, хуже честного, но тесного диска. Полезно держать в голове разделение на три класса.
- Можно ограничивать источник. Ротация журналов, потолок системного журнала, срок хранения дампов и старых образов. Это не очистка, а изменение правил, после которого категория сама выходит на плато. Такие меры надёжнее разовой чистки, потому что не требуют, чтобы кто-то вспомнил про диск.
- Можно удалять осторожно и после проверки. Устаревшие архивы сверх политики хранения, образы версий, которые вы точно не будете откатывать, временные каталоги, в которые давно ничего не пишется. Перед удалением проверьте, что именно лежит в каталоге и использует ли его запущенный процесс. Признак «давно не менялся» не равен признаку «не нужен».
- Не трогать ради места. Каталог вложений, файлы базы данных, активные журналы работающей базы, всё, что процессы держат открытым. Если для освобождения места вы готовы удалить хоть что-то из этого класса, значит, проблема не в чистке, а в ёмкости: нужен диск побольше или вынос данных, а не героические действия на живой системе.
Есть ещё один нюанс, который проявляется на журналах. Если процесс держит файл открытым, удаление файла не возвращает место, пока процесс не закроет его. А принудительное усечение живого файла может повредить то, что в него пишется. Поэтому ограничение источника через настройки ротации всегда предпочтительнее ручных действий над файлами. Вы сначала меняете правило, а потом, если нужно, убираете накопленное старое — уже архивные файлы, а не те, в которые идёт запись.
Прогноз из таких категорий становится ещё и инструментом принятия решений. Если вложения, единственная категория, которую нельзя ограничить политикой, занимают больше половины итога, значит, именно от них зависит срок жизни диска, и ответ на вопрос «когда расширяться» зависит от них. Остальное можно подтянуть настройками.
Прогноз с датой пересмотра
Расчёт, который однажды сделали и убрали в стол, перестаёт быть прогнозом ровно в момент, когда приложение начинает вести себя иначе. Поэтому у каждого прогноза должны быть две вещи: календарная дата пересмотра и список событий, при которых пересматривать надо раньше.
Календарную дату привязывайте к горизонту: для горизонта в 90 суток разумно возвращаться к цифрам через четыре–шесть недель, не дожидаясь конца. К этому моменту у вас уже накопится несколько недельных замеров, и вы увидите, держится ли ваш темп.
События-триггеры стоит записать заранее, потому что в момент самого события о диске вспомнят в последнюю очередь:
- включён подробный уровень логирования или добавлена трассировка запросов, потому что это умножает темп журналов в разы;
- появилась новая функция загрузки файлов или изменились допустимые размеры и форматы вложений, например, разрешили видео или сканы в высоком разрешении;
- добавлен новый тяжёлый отчёт, экспорт или фоновая обработка с промежуточными файлами;
- выросло число пользователей, одновременно работающих с отчётами, так как временные файлы базы умножаются на число сессий;
- изменилась политика копий: больше дампов, чаще создание, другой формат;
- изменилась структура выкладки: больше версий образов, дополнительные окружения на той же машине.
Запись в прогнозе должна выглядеть так: категория, текущий темп, срок жизни, дата измерения, источник измерения, условие пересмотра. Тогда через два месяца вы или ваш коллега, открыв документ, сможете отличить устаревшую цифру от актуальной. Для категорий с ротацией полезно записать и то, где именно настроен потолок, чтобы при разборе не искать, какой из слоёв конфигурации действует: параметры контейнеров, демон или система журналирования.
Отдельно о запасе. Он должен быть не процентом от диска, а суммой нескольких понятных величин: самая большая реалистичная вершина, запас на момент создания новой копии и выкладки новой версии, и темп постоянного роста, умноженный на интервал между проверками. В нашем учебном расчёте это получается 8,2 ГБ вершин плюс около 1,7 ГБ на недельный интервал при темпе бизнес-данных 0,24 ГБ в сутки, то есть около 10 ГБ — а не абстрактные «двадцать процентов». Если сумма не помещается, у вас есть три рычага: уменьшить срок жизни категории, уменьшить саму вершину, например, разбив тяжёлый отчёт на части, или взять диск больше.
На этом шаге и стоит сопоставить результат с тарифами. Диски в линейке MAATRIX варьируются от 40 ГБ NVMe у Ba до 80 у Uab, 160 у Heka, 240 у Maat и 320 у Djed. Но выбирать надо по расчёту вашей суммы с вершинами и по тестовому прогону, а не по ощущению, что места «должно хватить»: параметры плана задают границы стенда, на котором вы проверите свои цифры, а не гарантируют ёмкость. Для проектов, где нужно держать рядом приложение, базу и файловое хранилище, например, развёрнутое Nextcloud или рабочий процесс n8n с долгим журналом выполнений, именно разложение по категориям покажет, какая из них в вашем случае окажется главной. Наличие конкретных образов и операционных систем лучше проверить в форме заказа.
Возвращаясь к журналам
В нашем примере неожиданный рост дал не бизнес и не вложения. Его дал журнал контейнера, у которого по умолчанию нет верхней границы: 400 МБ в сутки, 36 ГБ за три месяца, больше, чем все вложения вместе с базой. Это та самая линия, которой не было в исходном прогнозе, и та самая причина, по которой свободное место таяло, пока заявок прибавлялось немного.
Действие здесь одно и очень конкретное: задать для драйвера json-file значения max-size и max-file, а затем пересоздать существующие контейнеры, потому что уже запущенные новых умолчаний сами не получат. Системный журнал при этом ограничить через SystemMaxUse и, если нужно, SystemKeepFree, оставив небольшой запас на активный файл. После этого категория логов превращается из растущей линии в плоскую строку расчёта в четверть гигабайта, и прогноз начинает зависеть от того, от чего ему и положено зависеть, — от ваших данных.