Limiting resources on a process
This commit is contained in:
parent
a6f87c555c
commit
14acbe7479
|
@ -14,12 +14,13 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/go-multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
//"github.com/opencontainers/runc/libcontainer/cgroups"
|
"github.com/opencontainers/runc/libcontainer/cgroups"
|
||||||
//cgroupFs "github.com/opencontainers/runc/libcontainer/cgroups/fs"
|
cgroupFs "github.com/opencontainers/runc/libcontainer/cgroups/fs"
|
||||||
//"github.com/opencontainers/runc/libcontainer/cgroups/systemd"
|
"github.com/opencontainers/runc/libcontainer/cgroups/systemd"
|
||||||
//cgroupConfig "github.com/opencontainers/runc/libcontainer/configs"
|
cgroupConfig "github.com/opencontainers/runc/libcontainer/configs"
|
||||||
|
|
||||||
"github.com/hashicorp/nomad/client/allocdir"
|
"github.com/hashicorp/nomad/client/allocdir"
|
||||||
|
"github.com/hashicorp/nomad/nomad/structs"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -41,7 +42,7 @@ type LinuxExecutor struct {
|
||||||
cmd exec.Cmd
|
cmd exec.Cmd
|
||||||
ctx *ExecutorContext
|
ctx *ExecutorContext
|
||||||
|
|
||||||
//groups *cgroupConfig.Cgroup
|
groups *cgroupConfig.Cgroup
|
||||||
taskDir string
|
taskDir string
|
||||||
|
|
||||||
logger *log.Logger
|
logger *log.Logger
|
||||||
|
@ -86,10 +87,23 @@ func (e *LinuxExecutor) LaunchCmd(command *ExecCommand, ctx *ExecutorContext) (*
|
||||||
|
|
||||||
e.configureChroot()
|
e.configureChroot()
|
||||||
|
|
||||||
|
if err := e.configureCgroups(e.ctx.Task.Resources); err != nil {
|
||||||
|
return nil, fmt.Errorf("error creating cgroups: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
if err := e.cmd.Start(); err != nil {
|
if err := e.cmd.Start(); err != nil {
|
||||||
return nil, fmt.Errorf("error starting command: %v", err)
|
return nil, fmt.Errorf("error starting command: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
manager := e.getCgroupManager(e.groups)
|
||||||
|
if err := manager.Apply(e.cmd.Process.Pid); err != nil {
|
||||||
|
e.logger.Printf("[ERROR] unable to join cgroup: %v", err)
|
||||||
|
if err := e.Exit(); err != nil {
|
||||||
|
e.logger.Printf("[ERROR] unable to kill process: %v", err)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return &ProcessState{Pid: e.cmd.Process.Pid, ExitCode: -1, Time: time.Now()}, nil
|
return &ProcessState{Pid: e.cmd.Process.Pid, ExitCode: -1, Time: time.Now()}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,15 +258,18 @@ func (e *LinuxExecutor) Wait() (*ProcessState, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
e.cleanTaskDir()
|
e.cleanTaskDir()
|
||||||
|
e.destroyCgroup()
|
||||||
return &ProcessState{Pid: 0, ExitCode: exitCode, Time: time.Now()}, nil
|
return &ProcessState{Pid: 0, ExitCode: exitCode, Time: time.Now()}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *LinuxExecutor) Exit() error {
|
func (e *LinuxExecutor) Exit() error {
|
||||||
|
e.logger.Printf("[INFO] Exiting plugin for task %q", e.ctx.Task.Name)
|
||||||
proc, err := os.FindProcess(e.cmd.Process.Pid)
|
proc, err := os.FindProcess(e.cmd.Process.Pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failied to find user process %v: %v", e.cmd.Process.Pid, err)
|
return fmt.Errorf("failied to find user process %v: %v", e.cmd.Process.Pid, err)
|
||||||
}
|
}
|
||||||
e.cleanTaskDir()
|
e.cleanTaskDir()
|
||||||
|
e.destroyCgroup()
|
||||||
return proc.Kill()
|
return proc.Kill()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,3 +283,92 @@ func (e *LinuxExecutor) ShutDown() error {
|
||||||
}
|
}
|
||||||
return proc.Signal(os.Interrupt)
|
return proc.Signal(os.Interrupt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// configureCgroups converts a Nomad Resources specification into the equivalent
|
||||||
|
// cgroup configuration. It returns an error if the resources are invalid.
|
||||||
|
func (e *LinuxExecutor) configureCgroups(resources *structs.Resources) error {
|
||||||
|
e.groups = &cgroupConfig.Cgroup{}
|
||||||
|
e.groups.Resources = &cgroupConfig.Resources{}
|
||||||
|
e.groups.Name = structs.GenerateUUID()
|
||||||
|
|
||||||
|
// TODO: verify this is needed for things like network access
|
||||||
|
e.groups.Resources.AllowAllDevices = true
|
||||||
|
|
||||||
|
if resources.MemoryMB > 0 {
|
||||||
|
// Total amount of memory allowed to consume
|
||||||
|
e.groups.Resources.Memory = int64(resources.MemoryMB * 1024 * 1024)
|
||||||
|
// Disable swap to avoid issues on the machine
|
||||||
|
e.groups.Resources.MemorySwap = int64(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resources.CPU < 2 {
|
||||||
|
return fmt.Errorf("resources.CPU must be equal to or greater than 2: %v", resources.CPU)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the relative CPU shares for this cgroup.
|
||||||
|
e.groups.Resources.CpuShares = int64(resources.CPU)
|
||||||
|
|
||||||
|
if resources.IOPS != 0 {
|
||||||
|
// Validate it is in an acceptable range.
|
||||||
|
if resources.IOPS < 10 || resources.IOPS > 1000 {
|
||||||
|
return fmt.Errorf("resources.IOPS must be between 10 and 1000: %d", resources.IOPS)
|
||||||
|
}
|
||||||
|
|
||||||
|
e.groups.Resources.BlkioWeight = uint16(resources.IOPS)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// destroyCgroup kills all processes in the cgroup and removes the cgroup
|
||||||
|
// configuration from the host.
|
||||||
|
func (e *LinuxExecutor) destroyCgroup() error {
|
||||||
|
if e.groups == nil {
|
||||||
|
return fmt.Errorf("Can't destroy: cgroup configuration empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent a race between Wait/ForceStop
|
||||||
|
e.lock.Lock()
|
||||||
|
defer e.lock.Unlock()
|
||||||
|
|
||||||
|
manager := e.getCgroupManager(e.groups)
|
||||||
|
pids, err := manager.GetPids()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to get pids in the cgroup %v: %v", e.groups.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
errs := new(multierror.Error)
|
||||||
|
for _, pid := range pids {
|
||||||
|
process, err := os.FindProcess(pid)
|
||||||
|
if err != nil {
|
||||||
|
multierror.Append(errs, fmt.Errorf("Failed to find Pid %v: %v", pid, err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := process.Kill(); err != nil && err.Error() != "os: process already finished" {
|
||||||
|
multierror.Append(errs, fmt.Errorf("Failed to kill Pid %v: %v", pid, err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the cgroup.
|
||||||
|
if err := manager.Destroy(); err != nil {
|
||||||
|
multierror.Append(errs, fmt.Errorf("Failed to delete the cgroup directories: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(errs.Errors) != 0 {
|
||||||
|
return fmt.Errorf("Failed to destroy cgroup: %v", errs)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getCgroupManager returns the correct libcontainer cgroup manager.
|
||||||
|
func (e *LinuxExecutor) getCgroupManager(groups *cgroupConfig.Cgroup) cgroups.Manager {
|
||||||
|
var manager cgroups.Manager
|
||||||
|
manager = &cgroupFs.Manager{Cgroups: groups}
|
||||||
|
if systemd.UseSystemd() {
|
||||||
|
manager = &systemd.Manager{Cgroups: groups}
|
||||||
|
}
|
||||||
|
return manager
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue