mirror of
https://github.com/criyle/go-sandbox.git
synced 2025-11-04 14:49:53 +08:00
Add support to cpuset cgroup
This commit is contained in:
parent
7ce98ffa42
commit
6d71f9dc4b
@ -6,68 +6,64 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func BenchmarkCgroup(b *testing.B) {
|
func BenchmarkCgroup(b *testing.B) {
|
||||||
builder, err := NewBuilder("benchmark").WithCPUAcct().WithMemory().WithPids().FilterByEnv()
|
builder, err := NewBuilder("benchmark").WithCPUSet().WithCPUAcct().WithMemory().WithPids().FilterByEnv()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Error(err)
|
b.Fatal(err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
cg, err := builder.Build()
|
cg, err := builder.Build()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Error(err)
|
b.Fatal(err)
|
||||||
return
|
}
|
||||||
|
if err := cg.SetCpusetCpus([]byte("0")); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
if err := cg.SetMemoryLimitInBytes(4096); err != nil {
|
if err := cg.SetMemoryLimitInBytes(4096); err != nil {
|
||||||
b.Error(err)
|
b.Fatal(err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if err := cg.SetPidsMax(1); err != nil {
|
if err := cg.SetPidsMax(1); err != nil {
|
||||||
b.Error(err)
|
b.Fatal(err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if _, err := cg.CpuacctUsage(); err != nil {
|
if _, err := cg.CpuacctUsage(); err != nil {
|
||||||
b.Error(err)
|
b.Fatal(err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if _, err := cg.MemoryMaxUsageInBytes(); err != nil {
|
if _, err := cg.MemoryMaxUsageInBytes(); err != nil {
|
||||||
b.Error(err)
|
b.Fatal(err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
cg.Destroy()
|
cg.Destroy()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCgroup(t *testing.T) {
|
func TestCgroupAll(t *testing.T) {
|
||||||
// ensure root privillege when testing
|
// ensure root privillege when testing
|
||||||
if os.Getuid() != 1 {
|
if os.Getuid() != 0 {
|
||||||
t.Skip("no root privillege")
|
t.Skip("no root privillege")
|
||||||
}
|
}
|
||||||
builder, err := NewBuilder("test").WithCPUAcct().WithMemory().WithPids().FilterByEnv()
|
builder, err := NewBuilder("test").WithCPUSet().WithCPUAcct().WithMemory().WithPids().FilterByEnv()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Fatal(err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
cg, err := builder.Build()
|
cg, err := builder.Build()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Fatal(err)
|
||||||
return
|
}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
cg.Destroy()
|
||||||
|
})
|
||||||
|
if err := cg.SetCpusetCpus([]byte("0")); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if err := cg.SetMemoryLimitInBytes(4096); err != nil {
|
if err := cg.SetMemoryLimitInBytes(4096); err != nil {
|
||||||
t.Error(err)
|
t.Fatal(err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if err := cg.SetPidsMax(1); err != nil {
|
if err := cg.SetPidsMax(1); err != nil {
|
||||||
t.Error(err)
|
t.Fatal(err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if _, err := cg.CpuacctUsage(); err != nil {
|
if _, err := cg.CpuacctUsage(); err != nil {
|
||||||
t.Error(err)
|
t.Fatal(err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if _, err := cg.MemoryMaxUsageInBytes(); err != nil {
|
if _, err := cg.MemoryMaxUsageInBytes(); err != nil {
|
||||||
t.Error(err)
|
t.Fatal(err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
cg.Destroy()
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,8 +8,12 @@ import (
|
|||||||
// Builder builds cgroup directories
|
// Builder builds cgroup directories
|
||||||
// available: cpuacct, memory, pids
|
// available: cpuacct, memory, pids
|
||||||
type Builder struct {
|
type Builder struct {
|
||||||
Prefix string
|
Prefix string
|
||||||
CPUAcct, Memory, Pids bool
|
|
||||||
|
CPUSet bool
|
||||||
|
CPUAcct bool
|
||||||
|
Memory bool
|
||||||
|
Pids bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBuilder return a dumb builder without any sub-cgroup
|
// NewBuilder return a dumb builder without any sub-cgroup
|
||||||
@ -19,6 +23,12 @@ func NewBuilder(prefix string) *Builder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithCPUSet includes cpuset cgroup
|
||||||
|
func (b *Builder) WithCPUSet() *Builder {
|
||||||
|
b.CPUSet = true
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
// WithCPUAcct includes cpuacct cgroup
|
// WithCPUAcct includes cpuacct cgroup
|
||||||
func (b *Builder) WithCPUAcct() *Builder {
|
func (b *Builder) WithCPUAcct() *Builder {
|
||||||
b.CPUAcct = true
|
b.CPUAcct = true
|
||||||
@ -43,6 +53,7 @@ func (b *Builder) FilterByEnv() (*Builder, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return b, err
|
return b, err
|
||||||
}
|
}
|
||||||
|
b.CPUSet = b.CPUSet && m["cpuset"]
|
||||||
b.CPUAcct = b.CPUAcct && m["cpuacct"]
|
b.CPUAcct = b.CPUAcct && m["cpuacct"]
|
||||||
b.Memory = b.Memory && m["memory"]
|
b.Memory = b.Memory && m["memory"]
|
||||||
b.Pids = b.Pids && m["pids"]
|
b.Pids = b.Pids && m["pids"]
|
||||||
@ -56,6 +67,7 @@ func (b *Builder) String() string {
|
|||||||
name string
|
name string
|
||||||
enabled bool
|
enabled bool
|
||||||
}{
|
}{
|
||||||
|
{"cpuset", b.CPUSet},
|
||||||
{"cpuacct", b.CPUAcct},
|
{"cpuacct", b.CPUAcct},
|
||||||
{"memory", b.Memory},
|
{"memory", b.Memory},
|
||||||
{"pids", b.Pids},
|
{"pids", b.Pids},
|
||||||
|
|||||||
@ -3,46 +3,63 @@ package cgroup
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Cgroup is the combination of sub-cgroups
|
// Cgroup is the combination of sub-cgroups
|
||||||
type Cgroup struct {
|
type Cgroup struct {
|
||||||
prefix string
|
prefix string
|
||||||
cpuacct, memory, pids *SubCgroup
|
|
||||||
|
cpuset *SubCgroup
|
||||||
|
cpuacct *SubCgroup
|
||||||
|
memory *SubCgroup
|
||||||
|
pids *SubCgroup
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build creates new cgrouup directories
|
// Build creates new cgrouup directories
|
||||||
func (b *Builder) Build() (cg *Cgroup, err error) {
|
func (b *Builder) Build() (cg *Cgroup, err error) {
|
||||||
var (
|
var (
|
||||||
cpuacctPath, memoryPath, pidsPath string
|
cpuSetPath, cpuacctPath, memoryPath, pidsPath string
|
||||||
)
|
)
|
||||||
// if failed, remove potential created directory
|
// if failed, remove potential created directory
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
remove(cpuSetPath)
|
||||||
remove(cpuacctPath)
|
remove(cpuacctPath)
|
||||||
remove(memoryPath)
|
remove(memoryPath)
|
||||||
remove(pidsPath)
|
remove(pidsPath)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
if b.CPUAcct {
|
for _, c := range []struct {
|
||||||
if cpuacctPath, err = CreateSubCgroupPath("cpuacct", b.Prefix); err != nil {
|
enable bool
|
||||||
|
name string
|
||||||
|
path *string
|
||||||
|
}{
|
||||||
|
{b.CPUSet, "cpuset", &cpuSetPath},
|
||||||
|
{b.CPUAcct, "cpuacct", &cpuacctPath},
|
||||||
|
{b.Memory, "memory", &memoryPath},
|
||||||
|
{b.Pids, "pids", &pidsPath},
|
||||||
|
} {
|
||||||
|
if !c.enable {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if *c.path, err = CreateSubCgroupPath(c.name, b.Prefix); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if b.Memory {
|
|
||||||
if memoryPath, err = CreateSubCgroupPath("memory", b.Prefix); err != nil {
|
if b.CPUSet {
|
||||||
return
|
if err = initCpuset(cpuSetPath); err != nil {
|
||||||
}
|
|
||||||
}
|
|
||||||
if b.Pids {
|
|
||||||
if pidsPath, err = CreateSubCgroupPath("pids", b.Prefix); err != nil {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Cgroup{
|
return &Cgroup{
|
||||||
prefix: b.Prefix,
|
prefix: b.Prefix,
|
||||||
|
cpuset: NewSubCgroup(cpuSetPath),
|
||||||
cpuacct: NewSubCgroup(cpuacctPath),
|
cpuacct: NewSubCgroup(cpuacctPath),
|
||||||
memory: NewSubCgroup(memoryPath),
|
memory: NewSubCgroup(memoryPath),
|
||||||
pids: NewSubCgroup(pidsPath),
|
pids: NewSubCgroup(pidsPath),
|
||||||
@ -51,14 +68,10 @@ func (b *Builder) Build() (cg *Cgroup, err error) {
|
|||||||
|
|
||||||
// AddProc writes cgroup.procs to all sub-cgroup
|
// AddProc writes cgroup.procs to all sub-cgroup
|
||||||
func (c *Cgroup) AddProc(pid int) error {
|
func (c *Cgroup) AddProc(pid int) error {
|
||||||
if err := c.cpuacct.WriteUint(cgroupProcs, uint64(pid)); err != nil {
|
for _, s := range []*SubCgroup{c.cpuset, c.cpuset, c.memory, c.pids} {
|
||||||
return err
|
if err := s.WriteUint(cgroupProcs, uint64(pid)); err != nil {
|
||||||
}
|
return err
|
||||||
if err := c.memory.WriteUint(cgroupProcs, uint64(pid)); err != nil {
|
}
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := c.pids.WriteUint(cgroupProcs, uint64(pid)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -66,18 +79,24 @@ func (c *Cgroup) AddProc(pid int) error {
|
|||||||
// Destroy removes dir for sub-cgroup, errors are ignored if remove one failed
|
// Destroy removes dir for sub-cgroup, errors are ignored if remove one failed
|
||||||
func (c *Cgroup) Destroy() error {
|
func (c *Cgroup) Destroy() error {
|
||||||
var err1 error
|
var err1 error
|
||||||
if err := remove(c.cpuacct.path); err != nil {
|
for _, s := range []*SubCgroup{c.cpuset, c.cpuset, c.memory, c.pids} {
|
||||||
err1 = err
|
if err := remove(s.path); err != nil {
|
||||||
}
|
err1 = err
|
||||||
if err := remove(c.memory.path); err != nil {
|
}
|
||||||
err1 = err
|
|
||||||
}
|
|
||||||
if err := remove(c.pids.path); err != nil {
|
|
||||||
err1 = err
|
|
||||||
}
|
}
|
||||||
return err1
|
return err1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetCpusetCpus set cpuset.cpus
|
||||||
|
func (c *Cgroup) SetCpusetCpus(b []byte) error {
|
||||||
|
return c.cpuset.WriteFile("cpuset.cpus", b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCpusetMems set cpuset.mems
|
||||||
|
func (c *Cgroup) SetCpusetMems(b []byte) error {
|
||||||
|
return c.cpuset.WriteFile("cpuset.mems", b)
|
||||||
|
}
|
||||||
|
|
||||||
// CpuacctUsage read cpuacct.usage in ns
|
// CpuacctUsage read cpuacct.usage in ns
|
||||||
func (c *Cgroup) CpuacctUsage() (uint64, error) {
|
func (c *Cgroup) CpuacctUsage() (uint64, error) {
|
||||||
return c.cpuacct.ReadUint("cpuacct.usage")
|
return c.cpuacct.ReadUint("cpuacct.usage")
|
||||||
@ -128,6 +147,36 @@ func (c *Cgroup) FindMemoryStatProperty(prop string) (uint64, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// initCpuset will copy the config from the parent cpu sets if not exists
|
||||||
|
func initCpuset(path string) error {
|
||||||
|
for _, f := range []string{"cpuset.cpus", "cpuset.mems"} {
|
||||||
|
if err := copyCgroupPropertyFromParent(path, f); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyCgroupPropertyFromParent(path, name string) error {
|
||||||
|
// ensure current one empty
|
||||||
|
b, err := ioutil.ReadFile(filepath.Join(path, name))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(strings.TrimSpace(string(b))) > 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// otherwise copy from parent, first to ensure it is empty by recurssion
|
||||||
|
if err := copyCgroupPropertyFromParent(filepath.Dir(path), name); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
b, err = ioutil.ReadFile(filepath.Join(filepath.Dir(path), name))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return ioutil.WriteFile(filepath.Join(path, name), b, filePerm)
|
||||||
|
}
|
||||||
|
|
||||||
func remove(name string) error {
|
func remove(name string) error {
|
||||||
if name != "" {
|
if name != "" {
|
||||||
return os.Remove(name)
|
return os.Remove(name)
|
||||||
|
|||||||
@ -5,4 +5,6 @@ const (
|
|||||||
basePath = "/sys/fs/cgroup"
|
basePath = "/sys/fs/cgroup"
|
||||||
cgroupProcs = "cgroup.procs"
|
cgroupProcs = "cgroup.procs"
|
||||||
procCgroupsPath = "/proc/cgroups"
|
procCgroupsPath = "/proc/cgroups"
|
||||||
|
|
||||||
|
filePerm = 0644
|
||||||
)
|
)
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
// cpuacct
|
// cpuacct
|
||||||
// memory
|
// memory
|
||||||
// pids
|
// pids
|
||||||
|
// cpuset
|
||||||
//
|
//
|
||||||
// Current not available: cpu, cpuset, devices, freezer, net_cls, blkio, perf_event, net_prio, huge_tlb, rdma
|
// Current not available: cpu, cpuset, devices, freezer, net_cls, blkio, perf_event, net_prio, huge_tlb, rdma
|
||||||
//
|
//
|
||||||
|
|||||||
@ -49,10 +49,13 @@ func (c *SubCgroup) ReadUint(filename string) (uint64, error) {
|
|||||||
// WriteFile writes cgroup file and handles potential EINTR error while writes to
|
// WriteFile writes cgroup file and handles potential EINTR error while writes to
|
||||||
// the slow device (cgroup)
|
// the slow device (cgroup)
|
||||||
func (c *SubCgroup) WriteFile(name string, content []byte) error {
|
func (c *SubCgroup) WriteFile(name string, content []byte) error {
|
||||||
|
if c.path == "" {
|
||||||
|
return ErrNotInitialized
|
||||||
|
}
|
||||||
p := path.Join(c.path, name)
|
p := path.Join(c.path, name)
|
||||||
err := ioutil.WriteFile(p, content, 0664)
|
err := ioutil.WriteFile(p, content, filePerm)
|
||||||
for err != nil && errors.Is(err, syscall.EINTR) {
|
for err != nil && errors.Is(err, syscall.EINTR) {
|
||||||
err = ioutil.WriteFile(p, content, 0664)
|
err = ioutil.WriteFile(p, content, filePerm)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -60,6 +63,9 @@ func (c *SubCgroup) WriteFile(name string, content []byte) error {
|
|||||||
// ReadFile reads cgroup file and handles potential EINTR error while read to
|
// ReadFile reads cgroup file and handles potential EINTR error while read to
|
||||||
// the slow device (cgroup)
|
// the slow device (cgroup)
|
||||||
func (c *SubCgroup) ReadFile(name string) ([]byte, error) {
|
func (c *SubCgroup) ReadFile(name string) ([]byte, error) {
|
||||||
|
if c.path == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
p := path.Join(c.path, name)
|
p := path.Join(c.path, name)
|
||||||
data, err := ioutil.ReadFile(p)
|
data, err := ioutil.ReadFile(p)
|
||||||
for err != nil && errors.Is(err, syscall.EINTR) {
|
for err != nil && errors.Is(err, syscall.EINTR) {
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import (
|
|||||||
// EnsureDirExists creates directories if the path not exists
|
// EnsureDirExists creates directories if the path not exists
|
||||||
func EnsureDirExists(path string) error {
|
func EnsureDirExists(path string) error {
|
||||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||||
return os.Mkdir(path, os.ModePerm)
|
return os.MkdirAll(path, os.ModePerm)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user