На многопользовательском сервере общий жёсткий диск быстро превращается в поле битвы за место. Один человек выгружает в домашний каталог архив на сотню гигабайт, и через сутки свободного пространства не остаётся ни у кого: логи перестают писаться, базы данных отказываются принимать записи, резервные копии падают на середине. Дисковые квоты решают проблему на уровне файловой системы ext4, не надеясь на дисциплину пользователей и уговоры в рабочем чате. Мягкий лимит предупреждает, жёсткий отсекает, льготный период даёт время разобраться. Дальше показана настройка от установки пакета до автоматических напоминаний нарушителям, со всеми командами и значениями по умолчанию.

Чем грозит диск, заполненный одним пользователем

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

df -h /home
Filesystem      Size  Used Avail Use% Mounted on
/dev/sdb1       500G  500G   28K 100% /home

Здесь раздел /dev/sdb1 ёмкостью 500 ГБ забит целиком. Небольшой запас в 28 КБ выглядит странно, но это работа штатной защиты ext4: по умолчанию при создании файловой системы 5% блоков резервируются для root (параметр -m у mkfs.ext4), поэтому служебные процессы ещё дышат, а обычные пользователи уже повсеместно получают "No space left on device". Многим знакомо это чувство, когда сервер формально жив, но работать на нём невозможно.

Вторая угроза менее очевидна. Квота считает не только место, но и число файлов, то есть inode. Тысячи мелких файлов из кэша, сессий или распакованных архивов способны исчерпать inode даже тогда, когда гигабайты свободны. Диагностируется это командой df -i:

df -i /home
Filesystem      Inodes  IUsed   IFree IUse% Mounted on
/dev/sdb1      32768000 32767400     0  100% /home

Свободного места в килобайтах ещё полно, а писать файлы система уже не может. Лечится это только квотой именно на inode, потому что уговорить человека удалить сто тысяч пустых картинок почти невозможно.

Устройство мягких и жёстких лимитов

Квота в ext4 ведёт учёт по каждому пользователю и каждой группе отдельно, причём на уровне ядра, без посредников. Считаются два ресурса: блоки, то есть фактическое место, и inode, то есть количество файлов и каталогов. По каждому ресурсу задаётся пара порогов. Мягкий лимит (soft) превышать можно, жёсткий (hard) нельзя, а значение 0 снимает ограничение совсем.

Между ними работает льготный период, в документации он называется grace period. Как только человек перешагнул soft, ядро запускает для него таймер. По умолчанию он равен 7 дням, или 604800 секундам. Пока таймер тикает, записи разрешены. Если к концу срока расход не опустился ниже soft, мягкий лимит начинает действовать как жёсткий. Опустился ниже, таймер гаснет, и всё возвращается на круги своя. Таймер ведётся отдельно для блоков и отдельно для inode, по каждому пользователю и по каждому разделу.

Жёсткий лимит срабатывает мгновенно. Очередная запись блокируется, приложение получает ошибку EDQUOT, которую человек видит как "Disk quota exceeded" или "Превышена дисковая квота". Отсюда практическое правило: soft ставят на 10-20% ниже hard. Например, soft 10 ГБ, hard 12 ГБ. Система успевает честно предупредить до того, как у пользователя посреди рабочего дня откажется сохраняться документ. Такой порядок экономит нервы и администратору, и людям, которых он обслуживает.

Есть у квот и свойство, о котором часто забывают: учёт ведётся по владельцу каждого файла на разделе, а не по каталогам. Файлы, которые ivan разбросал по общим папкам в чужих каталогах, тоже попадут в его счёт, поэтому расход в выводе quota -vs может не совпадать с результатом du -sh по домашнему каталогу. Это не ошибка, а как раз тот случай, когда квота честнее привычных утилит.

Подготовка ext4 и подключение опций монтирования

Ядро считает квоты только для разделов, смонтированных с опциями usrquota (учёт по пользователям) и grpquota (по группам). В файле /etc/fstab соответствующая строка выглядит так:

UUID=6f3b1c2d-9a44-4f8e-8b1a-77c0e5d24f31  /home  ext4  defaults,usrquota,grpquota  0  2

