systemd-run to limit something's RAM consumption on the fly
A year ago I wrote about using cgroups to limit something's
RAM consumption, for limiting the resources
make'ing Firefox could use when I did it. At the time my
approach with an explicitly configured cgroup and the direct use of
cgexec was the only way to do it on my machines; although systemd has facilities
to do this in general, my version could not do this for ad hoc
user-run programs. Well, I've upgraded to Fedora 21 and that's now
changed, so here's a quick guide to doing it the systemd way.
The core command is
we use to start a command with various limits set. The basic command
systemd-run --user --scope -p LIM1=VAL1 -p LIM2=VAL2 [...] CMD ARG [...]
--user makes things run as ourselves with no special privileges,
and is necessary to get things to run. The
means 'run this as a subcommand', although systemd considers it a
named object while it's running. Systemd-run will make up a name
for it (and report the name when it starts your command), or you
--unit NAME to give it your own name.
The limits you can set are covered in systemd.resource-control. Since systemd is just using cgroups, the limits you can set up are just the cgroup limits (and the documentation will tell you exactly what the mapping is, if you need it). Conveniently, systemd-run allows you to specify memory limits in Gb (or Mb), not just bytes. The specific limits I set up in the original entry give us a final command of:
systemd-run --user --scope -p MemoryLimit=3G -p CPUShares=512 -p BlockIOWeight=500 make
(Here I'm once again running
make as my example command.)
You can inspect the parameters of your new scope with '
show --user <scope>', and change them on the fly with '
set-property --user <scope> LIM=VAL'. I'll leave potential uses
of this up to your imagination.
systemd-cgls can be used to show
all of the scopes and find any particular one that's running this
way (and show its processes).
(It would be nice if
systemd-cgtop gave you a nice rundown of what
resources were getting used by your confined scope, but as far as I can
tell it doesn't. Maybe I'm missing a magic trick here.)
Now, there's a subtle semantic difference between what we're doing
here and what I did in the original entry. With
everything that ran in our
confine cgroup shared the same limit
even if they were started completely separately. With
separately started commands have separate limits; if you start two
makes in parallel, each of them can use 3 GB of RAM. I'm not sure
yet how you fix this in the official systemd way, but I think it
involves defining a slice
and then attaching our scopes to it.
(On the other hand, this separation of limits for separate commands may be something you consider a feature.)
cgexec et al
In Fedora 20 and Fedora 21,
cgexec works okay for me but I found
that systemd would periodically clear out my custom
and I'd have to do '
systemctl restart cgconfig' to recreate it
(generally anything that caused systemd to reload itself would do
yum package updates that poked systemd). Now that
the Fedora 21 version of
-p, using it and
doing things the systemd way is just easier.
(I wrap the entire invocation up in a script, of course.)