Skip to content

Commit

Permalink
client: ensure minimal cgroup controllers enabled
Browse files Browse the repository at this point in the history
This PR fixes a bug where Nomad could not operate properly on operating
systems that set the root cgroup.subtree_control to a set of controllers that
do not include the minimal set of controllers needed by Nomad.

Nomad needs these controllers enabled to operate:
- cpuset
- cpu
- io
- memory
- pids

Now, Nomad will ensure these controllers are enabled during Client initialization,
adding them to cgroup.subtree_control as necessary. This should be particularly
helpful on the RHEL/CentOS/Fedora family of system. Ubuntu systems should be
unaffected as they enable all controllers by default.

Fixes: #14494
  • Loading branch information
shoenig committed Oct 24, 2022
1 parent 563e5e3 commit fb67296
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .changelog/15027.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
client: Fixed a bug where Nomad could not detect cores on recent RHEL systems
```
34 changes: 32 additions & 2 deletions client/lib/cgutil/cpuset_manager_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"time"

"github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-set"
"github.com/hashicorp/nomad/helper"
"github.com/hashicorp/nomad/lib/cpuset"
"github.com/hashicorp/nomad/nomad/structs"
Expand Down Expand Up @@ -54,16 +55,21 @@ type cpusetManagerV2 struct {
}

func NewCpusetManagerV2(parent string, reservable []uint16, logger hclog.Logger) CpusetManager {
if err := minimumRootControllers(); err != nil {
logger.Error("failed to enabled minimum set of cgroup controllers; disable cpuset management", "error", err)
return new(NoopCpusetManager)
}

parentAbs := filepath.Join(CgroupRoot, parent)
if err := os.MkdirAll(parentAbs, 0o755); err != nil {
logger.Warn("failed to ensure nomad parent cgroup exists; disable cpuset management", "error", err)
logger.Error("failed to ensure nomad parent cgroup exists; disable cpuset management", "error", err)
return new(NoopCpusetManager)
}

if len(reservable) == 0 {
// read from group
if cpus, err := GetCPUsFromCgroup(parent); err != nil {
logger.Warn("failed to lookup cpus from parent cgroup; disable cpuset management", "error", err)
logger.Error("failed to lookup cpus from parent cgroup; disable cpuset management", "error", err)
return new(NoopCpusetManager)
} else {
reservable = cpus
Expand All @@ -80,6 +86,30 @@ func NewCpusetManagerV2(parent string, reservable []uint16, logger hclog.Logger)
}
}

// minimumControllers sets the minimum set of required controllers on the
// /sys/fs/cgroup/cgroup.subtree_control file. Some systems like Ubuntu turn on
// all controllers by default, and will be unaffected. Other systems like RHEL,
// CentOS, Fedora turn of most controllers, and provide a default that excludes
// controllers needed by Nomad. This helper ensures all of:
// [cpuset, cpu, io, memory, pids]
// are enabled.
func minimumRootControllers() error {
e := new(editor)
s, err := e.read("cgroup.subtree_control")
if err != nil {
return err
}
required := set.From[string]([]string{"cpuset", "cpu", "io", "memory", "pids"})
enabled := set.From[string](strings.Fields(s))
needed := required.Difference(enabled)
sb := new(strings.Builder)
for _, controller := range needed.List() {
sb.WriteString("+" + controller + " ")
}
activation := sb.String()
return e.write("cgroup.subtree_control", activation)
}

func (c *cpusetManagerV2) Init() {
c.logger.Debug("initializing with", "cores", c.initial)
}
Expand Down

0 comments on commit fb67296

Please sign in to comment.