Идентификатор UUID у каждого раздела свой, его показывает команда blkid. После правки раздел перемонтируется, и опции проверяются:

mount -o remount /home
mount | grep /home

В выводе второй команды среди опций должны появиться usrquota и grpquota. Если их нет, ядро лимиты считать не станет, сколько бы команд потом ни было выполнено.

Существует и второй путь, журналируемые квоты. Опции в fstab тогда записываются иначе:

defaults,usrjquota=aquota.user,grpjquota=aquota.group,jqfmt=vfsv0

В этом варианте ядро ведёт учёт прямо в журнале файловой системы, отдельный пересчёт после сбоя не нужен, и включать квоты командой quotaon не требуется. Тот же эффект даёт включение штатной возможности прямо в суперблоке:

tune2fs -O quota,usrquota,grpquota /dev/sdb1
tune2fs -l /dev/sdb1 | grep -i quota

Строка "Filesystem features" в выводе должна содержать слово quota. Классическая схема с usrquota в fstab проще и понятнее, поэтому чаще встречается на реальных серверах. Журналируемая выигрывает там, где случаются внезапные перезагрузки: учёт остаётся точным без обязательного сканирования при старте.

Установка пакета и первый запуск квот

Инструменты управления квотами собраны в одноимённом пакете quota, и в стандартной поставке Debian или Ubuntu он часто отсутствует. Вся подготовка укладывается в пять шагов:

  1. пакет утилит ставится командой apt install quota, в RHEL и Fedora пакет называется так же, но устанавливается через dnf install quota;
  2. в файле /etc/fstab к опциям раздела с домашними каталогами добавляются usrquota,grpquota, затем выполняется mount -o remount /home;
  3. команда quotacheck -cugm /home сканирует раздел и создаёт файлы учёта aquota.user и aquota.group;
  4. квоты включаются командой quotaon -v /home;
  5. статус проверяется командой quotaon -p /home, которая коротко отвечает, включены ли квоты на разделе.

Ключи quotacheck расшифровываются так: c создаёт файлы учёта с нуля, u включает учёт по пользователям, g по группам, m запрещает утилите перемонтировать работающий раздел только для чтения. Точнее всего quotacheck работает на размонтированном разделе или в однопользовательском режиме, но и на живом сервере расхождение обычно минимально. Созданные файлы aquota.user и aquota.group лежат в корне раздела, принадлежат root и должны быть закрыты от посторонних (chmod 600), иначе любой пользователь увидит чужие лимиты. В системах с systemd службы quotaon.service и quotacheck.service подхватывают эти настройки автоматически при каждой загрузке, вручную включать квоты после перезагрузки не придётся.

Лимиты для конкретного пользователя и для группы

Классический способ назначить лимиты человеку выглядит архаично, но работает безотказно. От имени root запускается команда:

edquota -u ivan

Открывается редактор (по умолчанию vi, переменной EDITOR его можно заменить) с содержимым такого вида:

Disk quotas for user ivan (uid 1001):
  Filesystem                   blocks       soft       hard     inodes     soft     hard
  /dev/sdb1                     39244          0          0        812        0        0

Колонка blocks показывает текущий расход в килобайтных блоках: 39244 КБ это примерно 38 МиБ. Менять имеет смысл только soft и hard, значения использованных блоков и inode редактор просто игнорирует. Поставим ivan 10 и 12 ГБ. Расчёт простой: 1 ГБ равен 1048576 КБ, значит soft равен 10485760, а hard равен 12582912. Заодно ограничим число файлов пределами 100000 и 120000 inode.

Тем, кто не любит текстовые редакторы, подойдёт setquota, она делает всё той же одной строкой и отлично встраивается в скрипты:

setquota -u ivan 10485760 12582912 100000 120000 /home

Порядок аргументов такой: имя пользователя, block soft, block hard, inode soft, inode hard, точка монтирования.

Копировать готовый профиль на всю команду можно через edquota с ключом -p, где первое имя служит образцом:

edquota -p ivan petr anna olga

