Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cgroup: make paths available, simplify getting manager #3216

Merged
merged 9 commits into from
Sep 23, 2021

Conversation

kolyshkin
Copy link
Contributor

@kolyshkin kolyshkin commented Sep 15, 2021

This is based on and currently includes #3215. used to be part of #3131.

High level overview:

  • allow cgroup instantiation (NewManager) to return errors;
  • initialize cgroup paths during instantiation, so paths are always available
    (and now we can use a new instance for e.g. Destroy or Exists,
    which was not possible before);
  • simplifies cgroup manager instantiation, replacing the complicated
    logic of choosing a cgroup manager with a simple manager.New() call.
  • fixes some panics in cgroup manager (with Resources==nil).

This helps further commits, and makes cgroup manager easier to use
from e.g. Kubernetes.

Fixes: #3177
Fixes: #3178

Proposed changelog entry

libcontainer API changes:
* New configs.Cgroup structure fields (#3177):
  * Systemd (whether to use systemd cgroup manager);
  * Rootless (whether to use rootless cgroups).
* New cgroups/manager package aiming to simplify cgroup manager instantiation. (#3177)
* All cgroup managers' instantiation methods now initialize cgroup paths and can return errors.
  This allows to use any cgroup manager method (e.g. Exists, Destroy, Set, GetStats) right after
  instantiation, which was not possible before (as paths were initialized in Apply only). (#3178)

@kolyshkin

This comment has been minimized.

@kolyshkin kolyshkin requested review from cyphar, thaJeztah and AkihiroSuda and removed request for cyphar, thaJeztah and AkihiroSuda September 15, 2021 22:27
@kolyshkin kolyshkin added this to the 1.1.0 milestone Sep 15, 2021
@kolyshkin kolyshkin marked this pull request as draft September 20, 2021 19:22
@kolyshkin

This comment has been minimized.

@cyphar
Copy link
Member

cyphar commented Sep 21, 2021

How many PRs until the cgroup handling is completely rewritten, it feels like this code is runc's Ship of Theseus. 😉 I'll review this once #3215 is done.

Comment on lines +58 to +59
// Some v1 controllers (cpu, cpuset, and devices) expect
// cgroups.Resources to not be nil in Apply.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not 100% sure this is the right thing to do. Maybe we should not require it.

It is required because

  1. cpu needs to set RT params, see
    // We should set the real-Time group scheduling settings before moving
    // in the process because if the process is already in SCHED_RR mode
    // and no RT bandwidth is set, adding it will fail.
    if err := s.SetRtSched(path, r); err != nil {
  2. cpuset needs to set cpus and mems in the parent cgroup (yes this is super ugly), see
    // 'ensureParent' start with parent because we don't want to
    // explicitly inherit from parent, it could conflict with
    // 'cpuset.cpu_exclusive'.
    if err := cpusetEnsureParent(filepath.Dir(dir)); err != nil {
    return err
    }
    if err := os.Mkdir(dir, 0o755); err != nil && !os.IsExist(err) {
    return err
    }
    // We didn't inherit cpuset configs from parent, but we have
    // to ensure cpuset configs are set before moving task into the
    // cgroup.
    // The logic is, if user specified cpuset configs, use these
    // specified configs, otherwise, inherit from parent. This makes
    // cpuset configs work correctly with 'cpuset.cpu_exclusive', and
    // keep backward compatibility.
    if err := s.ensureCpusAndMems(dir, r); err != nil {
  3. devices need to check SkipDevices flag, see
    if r.SkipDevices {

Now, we can modify all the three places to allow nil (and assume RT, cpus/mems, and SkipDevices are not set), and drop this requirement. After all, it is only for some specific cases, and not always required.

Honestly I don't know which way is better.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that creating a cgroup should be separated from the applying values, so my preference is to allow nil at those 3 places and remove this check.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

creating a cgroup should be separated from the applying values

Alas, with cgroup v1 it is not possible in some cases (outlined above), so as much as we want to separate create and set, we can not.

In this case, though, we can allow nil (the price to pay is to fail later on Apply in case either of CPU RT priority, cpus/mems, SkipDevices is set).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OTOH it's easier to require non-nil Resources here, otherwise it's hard to explain later why cpu/cpuset/devices configuration has unexpectedly failed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, let's leave it as is, unless there are other opinions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better to leave it as-is for the moment. The fact that we need to configure other cgroups for cpu makes me think that allowing nil Resources will make this even more ugly than it is today.

@kolyshkin kolyshkin force-pushed the manager-new branch 2 times, most recently from b3d89b9 to 8fb2e2c Compare September 22, 2021 00:28
// For cgroup v1, the keys are controller/subsystem name, and the values
// are absolute filesystem paths to the appropriate cgroups.
//
// For cgroup v2, the only key allowed is "" (empty string), and the value
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Instead of "", could we use a predefined string like "unified"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I seriously considered doing it (about a year ago) and ultimately decided against it, mostly because changing it requires adding some backward-compatible hacks (this key and value is saved into state.json). I am not even taking external users such as kubernetes and cadvisor into account -- with those it will be a bloody mess.

Note that this is the convention that is universally used in many places in runc, I am not introducing it here, merely documenting the fact (which is also documented in some other places, for example

// GetPaths returns cgroup path(s) to save in a state file in order to
// restore later.
//
// For cgroup v1, a key is cgroup subsystem name, and the value is the
// path to the cgroup for this subsystem.
//
// For cgroup v2 unified hierarchy, a key is "", and the value is the
// unified path.
GetPaths() map[string]string
)

libcontainer/cgroups/fs2/fs2.go Show resolved Hide resolved
libcontainer/cgroups/systemd/v1.go Outdated Show resolved Hide resolved
if cg.Resources != nil && cg.Resources.Unified != nil {
return nil, cgroups.ErrV1NoUnified
}
if paths == nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking for potential corner-cases: is an empty map[string]string to be considered different for this code path, or should it be treated the same? (if "the same", we'd have to check for len(paths))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forget to reply -- I thought about that and I guess the non-nil map (even when empty) should be handled as it is right now, IOW it means "paths are set, no need to guess any". Not sure if/when this can be useful but theoretically it kind of makes sense.

libcontainer/cgroups/fs2/fs2.go Show resolved Hide resolved
1. Make Rootless and Systemd flags part of config.Cgroups.

2. Make all cgroup managers (not just fs2) return error (so it can do
   more initialization -- added by the following commits).

3. Replace complicated cgroup manager instantiation in factory_linux
   by a single (and simple) libcontainer/cgroups/manager.New() function.

4. getUnifiedPath is simplified to check that only a single path is
   supplied (rather than checking that other paths, if supplied,
   are the same).

[v2: can't -> cannot]

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
1. Separate path initialization logic from Apply to initPaths,
   and call initPaths from NewManager, so:
   - we can error out early (in NewManager rather than Apply);
   - always have m.paths available (e.g. in Destroy or Exists).
   - do not unnecessarily call subsysPath from Apply in case
     the paths were already provided.

2. Add a check for non-nil cgroups.Resources to NewManager,
   since initPaths, as well as some controller's Apply methods,
   need it.

3. Move cgroups.Resources.Unified check from Apply to NewManager,
   so we can error out early (same check exists in Set).

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
This is already documented but I guess more explanations (in particular,
why the path is being removed from paths) won't hurt.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
This way we
 - won't re-initialize the paths if they were provided;
 - will always have paths ready for every method.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
cgName and cgParent are only used when cgPath is empty, so move
their cleaning to the body of the appropriate "if" statement.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
This fixes the same issue as e.g. commit 4f8ccc5
but in a more universal way.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
Many operations require fsMgr, so let's create it right in
NewUnifiedManager and reuse.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
Cgroup controllers should never panic, and yet sometimes they do.

Add a unit test to check that controllers never panic when called with
nil arguments and/or resources, and fix a few found cases.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
It is assumed that m.config is not nil, so these checks are redundant
(in case it is nil, NewManager panics and this code is unreachable).

Note that cgroups/manager.New checks that config is not nil.

Remove them.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
@kolyshkin
Copy link
Contributor Author

Implemented some very minor changes (can't -> cannot plus the last commit) based on @thaJeztah review.

Copy link
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks!

@mrunalp
Copy link
Contributor

mrunalp commented Sep 23, 2021

This is a big improvement in cleaning up the code. Thanks!

@mrunalp mrunalp merged commit b1b0e7f into opencontainers:master Sep 23, 2021
@kolyshkin
Copy link
Contributor Author

@AkihiroSuda PTAL (this is merged but if there's anything you feel is wrong here, we can address in a followup)

@cyphar
Copy link
Member

cyphar commented Sep 24, 2021

FWIW, LGTM.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants