Using cgroups to limit something's RAM consumption (a practical guide)
Suppose, not entirely hypothetically, that you periodically rebuild Firefox from source and that you've discovered that parts of this build process kill your machine due to memory (over)use. You would like to fix this by somehow choking off the total amount of RAM that the whole Firefox build process uses. The relatively simple tool to use for this is a cgroup.
There is probably lots of documentation on the full details of cgroups floating around. This is a practical guide instead.
First we need a cgroup that will actually apply memory limits.
Ad-hoc cgroups are created on the fly with cgcreate
(which is
run as root
):
cgcreate -t cks -a cks -g memory,cpu,blkio:confine
I'm doing some overkill here; in theory we only need to limit memory
usage. But who cares. As I found out, it's important to specify both
-t
and -a
here; -a
lets us set limits, -t
lets us actually
put something into the new cgroup.
The easiest way to set limits is by writing to files in
/sys/fs/cgroup/controller/path
. Here we have controllers
for memory, cpu, and blkio and our path under them is confine
,
so:
cd /sys/fs/cgroup # This is 3 GB echo 3221225472 >memory/confine/memory.limit_in_bytes # only gets half of contended CPU and disk bandwidth # (in theory) echo 512 >cpu/confine/cpu.shares echo 500 >blkio/confine/blkio.weight
(Since we gave ourselves permissions with -a
, we can set all
of these limits directly without being root
.)
What parameters controllers take is established partly by poking
around in /sys/fs/cgroup
, partly by experimentation, partly by
Internet searches, and sometimes from the official kernel
documentation.
Where limits exist (and work) they may have side effects; for
example, limiting total RAM here is going to force a memory-hungry
program to swap, using up a bunch of disk IO bandwidth.
(If you want this cgroup and its settings to persist over reboot you can
make a suitable entry in /etc/cgconfig.conf
. On Fedora you may also
need to make sure that the cgconfig
service is enabled.)
Finally we need to actually run our make
or whatever so that it is
put into our new 'confine
' cgroup and it and its children have their
total RAM usage limited the way we want. This is done on the fly with
cgexec
(run as ourselves):
cgexec -g memory,cpu,blkio:confine --sticky make
You don't need --sticky
in various common situations, for example if
you're not running the cgroups automatic classification daemon. But I
don't think it does any harm to supply it and anyways you may well
want to wrap this magic command up in a script so you don't have to
remember it.
You can check to see that cgexec
is properly putting things into
cgroups by looking at /proc/pid/cgroup
to see what cgroups a
suitable process is part of. In this case you would expect to see
memory:/confine
among the list. Testing whether your actual cgroup
controller settings are working and doing what you want is beyond the
scope of this entry.
The good news is that this seems to work for me. My Firefox build process has been significantly tamed.
(I've looked at fair share scheduling with cgroups before, which certainly helped here. People have written all of this information down in bits and pieces and partial explanation and Stack Overflow answers and so on, but since I put it together I want to write it down all in one place for later use. (I'm sure there'll be later use.))
|
|