proclimit

package module
v0.0.0-...-391da56 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 3, 2019 License: MIT Imports: 9 Imported by: 0

README

proclimit

proclimit is a Go library for running external commands with configurable resource (CPU and memory) limits. Currently, only Linux (cgroups) and Windows (Job Objects) are supported.

The project also includes an application to run processes with limited resources.

Installation

go get github.com/aoldershaw/proclimit

To install and run the application:

go get github.com/aoldershaw/proclimit/cmd/proclimit

proclimit -cpu=50 -memory=512M my-application arg1 arg2

Usage

func main() {
    limiter, _ := proclimit.New(
        // The CPU limit is relative to a single core. Specifying 200% on a 4 core machine
        // restricts the total CPU usage of all processes in the limiter to use up to 2 cores
        //
        // It is not guaranteed that the processes will only be scheduled on 2 physical cores -
        // in the example above, it is possible that each of the 4 cores will be at 50% utilization
        // (meaning the total CPU usage is 2 "full" cores)
        proclimit.WithCPULimit(proclimit.Percent(50)),
        // The memory limit is based on total virtual memory
        proclimit.WithMemoryLimit(512 * proclimit.Megabyte),
    )
    defer limiter.Close()

    // limiter.Command is nearly identical to exec.Command - the returned *Cmd can be treated as an *exec.Cmd
    // However, when the *Cmd is started (through Start, Run, Output, or CombinedOutput), it will run with
    // limited resources
    cmd1 := limiter.Command("application1", "arg1", "arg2")
    cmd1.Stdout = os.Stdout

    // application1 will be limited to 512M of virtual memory and 50% of a single core's compute
    cmd1.Start()

    cmd2 := limiter.Command("application2")
    
    // Since application2 is run in the same limiter, the CPU and memory limits apply to the
    // combined utilization of application1 and application2.
    // If application1 uses 40% CPU, then application2 can only use up to 10%
    out, _ := cmd2.Output()
}
func main() {
    limiter, _ := proclimit.New(...)
    ...
    // proclimit can also limit resources of currently running processes by pid
    limiter.Limit(1234)
}

Note

  • proclimit is still very early in development and requires more testing (particularly on the Windows side, as I don't have easy access to a Windows machine).
  • Only Linux and Windows are supported at the moment
  • Processes will first start with no limits applied. If it is important that a process start up with the limits applied (for instance, if using github.com/uber-go/automaxprocs in the application being started), proclimit is currently not the tool for the job.

License

MIT

Documentation

Overview

Package proclimit provides a consistent cross-platform API for limiting CPU and memory resources to processes. It also provides a wrapper Cmd type that mimics exec.Cmd, while applying resource limits upon starting the commands.

Index

Examples

Constants

View Source
const (
	Byte     Memory = 1
	Kilobyte        = 1024 * Byte
	Megabyte        = 1024 * Kilobyte
	Gigabyte        = 1024 * Megabyte
)

Variables

This section is empty.

Functions

This section is empty.

Types

type Cgroup

type Cgroup struct {
	Name           string
	LinuxResources *specs.LinuxResources
	// contains filtered or unexported fields
}

Cgroup represents a cgroup in a Linux system. Resource limits can be configured by modifying LinuxResources through Options. Modifying LinuxResources after calling New(...) will have no effect.

func Existing

func Existing(name string) (*Cgroup, error)

New loads an existing Cgroup by name

func New

func New(options ...Option) (*Cgroup, error)

New creates a new Cgroup. Resource limits and the name of the Cgroup can be defined using Option arguments.

Example
package main

import (
	"github.com/aoldershaw/proclimit"
	"os"
)

func main() {
	limiter, err := proclimit.New(
		proclimit.WithName("my-cgroup"),
		proclimit.WithCPULimit(proclimit.Percent(50)),
		proclimit.WithMemoryLimit(512*proclimit.Megabyte),
	)
	if err != nil {
		// handle err
	}
	defer limiter.Close()

	cmd := limiter.Command("stress", "--cpu", "2", "--timeout", "10")
	cmd.Stdout = os.Stdout
	err = cmd.Run()
	if err != nil {
		// handle err
	}
}
Output:

func (*Cgroup) Close

func (c *Cgroup) Close() error

Close deletes the Cgroup definition from the filesystem.

func (*Cgroup) Command

func (c *Cgroup) Command(name string, arg ...string) *Cmd

Command constructs a wrapped Cmd struct to execute the named program with the given arguments. This wrapped Cmd will be added to the Cgroup when it is started.

func (*Cgroup) Limit

func (c *Cgroup) Limit(pid int) error

Limit applies Cgroup resource limits to a running process by its pid.

func (*Cgroup) Wrap

func (c *Cgroup) Wrap(cmd *exec.Cmd) *Cmd

Wrap takes an existing exec.Cmd and converts it into a proclimit.Cmd. When the returned Cmd is started, it will have the resources applied.

If cmd has already been started, Wrap will panic. To limit the resources of a running process, use Limit instead.

type Cmd

type Cmd struct {
	*exec.Cmd
	Limiter Limiter
}

Cmd represents an external command being prepared or run. This command will be limited by the provided Limiter.

It can be used exactly as an exec.Cmd can be used.

func (*Cmd) CombinedOutput

func (c *Cmd) CombinedOutput() ([]byte, error)

CombinedOutput runs the command (with limits) and returns its combined standard output and standard error.

func (*Cmd) Output

func (c *Cmd) Output() ([]byte, error)

Output runs the command (with limits) and returns its standard output.

func (*Cmd) Run

func (c *Cmd) Run() error

Run starts the specified command (with limits), and waits for it to complete.

func (*Cmd) Start

func (c *Cmd) Start() error

Start begins the execution of a Cmd, and applies the limits defined by the associated Limiter. If the Limiter fails to apply limits, the process will be killed.

Note that the Cmd will start before the limits are applied, so there will be a brief period where the limits are not enforced.

type Limiter

type Limiter interface {
	// Limit applies limits to a running process by its pid.
	// The specific limits to apply are defined by the implementation.
	Limit(pid int) error
}

Limiter allows limiting a running process' resources

type Memory

type Memory uint

Memory represents a number of bytes

type Option

type Option func(cgroup *Cgroup)

Option allows for customizing the behaviour of the Cgroup limiter.

Options will typically perform operations on cgroup.LinuxResources.

func WithCPULimit

func WithCPULimit(cpuLimit Percent) Option

WithCPULimit sets the maximum CPU limit (as a percentage) allowed for all processes within the Cgroup. The percentage is based on a single CPU core. That is to say, 50 allows for the use of half of a core, 200 allows for the use of two cores, etc.

`cpu.cfs_period_us` will be set to 100000 (100ms) unless it has been overridden (e.g. by an Option that is added before this Option. Note that no such Option has been implemented currently).

`cpu.cfs_quota_us` will be set to cpuLimit percent of `cpu.cfs_period_us`.

func WithMemoryLimit

func WithMemoryLimit(memory Memory) Option

WithMemoryLimit sets the maximum amount of virtual memory allowed for all processes within the Cgroup.

`memory.max_usage_in_bytes` is set to memory

func WithName

func WithName(name string) Option

WithName sets the name of the Cgroup. If not specified, a random UUID will be generated as the name

type Percent

type Percent uint

Percent is a percentage value. It is used to specify CPU rate limits.

Directories

Path Synopsis
cmd
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL