github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/client/lib/cgutil/cgutil_linux.go (about)

     1  //go:build linux
     2  
     3  package cgutil
     4  
     5  import (
     6  	"fmt"
     7  	"os"
     8  	"path/filepath"
     9  
    10  	"github.com/hashicorp/go-hclog"
    11  	"github.com/hashicorp/nomad/helper/uuid"
    12  	"github.com/opencontainers/runc/libcontainer/cgroups"
    13  	lcc "github.com/opencontainers/runc/libcontainer/configs"
    14  )
    15  
    16  // UseV2 indicates whether only cgroups.v2 is enabled. If cgroups.v2 is not
    17  // enabled or is running in hybrid mode with cgroups.v1, Nomad will make use of
    18  // cgroups.v1
    19  //
    20  // This is a read-only value.
    21  var UseV2 = cgroups.IsCgroup2UnifiedMode()
    22  
    23  // GetCgroupParent returns the mount point under the root cgroup in which Nomad
    24  // will create cgroups. If parent is not set, an appropriate name for the version
    25  // of cgroups will be used.
    26  func GetCgroupParent(parent string) string {
    27  	switch {
    28  	case parent != "":
    29  		return parent
    30  	case UseV2:
    31  		return DefaultCgroupParentV2
    32  	default:
    33  		return DefaultCgroupV1Parent
    34  	}
    35  }
    36  
    37  // CreateCPUSetManager creates a V1 or V2 CpusetManager depending on system configuration.
    38  func CreateCPUSetManager(parent string, reservable []uint16, logger hclog.Logger) CpusetManager {
    39  	parent = GetCgroupParent(parent) // use appropriate default parent if not set in client config
    40  	switch {
    41  	case UseV2:
    42  		return NewCpusetManagerV2(parent, reservable, logger.Named("cpuset.v2"))
    43  	default:
    44  		return NewCpusetManagerV1(parent, reservable, logger.Named("cpuset.v1"))
    45  	}
    46  }
    47  
    48  // GetCPUsFromCgroup gets the effective cpuset value for the given cgroup.
    49  func GetCPUsFromCgroup(group string) ([]uint16, error) {
    50  	group = GetCgroupParent(group)
    51  	if UseV2 {
    52  		return getCPUsFromCgroupV2(group)
    53  	}
    54  	return getCPUsFromCgroupV1(group)
    55  }
    56  
    57  // CgroupScope returns the name of the scope for Nomad's managed cgroups for
    58  // the given allocID and task.
    59  //
    60  // e.g. "<allocID>.<task>.scope"
    61  //
    62  // Only useful for v2.
    63  func CgroupScope(allocID, task string) string {
    64  	return fmt.Sprintf("%s.%s.scope", allocID, task)
    65  }
    66  
    67  // ConfigureBasicCgroups will initialize a cgroup and modify config to contain
    68  // a reference to its path.
    69  //
    70  // v1: creates a random "freezer" cgroup which can later be used for cleanup of processes.
    71  // v2: does nothing.
    72  func ConfigureBasicCgroups(config *lcc.Config) error {
    73  	if UseV2 {
    74  		return nil
    75  	}
    76  
    77  	id := uuid.Generate()
    78  	// In v1 we must setup the freezer cgroup ourselves.
    79  	subsystem := "freezer"
    80  	path, err := GetCgroupPathHelperV1(subsystem, filepath.Join(DefaultCgroupV1Parent, id))
    81  	if err != nil {
    82  		return fmt.Errorf("failed to find %s cgroup mountpoint: %v", subsystem, err)
    83  	}
    84  	if err = os.MkdirAll(path, 0755); err != nil {
    85  		return err
    86  	}
    87  	config.Cgroups.Path = path
    88  	return nil
    89  }
    90  
    91  // FindCgroupMountpointDir is used to find the cgroup mount point on a Linux
    92  // system.
    93  //
    94  // Note that in cgroups.v1, this returns one of many subsystems that are mounted.
    95  // e.g. a return value of "/sys/fs/cgroup/systemd" really implies the root is
    96  // "/sys/fs/cgroup", which is interesting on hybrid systems where the 'unified'
    97  // subsystem is mounted as if it were a subsystem, but the actual root is different.
    98  // (i.e. /sys/fs/cgroup/unified).
    99  //
   100  // As far as Nomad is concerned, UseV2 is the source of truth for which hierarchy
   101  // to use, and that will only be a true value if cgroups.v2 is mounted on
   102  // /sys/fs/cgroup (i.e. system is not in v1 or hybrid mode).
   103  //
   104  // ➜ mount -l | grep cgroup
   105  // tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,mode=755,inode64)
   106  // cgroup2 on /sys/fs/cgroup/unified type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate)
   107  // cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,name=systemd)
   108  // cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
   109  // (etc.)
   110  func FindCgroupMountpointDir() (string, error) {
   111  	mount, err := cgroups.GetCgroupMounts(false)
   112  	if err != nil {
   113  		return "", err
   114  	}
   115  	// It's okay if the mount point is not discovered
   116  	if len(mount) == 0 {
   117  		return "", nil
   118  	}
   119  	return mount[0].Mountpoint, nil
   120  }
   121  
   122  // CopyCpuset copies the cpuset.cpus value from source into destination.
   123  func CopyCpuset(source, destination string) error {
   124  	correct, err := cgroups.ReadFile(source, "cpuset.cpus")
   125  	if err != nil {
   126  		return err
   127  	}
   128  
   129  	err = cgroups.WriteFile(destination, "cpuset.cpus", correct)
   130  	if err != nil {
   131  		return err
   132  	}
   133  
   134  	return nil
   135  }