Назад | Перейти на главную страницу

systemd, ограничения ЦП и / или памяти для каждого пользователя

Есть аналогичный вопрос: Cgroups, ограничение памяти на пользователя, но это решение не работает в «современных» системах, где иерархия групп управляется systemd.

Простое решение - создание шаблона user-UID.slice - не будет работать, потому что оно не поддерживается, см. https://github.com/systemd/systemd/issues/2556.

Есть ли способ добиться желаемого эффекта - управлять ресурсами процессора и / или памяти для каждого пользователя?

UPD: Я сохраню свое решение ради истории, но systemctl set-property должен вызываться во время входа в систему, используя pam_exec, видеть https://github.com/hashbang/shell-etc/pull/183. При таком подходе нет временного окна между входом пользователя в систему и установкой лимитов.

Мое решение. Интерфейс org.freedesktop.login1.Manage из /org/freedesktop/login1 объект испускает UserNew(u uid, o object_path) сигнал. Я написал простой демон, который слушает сигнал и каждый раз, когда он отправляется, устанавливает CPUAccounting=true для фрагмента только что вошедшего в систему пользователя.

UPD: Я сохраню свое решение ради истории, но systemctl set-property должен вызываться во время входа в систему, используя pam_exec, видеть https://github.com/hashbang/shell-etc/pull/183. При таком подходе нет временного окна между входом пользователя в систему и установкой лимитов.

Старое решение

Вот очень простой скрипт, который выполняет свою работу

#!/bin/bash

STATE=1 # 1 -- waiting for signal; 2 -- reading UID

dbus-monitor --system "interface=org.freedesktop.login1.Manager,member=UserNew" |
while read line
do
    case $STATE in
    1) [[ $line =~ member=UserNew ]] && STATE=2 ;;
    2) read dbus_type ID <<< $line
       systemctl set-property user-$ID.slice CPUAccounting=true
       STATE=1
    ;;
    esac
done

Его можно легко расширить для поддержки ограничений памяти для каждого пользователя.

Протестировал на виртуальной машине с двумя процессорами и двумя пользователями. Первый запуск пользователя dd if=/dev/zero of=/dev/null | dd if=/dev/zero of=/dev/null команда, а вторая запускает только один экземпляр dd. Без этого скрипта каждый экземпляр dd используется около 70% ЦП.

Затем я запустил скрипт, повторно зарегистрировал пользователей и начал dd команды снова. На этот раз два dd процессы первого пользователя занимали только 50% ЦП, а процесс второго пользователя занимал 100% ЦП. Также, systemd-cgtop показало, что /user.slice/user-UID1.slice и /user.slice/user-UID2.slice каждый занимает 100% процессорного времени, но первый срез содержит 6 задач, а второй - только 5 задач.

Когда я убиваю dd задача второго пользователя, первый пользователь начинает использовать 200% процессорного времени. Таким образом, у нас справедливое распределение ресурсов без искусственных ограничений типа «каждый пользователь может использовать только одно ядро».

Начиная с systemd v239, вы можете использовать drop-ins https://github.com/systemd/systemd/commit/5396624506e155c4bc10c0ee65b939600860ab67

# mkdir -p /etc/systemd/system/user-.slice.d
# cat > /etc/systemd/system/user-.slice.d/50-memory.conf << EOF
[Slice]
MemoryMax=1G
EOF
# systemctl daemon-reload

Вопрос, о котором вы упомянули, все еще открыт, но для меня это работает.

sudo systemctl edit --force user-1234.slice

Затем введите и сохраните это:

[Slice]
CPUQuota=10%

Я не уверен, почему это работает.

Другая возможность достижения ограничений ЦП или памяти с помощью контрольных групп - использование libpam-cgroup, который может выбрать профиль при входе в систему из /etc/cgconfig.conf. Типичный пример:

group limited {
   cpu {
     cpu.cfs_period_us = "1000000";
     cpu.cfs_quota_us = "500000";
   }
   memory {
      memory.limit_in_bytes = "1G";
      memory.memsw.limit_in_bytes = "1G";
   }
}

Это создает interactive группа, ограничивающая ресурсы до 1 ГБ памяти и 50% ядра ЦП (500 мс в секунду от общего времени ЦП). Затем /etc/cgrules.conf файл может быть настроен для помещения определенной группы или пользователей в эту группу:

root       cpu,memory      /
@nolimits  cpu,memory      /
*          cpu,memory      /limited

В таком случае, root и все, принадлежащие к nolimits group получит стандартную (неограниченную) контрольную группу, в то время как все остальные пользователи будут ограничены политикой, установленной выше.

Используется первая совпадающая строка, поэтому можно иметь неограниченную политику по умолчанию, ограничивая конкретного пользователя или группу, например:

spam       cpu,memory      /limited
*          cpu,memory      /

В этом случае мы ограничиваем spam пользователь, и никто другой.

Чтобы это сработало, pam_cgroup.so должен быть включен, например, в /etc/pam.d/sshd Профиль PAM:

session    optional     pam_cgroup.so

Наконец, cgrules.conf файл должен быть проанализирован при загрузке. Этого можно добиться с помощью простой службы systemd:

[Unit]
Description=Load cgroup profiles

[Service]
Type=oneshot
ExecStart=/usr/sbin/cgconfigparser -l /etc/cgconfig.conf

[Install]
WantedBy=multi-user.target

Это можно сохранить, например, в /etc/systemd/system/loadcg.service а затем включен с помощью systemctl enable loadcg.