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 }