Когда новеньких много, список имён по одному в строке складывают в файл и прогоняют циклом:

for u in $(cat /root/team.txt); do
  setquota -u $u 10485760 12582912 100000 120000 /home
done

Для группы действует отдельный лимит, общий на всех её участников:

setquota -g developers 209715200 230686720 0 0 /home

Здесь на группу developers выделено 200 ГБ мягко и 220 ГБ жёстко. Групповая квота удобна для проектных каталогов: сколько бы участников ни пришло, суммарный расход останется предсказуемым. Свои лимиты каждый пользователь видит сам командой quota -vs, где ключ s переводит цифры в человекочитаемые мегабайты и гигабайты.

Квоты часто включают на сервере, где люди годами складывали файлы без ограничений, и часть аккаунтов может сразу оказаться за мягким порогом. Резать уже записанное ядро не станет: жёсткий лимит останавливает только новые записи, а на накопленное начинает тикать льготный таймер. Переход получается мягким, но сроки в edquota -t в такой ситуации лучше назначить с запасом, чтобы у людей было время разобрать завалы.

Льготный период под свои правила

Семь дней по умолчанию устраивают не всех. Посмотреть и изменить общие значения можно командой edquota -t от имени root:

Grace period before enforcing soft limits for users:
Time units may be: days, hours, minutes, or seconds
Filesystem             Block grace period     Inode grace period
/dev/sdb1                  7days                  7days

Допустимы записи вида 2days, 12hours, 30minutes. Слишком щедрый срок превращает неделю в марафон по заполнению диска, слишком короткий раздражает людей, которые за один вечер легитимно скачали большой датасет и готовы его разобрать. На практике для блоков на общих серверах ставят от 2 до 7 дней, а для inode срок часто укорачивают, потому что лавина мелких файлов опаснее одного крупного. Отдельному человеку таймер продлевается командой edquota -T ivan, это редкая, но полезная опция, когда конкретному сотруднику нужно время на переброску данных. Как только расход падает ниже soft, персональный таймер исчезает сам.

Контроль расхода и напоминания нарушителям

Назначить лимиты это полдела, смотреть за ними придётся постоянно. Сводная таблица по всем пользователям раздела выводится от root:

repquota -vs /home
*** Report for user quotas on device /dev/sdb1 (-)
Block grace time: 7days; Inode grace time: 7days
                        Block limits                File limits
User            used    soft    hard  grace    used  soft  hard  grace
----------------------------------------------------------------------
ivan       --     38M   10240M   12288M            812     0     0
maria      +-   9980M   10240M   12288M    5days  12044     0     0

Символы после имени читаются как два флажка: первый про блоки, второй про inode, знак минус значит в пределах, плюс значит перешагнут soft. У maria виден остаток льготного периода, 5 дней. Ключ -a в repquota собирает отчёт сразу по всем разделам с квотами, что удобно в еженедельном обходе.

Ручное наблюдение быстро надоедает, поэтому в пакете quota есть warnquota: служба читает те же таблицы, находит перерасходовавших soft и рассылает им письма. Тема, текст и адрес администратора для копии настраиваются в файле /etc/warnquota.conf (поля SUBJECT, MESSAGE, CC_TO), а запуск удобно повесить в cron, например строкой в /etc/crontab:

17 7 * * 1  root  /usr/sbin/warnquota

Такая строка отправляет напоминания каждый понедельник в 07:17, до начала рабочего дня. Ядро, со своей стороны, сообщает о каждом превышении через netlink, и служба quota_nld передаёт эти сообщения на консоль или в почту администратора, включается она через systemctl enable --now quota_nld. Команда quotastats покажет, как ядро справляется с нагрузкой: сколько структур учёта в кэше и были ли потери записей о квотах.

Для контраста стоит знать, что в файловой системе XFS квоты встроены изначально: файлы aquota.* там не создаются, quotacheck не нужен, а лимиты назначаются утилитой xfs_quota с подкомандами limit и timer. Принципы мягких и жёстких порогов общие для обеих систем, так что выработанная привычка переезжает вместе с администратором.

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