Есть аналогичный вопрос: 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
.