Уже довольно давно в лиуксах используется такая чудная система для ограничения приложений по ресурсам как cgroups. Руки к ней я тянул тоже довольно давно и вот наконец-то я дотянулся до них.
Смысл в том, что мне наконец надоело, что либо браузер съедает CPU либо память, либо какое-то приложение (напр. rsync/cp/git) приседает на диск с такой силой, что работать просто невозможно. Соответственно, таки прожорливае программы надо как-то ограничивать. Жёсткие ограничения - это бессмысленно так как если есть простаивающие ресурсы, то почему бы их не передать нуждающемуся приложению, но если оно наглеет и не хочет, например, делиться CPU-шкой с другим приложением - это какбэ не сильно гуманно, должна быть ручка, которая как бы приложение зубами ни упиралось, всё равно легко и беззатейливо могла бы его подвинуть. И ручка такая есть - как раз cgroup для этого и создавалась.
Итак, к делу. После долгих забегов по граблям, выяснилось, что для оптимального руления группами нам необходимо:
<ul>
<li>чтобы модуль cn.ko (Connector - unified userspace <-> kernelspace linker) был встроен в ядро иначе будет невозможно находить процессы и автоматически пихать их в нужную cgroup.</li>
<li>Далее, несмотря на то, что cgroup-пы можно монтировать по отдельности: отдельно cpu, отдельно cpuacct, отдельно blkio и тд - для более эффективной работы лучше их смонтировать в одну кучу, напр. в sys/fs/cgroup.</li>
<li>Кроме того, во всех не sysdefault грумппах необходимо задавать cpuset.cpus (это ядра, на которых будут работать процессы в группе) и cpuset.mems (это домены памяти в NUMA-представлении, если система не NUMA, то = 0).</li>
</ul>
Поскольку я на практике использую Slackware Linux, то из каробки у меня есть 2 сервиса - это cgconfig и cgrules(cgred).
В стартовом скрипте для cgconfig пришлось подправить 2 момента: перед в процедуре запуска нужно вызывать останов, на всякий случай, если подсистемы запущены и cgropfs смонтированы (а такое бывает, когда cgroups не модулями, а вкомпилены в ядро) и второй момент - останов cgconfig происходит не полностью, для полного останова (и размонтирования) необходимо разика 3 остановить сервис, соответственно, в скрипте это тоже пришлось подправить.
Кроме того, cn.ko был собран модулем, так что пришлось пересобрать ядро, благо в slackware пересобрать ядро довольно просто :) Заодно выяснилось, что cgred работает на ядрах, по версии ниже чем 3.15, в котором влили какое-то ограничение по количеству процессов, управляющих cgroups, видимо с целью <a href="http://boycottsystemd.org/">привязать ядро и userspace</a> к systemd, там вроде нужно уведомить ядро, какой pid вносит процессы в различные cgroup.
Традиционно, пачка конфигов:
/etc/cgconfig.conf:
mount {
cpuset = "/sys/fs/cgroup";
cpu = "/sys/fs/cgroup";
cpuacct = "/sys/fs/cgroup";
memory = "/sys/fs/cgroup";
devices = "/sys/fs/cgroup";
freezer = "/sys/fs/cgroup";
net_cls = "/sys/fs/cgroup";
blkio = "/sys/fs/cgroup";
perf_event = "/sys/fs/cgroup";
}
group sysdefault {
cpu {
cpu.shares = 500;
}
blkio {
blkio.weight = 500;
}
}
group browser {
cpu {
cpu.shares = 100;
}
cpuset{
cpuset.cpus = 0-3;
cpuset.mems = 0;
}
blkio {
blkio.weight = 200;
}
}
group io {
cpu {
cpu.shares = 250;
}
cpuset{
cpuset.cpus = 0-3;
cpuset.mems = 0;
}
blkio {
blkio.weight = 100;
}
}
/etc/cgrules.conf:
*:firefox cpu,blkio browser/
*:thunderbird cpu,blkio browser/
*:cp cpu,blkio io/
*:mv cpu,blkio io/
*:svn cpu,blkio io/
*:hg cpu,blkio io/
*:git cpu,blkio io/
*:rsync cpu,blkio io/
*:mc cpu,blkio io/