На многопользовательском сервере общий жёсткий диск быстро превращается в поле битвы за место. Один человек выгружает в домашний каталог архив на сотню гигабайт, и через сутки свободного пространства не остаётся ни у кого: логи перестают писаться, базы данных отказываются принимать записи, резервные копии падают на середине. Дисковые квоты решают проблему на уровне файловой системы 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 он часто отсутствует. Вся подготовка укладывается в пять шагов:
- пакет утилит ставится командой
apt install quota, в RHEL и Fedora пакет называется так же, но устанавливается черезdnf install quota; - в файле
/etc/fstabк опциям раздела с домашними каталогами добавляютсяusrquota,grpquota, затем выполняетсяmount -o remount /home; - команда
quotacheck -cugm /homeсканирует раздел и создаёт файлы учётаaquota.userиaquota.group; - квоты включаются командой
quotaon -v /home; - статус проверяется командой
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. Принципы мягких и жёстких порогов общие для обеих систем, так что выработанная привычка переезжает вместе с администратором.
Квоты не отменяют ни резервного копирования, ни мониторинга, но снимают самую болезненную проблему общего сервера, внезапный отказ всего хозяйства из-за аппетитов одного аккаунта. Половина пользователей никогда не упрётся в свой лимит, вторая половина будет просить его расширить, и это уже нормальный управляемый разговор с цифрами на экране вместо внезапной аварии в пятницу вечером.