From 3f15a055e5c50d0f08d4c3e7cd9618d537b84f29 Mon Sep 17 00:00:00 2001 From: Ma Shimiao Date: Wed, 8 Jul 2015 19:06:48 +0800 Subject: [PATCH] Add support for blkio read/write bps device Signed-off-by: Ma Shimiao --- contrib/completion/bash/docker | 2 + contrib/completion/zsh/_docker | 2 + daemon/container_operations_unix.go | 32 +++++++---- daemon/daemon_unix.go | 40 +++++++++++++ daemon/daemon_windows.go | 8 +++ daemon/execdriver/driver_unix.go | 24 ++++---- docs/reference/api/docker_remote_api_v1.22.md | 8 +++ docs/reference/commandline/create.md | 2 + docs/reference/commandline/run.md | 2 + docs/reference/run.md | 16 ++++++ integration-cli/docker_cli_run_unix_test.go | 12 ++++ man/docker-create.1.md | 8 +++ man/docker-run.1.md | 8 +++ opts/opts.go | 27 +++++++++ opts/throttledevice.go | 56 +++++++++++++++++++ pkg/blkiodev/blkiodev.go | 10 ++++ pkg/sysinfo/sysinfo.go | 6 ++ pkg/sysinfo/sysinfo_linux.go | 16 +++++- runconfig/hostconfig.go | 30 +++++----- runconfig/parse.go | 36 +++++++----- 20 files changed, 294 insertions(+), 51 deletions(-) create mode 100644 opts/throttledevice.go diff --git a/contrib/completion/bash/docker b/contrib/completion/bash/docker index 03c593f600..f3997a0c77 100644 --- a/contrib/completion/bash/docker +++ b/contrib/completion/bash/docker @@ -1372,6 +1372,8 @@ _docker_run() { --cpuset-mems --cpu-shares --device + --device-read-bps + --device-write-bps --dns --dns-opt --dns-search diff --git a/contrib/completion/zsh/_docker b/contrib/completion/zsh/_docker index b5be7cff39..44037f9014 100644 --- a/contrib/completion/zsh/_docker +++ b/contrib/completion/zsh/_docker @@ -468,6 +468,8 @@ __docker_subcommand() { "($help)*--cap-drop=[Drop Linux capabilities]:capability: " "($help)--cidfile=[Write the container ID to the file]:CID file:_files" "($help)*--device=[Add a host device to the container]:device:_files" + "($help)*--device-read-bps=[Limit the read rate (bytes per second) from a device]:device:IO rate: " + "($help)*--device-write-bps=[Limit the write rate (bytes per second) to a device]:device:IO rate: " "($help)*--dns=[Set custom DNS servers]:DNS server: " "($help)*--dns-opt=[Set custom DNS options]:DNS option: " "($help)*--dns-search=[Set custom DNS search domains]:DNS domains: " diff --git a/daemon/container_operations_unix.go b/daemon/container_operations_unix.go index f660bd7593..70e482d002 100644 --- a/daemon/container_operations_unix.go +++ b/daemon/container_operations_unix.go @@ -163,6 +163,16 @@ func (daemon *Daemon) populateCommand(c *container.Container, env []string) erro return err } + readBpsDevice, err := getBlkioReadBpsDevices(c.HostConfig) + if err != nil { + return err + } + + writeBpsDevice, err := getBlkioWriteBpsDevices(c.HostConfig) + if err != nil { + return err + } + for _, limit := range ulimits { rl, err := limit.GetRlimit() if err != nil { @@ -178,16 +188,18 @@ func (daemon *Daemon) populateCommand(c *container.Container, env []string) erro CPUShares: c.HostConfig.CPUShares, BlkioWeight: c.HostConfig.BlkioWeight, }, - MemorySwap: c.HostConfig.MemorySwap, - KernelMemory: c.HostConfig.KernelMemory, - CpusetCpus: c.HostConfig.CpusetCpus, - CpusetMems: c.HostConfig.CpusetMems, - CPUPeriod: c.HostConfig.CPUPeriod, - CPUQuota: c.HostConfig.CPUQuota, - Rlimits: rlimits, - BlkioWeightDevice: weightDevices, - OomKillDisable: c.HostConfig.OomKillDisable, - MemorySwappiness: *c.HostConfig.MemorySwappiness, + MemorySwap: c.HostConfig.MemorySwap, + KernelMemory: c.HostConfig.KernelMemory, + CpusetCpus: c.HostConfig.CpusetCpus, + CpusetMems: c.HostConfig.CpusetMems, + CPUPeriod: c.HostConfig.CPUPeriod, + CPUQuota: c.HostConfig.CPUQuota, + Rlimits: rlimits, + BlkioWeightDevice: weightDevices, + BlkioThrottleReadBpsDevice: readBpsDevice, + BlkioThrottleWriteBpsDevice: writeBpsDevice, + OomKillDisable: c.HostConfig.OomKillDisable, + MemorySwappiness: *c.HostConfig.MemorySwappiness, } processConfig := execdriver.ProcessConfig{ diff --git a/daemon/daemon_unix.go b/daemon/daemon_unix.go index 202cdb22db..3050a381ba 100644 --- a/daemon/daemon_unix.go +++ b/daemon/daemon_unix.go @@ -83,6 +83,36 @@ func parseSecurityOpt(container *container.Container, config *runconfig.HostConf return err } +func getBlkioReadBpsDevices(config *runconfig.HostConfig) ([]*blkiodev.ThrottleDevice, error) { + var BlkioReadBpsDevice []*blkiodev.ThrottleDevice + var stat syscall.Stat_t + + for _, bpsDevice := range config.BlkioDeviceReadBps { + if err := syscall.Stat(bpsDevice.Path, &stat); err != nil { + return nil, err + } + ReadBpsDevice := blkiodev.NewThrottleDevice(int64(stat.Rdev/256), int64(stat.Rdev%256), bpsDevice.Rate) + BlkioReadBpsDevice = append(BlkioReadBpsDevice, ReadBpsDevice) + } + + return BlkioReadBpsDevice, nil +} + +func getBlkioWriteBpsDevices(config *runconfig.HostConfig) ([]*blkiodev.ThrottleDevice, error) { + var BlkioWriteBpsDevice []*blkiodev.ThrottleDevice + var stat syscall.Stat_t + + for _, bpsDevice := range config.BlkioDeviceWriteBps { + if err := syscall.Stat(bpsDevice.Path, &stat); err != nil { + return nil, err + } + WriteBpsDevice := blkiodev.NewThrottleDevice(int64(stat.Rdev/256), int64(stat.Rdev%256), bpsDevice.Rate) + BlkioWriteBpsDevice = append(BlkioWriteBpsDevice, WriteBpsDevice) + } + + return BlkioWriteBpsDevice, nil +} + func checkKernelVersion(k, major, minor int) bool { if v, err := kernel.GetKernelVersion(); err != nil { logrus.Warnf("%s", err) @@ -259,6 +289,16 @@ func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *runconfig.HostC logrus.Warnf("Your kernel does not support Block I/O weight_device. Weight-device discarded.") hostConfig.BlkioWeightDevice = []*pblkiodev.WeightDevice{} } + if len(hostConfig.BlkioDeviceReadBps) > 0 && !sysInfo.BlkioReadBpsDevice { + warnings = append(warnings, "Your kernel does not support Block read limit in bytes per second.") + logrus.Warnf("Your kernel does not support Block I/O read limit in bytes per second. --device-read-bps discarded.") + hostConfig.BlkioDeviceReadBps = []*pblkiodev.ThrottleDevice{} + } + if len(hostConfig.BlkioDeviceWriteBps) > 0 && !sysInfo.BlkioWriteBpsDevice { + warnings = append(warnings, "Your kernel does not support Block write limit in bytes per second.") + logrus.Warnf("Your kernel does not support Block I/O write limit in bytes per second. --device-write-bps discarded.") + hostConfig.BlkioDeviceWriteBps = []*pblkiodev.ThrottleDevice{} + } if hostConfig.OomKillDisable && !sysInfo.OomKillDisable { hostConfig.OomKillDisable = false return warnings, fmt.Errorf("Your kernel does not support oom kill disable.") diff --git a/daemon/daemon_windows.go b/daemon/daemon_windows.go index c2de6c8e99..bd35a2ee95 100644 --- a/daemon/daemon_windows.go +++ b/daemon/daemon_windows.go @@ -39,6 +39,14 @@ func parseSecurityOpt(container *container.Container, config *runconfig.HostConf return nil } +func getBlkioReadBpsDevices(config *runconfig.HostConfig) ([]*blkiodev.ThrottleDevice, error) { + return nil, nil +} + +func getBlkioWriteBpsDevices(config *runconfig.HostConfig) ([]*blkiodev.ThrottleDevice, error) { + return nil, nil +} + func setupInitLayer(initLayer string, rootUID, rootGID int) error { return nil } diff --git a/daemon/execdriver/driver_unix.go b/daemon/execdriver/driver_unix.go index eb4482f8e0..d77f77eb74 100644 --- a/daemon/execdriver/driver_unix.go +++ b/daemon/execdriver/driver_unix.go @@ -38,16 +38,18 @@ type Resources struct { // Fields below here are platform specific - BlkioWeightDevice []*blkiodev.WeightDevice `json:"blkio_weight_device"` - MemorySwap int64 `json:"memory_swap"` - KernelMemory int64 `json:"kernel_memory"` - CPUQuota int64 `json:"cpu_quota"` - CpusetCpus string `json:"cpuset_cpus"` - CpusetMems string `json:"cpuset_mems"` - CPUPeriod int64 `json:"cpu_period"` - Rlimits []*ulimit.Rlimit `json:"rlimits"` - OomKillDisable bool `json:"oom_kill_disable"` - MemorySwappiness int64 `json:"memory_swappiness"` + BlkioWeightDevice []*blkiodev.WeightDevice `json:"blkio_weight_device"` + BlkioThrottleReadBpsDevice []*blkiodev.ThrottleDevice `json:"blkio_throttle_read_bps_device"` + BlkioThrottleWriteBpsDevice []*blkiodev.ThrottleDevice `json:"blkio_throttle_write_bps_device"` + MemorySwap int64 `json:"memory_swap"` + KernelMemory int64 `json:"kernel_memory"` + CPUQuota int64 `json:"cpu_quota"` + CpusetCpus string `json:"cpuset_cpus"` + CpusetMems string `json:"cpuset_mems"` + CPUPeriod int64 `json:"cpu_period"` + Rlimits []*ulimit.Rlimit `json:"rlimits"` + OomKillDisable bool `json:"oom_kill_disable"` + MemorySwappiness int64 `json:"memory_swappiness"` } // ProcessConfig is the platform specific structure that describes a process @@ -170,6 +172,8 @@ func SetupCgroups(container *configs.Config, c *Command) error { container.Cgroups.CpuQuota = c.Resources.CPUQuota container.Cgroups.BlkioWeight = c.Resources.BlkioWeight container.Cgroups.BlkioWeightDevice = c.Resources.BlkioWeightDevice + container.Cgroups.BlkioThrottleReadBpsDevice = c.Resources.BlkioThrottleReadBpsDevice + container.Cgroups.BlkioThrottleWriteBpsDevice = c.Resources.BlkioThrottleWriteBpsDevice container.Cgroups.OomKillDisable = c.Resources.OomKillDisable container.Cgroups.MemorySwappiness = c.Resources.MemorySwappiness } diff --git a/docs/reference/api/docker_remote_api_v1.22.md b/docs/reference/api/docker_remote_api_v1.22.md index bef84fd5d5..b59a5e64a0 100644 --- a/docs/reference/api/docker_remote_api_v1.22.md +++ b/docs/reference/api/docker_remote_api_v1.22.md @@ -188,6 +188,8 @@ Create a container "CpusetMems": "0,1", "BlkioWeight": 300, "BlkioWeightDevice": [{}], + "BlkioDeviceReadBps": [{}], + "BlkioDeviceWriteBps": [{}], "MemorySwappiness": 60, "OomKillDisable": false, "OomScoreAdj": 500, @@ -245,6 +247,10 @@ Json Parameters: - **CpusetMems** - Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems. - **BlkioWeight** - Block IO weight (relative weight) accepts a weight value between 10 and 1000. - **BlkioWeightDevice** - Block IO weight (relative device weight) in the form of: `"BlkioWeightDevice": [{"Path": "device_path", "Weight": weight}]` +- **BlkioDeviceReadBps** - Limit read rate from a device in form of: `"BlkioDeviceReadBps": [{"Path": "device_path", "Rate": rate}]`, for example: + `"BlkioDeviceReadBps": [{"Path": "/dev/sda", "Rate": "1024"}]"` +- **BlkioDeviceWriteBps** - Limit write rate to a device in the form of: `"BlkioDeviceWriteBps": [{"Path": "deivce_path", "Rate": rate}]`, for example: + `"BlkioDeviceWriteBps": [{"Path": "/dev/sda", "Rate": "1024"}]"` - **MemorySwappiness** - Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100. - **OomKillDisable** - Boolean value, whether to disable OOM Killer for the container or not. - **OomScoreAdj** - An integer value containing the score given to the container in order to tune OOM killer preferences. @@ -398,6 +404,8 @@ Return low-level information on the container `id` "Binds": null, "BlkioWeight": 0, "BlkioWeightDevice": [{}], + "BlkioDeviceReadBps": [{}], + "BlkioDeviceWriteBps": [{}], "CapAdd": null, "CapDrop": null, "ContainerIDFile": "", diff --git a/docs/reference/commandline/create.md b/docs/reference/commandline/create.md index f05c2d00dc..a23d42a53f 100644 --- a/docs/reference/commandline/create.md +++ b/docs/reference/commandline/create.md @@ -30,6 +30,8 @@ Creates a new container. --cpuset-cpus="" CPUs in which to allow execution (0-3, 0,1) --cpuset-mems="" Memory nodes (MEMs) in which to allow execution (0-3, 0,1) --device=[] Add a host device to the container + --device-read-bps=[] Limit read rate (bytes per second) from a device (e.g., --device-read-bps=/dev/sda:1mb) + --device-write-bps=[] Limit write rate (bytes per second) to a device (e.g., --device-write-bps=/dev/sda:1mb) --disable-content-trust=true Skip image verification --dns=[] Set custom DNS servers --dns-opt=[] Set custom DNS options diff --git a/docs/reference/commandline/run.md b/docs/reference/commandline/run.md index 7015991ea6..3dd1aea78a 100644 --- a/docs/reference/commandline/run.md +++ b/docs/reference/commandline/run.md @@ -29,6 +29,8 @@ parent = "smn_cli" --cpuset-mems="" Memory nodes (MEMs) in which to allow execution (0-3, 0,1) -d, --detach=false Run container in background and print container ID --device=[] Add a host device to the container + --device-read-bps=[] Limit read rate (bytes per second) from a device (e.g., --device-read-bps=/dev/sda:1mb) + --device-write-bps=[] Limit write rate (bytes per second) to a device (e.g., --device-write-bps=/dev/sda:1mb) --disable-content-trust=true Skip image verification --dns=[] Set custom DNS servers --dns-opt=[] Set custom DNS options diff --git a/docs/reference/run.md b/docs/reference/run.md index 80961f91c5..53923f9afa 100644 --- a/docs/reference/run.md +++ b/docs/reference/run.md @@ -624,6 +624,10 @@ container: | `--cpu-quota=0` | Limit the CPU CFS (Completely Fair Scheduler) quota | | `--blkio-weight=0` | Block IO weight (relative weight) accepts a weight value between 10 and 1000. | | `--blkio-weight-device=""` | Block IO weight (relative device weight, format: `DEVICE_NAME:WEIGHT`) | +| `--device-read-bps="" ` | Limit read rate from a device (format: `:[]`. | +| | Number is a positive integer. Unit can be one of kb, mb, or gb. | +| `--device-write-bps="" ` | Limit write rate to a device (format: `:[]`. | +| | Number is a positive integer. Unit can be one of kb, mb, or gb. | | `--oom-kill-disable=false` | Whether to disable OOM Killer for the container or not. | | `--memory-swappiness="" ` | Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100. | | `--shm-size="" ` | Size of `/dev/shm`. The format is ``. `number` must be greater than `0`. | @@ -978,6 +982,18 @@ $ docker run -it \ --blkio-weight-device "/dev/sda:200" \ ubuntu +The `--device-read-bps` flag can limit read rate from a device. +For example, the command creates a container and limits theread rate to `1mb` per second from `/dev/sda`: + + $ docker run -ti --device-read-bps /dev/sda:1mb ubuntu + +The `--device-write-bps` flag can limit write rate to a device. +For example, the command creates a container and limits write rate to `1mb` per second to `/dev/sda`: + + $ docker run -ti --device-write-bps /dev/sda:1mb ubuntu + +Both flags take limits in the `:[unit]` format. Both read and write rates must be a positive integer. You can specify the rate in `kb` (kilobytes), `mb` (megabytes), or `gb` (gigabytes). + ## Additional groups --group-add: Add Linux capabilities diff --git a/integration-cli/docker_cli_run_unix_test.go b/integration-cli/docker_cli_run_unix_test.go index c0f09a3043..2b6bcd5dac 100644 --- a/integration-cli/docker_cli_run_unix_test.go +++ b/integration-cli/docker_cli_run_unix_test.go @@ -246,6 +246,18 @@ func (s *DockerSuite) TestRunWithBlkioInvalidWeightDevice(c *check.C) { c.Assert(err, check.NotNil, check.Commentf(out)) } +func (s *DockerSuite) TestRunWithBlkioInvalidDeivceReadBps(c *check.C) { + testRequires(c, blkioWeight) + out, _, err := dockerCmdWithError("run", "--device-read-bps", "/dev/sda:500", "busybox", "true") + c.Assert(err, check.NotNil, check.Commentf(out)) +} + +func (s *DockerSuite) TestRunWithBlkioInvalidDeviceWriteBps(c *check.C) { + testRequires(c, blkioWeight) + out, _, err := dockerCmdWithError("run", "--device-write-bps", "/dev/sda:500", "busybox", "true") + c.Assert(err, check.NotNil, check.Commentf(out)) +} + func (s *DockerSuite) TestRunOOMExitCode(c *check.C) { testRequires(c, oomControl) errChan := make(chan error) diff --git a/man/docker-create.1.md b/man/docker-create.1.md index 362eef7474..8212c6bd82 100644 --- a/man/docker-create.1.md +++ b/man/docker-create.1.md @@ -20,6 +20,8 @@ docker-create - Create a new container [**--cpuset-cpus**[=*CPUSET-CPUS*]] [**--cpuset-mems**[=*CPUSET-MEMS*]] [**--device**[=*[]*]] +[**--device-read-bps**[=*[]*]] +[**--device-write-bps**[=*[]*]] [**--dns**[=*[]*]] [**--dns-search**[=*[]*]] [**--dns-opt**[=*[]*]] @@ -125,6 +127,12 @@ two memory nodes. **--device**=[] Add a host device to the container (e.g. --device=/dev/sdc:/dev/xvdc:rwm) +**--device-read-bps**=[] + Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb) + +**--device-write-bps**=[] + Limit write rate (bytes per second) to a device (e.g. --device-write-bps=/dev/sda:1mb) + **--dns**=[] Set custom DNS servers diff --git a/man/docker-run.1.md b/man/docker-run.1.md index 5ee9dea044..83c901006c 100644 --- a/man/docker-run.1.md +++ b/man/docker-run.1.md @@ -21,6 +21,8 @@ docker-run - Run a command in a new container [**--cpuset-mems**[=*CPUSET-MEMS*]] [**-d**|**--detach**[=*false*]] [**--device**[=*[]*]] +[**--device-read-bps**[=*[]*]] +[**--device-write-bps**[=*[]*]] [**--dns**[=*[]*]] [**--dns-opt**[=*[]*]] [**--dns-search**[=*[]*]] @@ -192,6 +194,12 @@ stopping the process by pressing the keys CTRL-P CTRL-Q. **--device**=[] Add a host device to the container (e.g. --device=/dev/sdc:/dev/xvdc:rwm) +**--device-read-bps**=[] + Limit read rate from a device (e.g. --device-read-bps=/dev/sda:1mb) + +**--device-write-bps**=[] + Limit write rate to a device (e.g. --device-write-bps=/dev/sda:1mb) + **--dns-search**=[] Set custom DNS search domains (Use --dns-search=. if you don't wish to set the search domain) diff --git a/opts/opts.go b/opts/opts.go index b0271cda4c..6c75a9c9b1 100644 --- a/opts/opts.go +++ b/opts/opts.go @@ -11,6 +11,7 @@ import ( "github.com/docker/docker/pkg/blkiodev" "github.com/docker/docker/pkg/parsers" + "github.com/docker/docker/pkg/units" ) var ( @@ -173,6 +174,9 @@ type ValidatorFctType func(val string) (string, error) // ValidatorWeightFctType defines a validator function that returns a validated struct and/or an error. type ValidatorWeightFctType func(val string) (*blkiodev.WeightDevice, error) +// ValidatorThrottleFctType defines a validator function that returns a validated struct and/or an error. +type ValidatorThrottleFctType func(val string) (*blkiodev.ThrottleDevice, error) + // ValidatorFctListType defines a validator function that returns a validated list of string and/or an error type ValidatorFctListType func(val string) ([]string, error) @@ -210,6 +214,29 @@ func ValidateWeightDevice(val string) (*blkiodev.WeightDevice, error) { }, nil } +// ValidateThrottleBpsDevice validates that the specified string has a valid device-rate format. +func ValidateThrottleBpsDevice(val string) (*blkiodev.ThrottleDevice, error) { + split := strings.SplitN(val, ":", 2) + if len(split) != 2 { + return nil, fmt.Errorf("bad format: %s", val) + } + if !strings.HasPrefix(split[0], "/dev/") { + return nil, fmt.Errorf("bad format for device path: %s", val) + } + rate, err := units.RAMInBytes(split[1]) + if err != nil { + return nil, fmt.Errorf("invalid rate for device: %s. The correct format is :[]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val) + } + if rate < 0 { + return nil, fmt.Errorf("invalid rate for device: %s. The correct format is :[]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val) + } + + return &blkiodev.ThrottleDevice{ + Path: split[0], + Rate: uint64(rate), + }, nil +} + // ValidateLink validates that the specified string has a valid link format (containerName:alias). func ValidateLink(val string) (string, error) { if _, _, err := parsers.ParseLink(val); err != nil { diff --git a/opts/throttledevice.go b/opts/throttledevice.go new file mode 100644 index 0000000000..fb11802326 --- /dev/null +++ b/opts/throttledevice.go @@ -0,0 +1,56 @@ +package opts + +import ( + "fmt" + + "github.com/docker/docker/pkg/blkiodev" +) + +// ThrottledeviceOpt defines a map of ThrottleDevices +type ThrottledeviceOpt struct { + values []*blkiodev.ThrottleDevice + validator ValidatorThrottleFctType +} + +// NewThrottledeviceOpt creates a new ThrottledeviceOpt +func NewThrottledeviceOpt(validator ValidatorThrottleFctType) ThrottledeviceOpt { + values := []*blkiodev.ThrottleDevice{} + return ThrottledeviceOpt{ + values: values, + validator: validator, + } +} + +// Set validates a ThrottleDevice and sets its name as a key in ThrottledeviceOpt +func (opt *ThrottledeviceOpt) Set(val string) error { + var value *blkiodev.ThrottleDevice + if opt.validator != nil { + v, err := opt.validator(val) + if err != nil { + return err + } + value = v + } + (opt.values) = append((opt.values), value) + return nil +} + +// String returns ThrottledeviceOpt values as a string. +func (opt *ThrottledeviceOpt) String() string { + var out []string + for _, v := range opt.values { + out = append(out, v.String()) + } + + return fmt.Sprintf("%v", out) +} + +// GetList returns a slice of pointers to ThrottleDevices. +func (opt *ThrottledeviceOpt) GetList() []*blkiodev.ThrottleDevice { + var throttledevice []*blkiodev.ThrottleDevice + for _, v := range opt.values { + throttledevice = append(throttledevice, v) + } + + return throttledevice +} diff --git a/pkg/blkiodev/blkiodev.go b/pkg/blkiodev/blkiodev.go index 84cdbd76f7..e9a3649fce 100644 --- a/pkg/blkiodev/blkiodev.go +++ b/pkg/blkiodev/blkiodev.go @@ -13,3 +13,13 @@ type WeightDevice struct { func (w *WeightDevice) String() string { return fmt.Sprintf("%s:%d", w.Path, w.Weight) } + +// ThrottleDevice is a structure that hold device:rate_per_second pair +type ThrottleDevice struct { + Path string + Rate uint64 +} + +func (t *ThrottleDevice) String() string { + return fmt.Sprintf("%s:%d", t.Path, t.Rate) +} diff --git a/pkg/sysinfo/sysinfo.go b/pkg/sysinfo/sysinfo.go index 580b6ba29d..c12619f577 100644 --- a/pkg/sysinfo/sysinfo.go +++ b/pkg/sysinfo/sysinfo.go @@ -63,6 +63,12 @@ type cgroupBlkioInfo struct { // Whether Block IO weight_device is supported or not BlkioWeightDevice bool + + // Whether Block IO read limit in bytes per second is supported or not + BlkioReadBpsDevice bool + + // Whether Block IO write limit in bytes per second is supported or not + BlkioWriteBpsDevice bool } type cgroupCpusetInfo struct { diff --git a/pkg/sysinfo/sysinfo_linux.go b/pkg/sysinfo/sysinfo_linux.go index a93e781ec4..e30e79aed2 100644 --- a/pkg/sysinfo/sysinfo_linux.go +++ b/pkg/sysinfo/sysinfo_linux.go @@ -126,9 +126,21 @@ func checkCgroupBlkioInfo(quiet bool) cgroupBlkioInfo { if !quiet && !weightDevice { logrus.Warn("Your kernel does not support cgroup blkio weight_device") } + + readBpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.read_bps_device") + if !quiet && !readBpsDevice { + logrus.Warn("Your kernel does not support cgroup blkio throttle.read_bps_device") + } + + writeBpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.write_bps_device") + if !quiet && !writeBpsDevice { + logrus.Warn("Your kernel does not support cgroup blkio throttle.write_bps_device") + } return cgroupBlkioInfo{ - BlkioWeight: weight, - BlkioWeightDevice: weightDevice, + BlkioWeight: weight, + BlkioWeightDevice: weightDevice, + BlkioReadBpsDevice: readBpsDevice, + BlkioWriteBpsDevice: writeBpsDevice, } } diff --git a/runconfig/hostconfig.go b/runconfig/hostconfig.go index aa6028a48d..25b3d779a5 100644 --- a/runconfig/hostconfig.go +++ b/runconfig/hostconfig.go @@ -171,20 +171,22 @@ type Resources struct { CPUShares int64 `json:"CpuShares"` // CPU shares (relative weight vs. other containers) // Applicable to UNIX platforms - CgroupParent string // Parent cgroup. - BlkioWeight uint16 // Block IO weight (relative weight vs. other containers) - BlkioWeightDevice []*blkiodev.WeightDevice - CPUPeriod int64 `json:"CpuPeriod"` // CPU CFS (Completely Fair Scheduler) period - CPUQuota int64 `json:"CpuQuota"` // CPU CFS (Completely Fair Scheduler) quota - CpusetCpus string // CpusetCpus 0-2, 0,1 - CpusetMems string // CpusetMems 0-2, 0,1 - Devices []DeviceMapping // List of devices to map inside the container - KernelMemory int64 // Kernel memory limit (in bytes) - Memory int64 // Memory limit (in bytes) - MemoryReservation int64 // Memory soft limit (in bytes) - MemorySwap int64 // Total memory usage (memory + swap); set `-1` to disable swap - MemorySwappiness *int64 // Tuning container memory swappiness behaviour - Ulimits []*ulimit.Ulimit // List of ulimits to be set in the container + CgroupParent string // Parent cgroup. + BlkioWeight uint16 // Block IO weight (relative weight vs. other containers) + BlkioWeightDevice []*blkiodev.WeightDevice + BlkioDeviceReadBps []*blkiodev.ThrottleDevice + BlkioDeviceWriteBps []*blkiodev.ThrottleDevice + CPUPeriod int64 `json:"CpuPeriod"` // CPU CFS (Completely Fair Scheduler) period + CPUQuota int64 `json:"CpuQuota"` // CPU CFS (Completely Fair Scheduler) quota + CpusetCpus string // CpusetCpus 0-2, 0,1 + CpusetMems string // CpusetMems 0-2, 0,1 + Devices []DeviceMapping // List of devices to map inside the container + KernelMemory int64 // Kernel memory limit (in bytes) + Memory int64 // Memory limit (in bytes) + MemoryReservation int64 // Memory soft limit (in bytes) + MemorySwap int64 // Total memory usage (memory + swap); set `-1` to disable swap + MemorySwappiness *int64 // Tuning container memory swappiness behaviour + Ulimits []*ulimit.Ulimit // List of ulimits to be set in the container } // HostConfig the non-portable Config structure of a container. diff --git a/runconfig/parse.go b/runconfig/parse.go index 1bcd94c0b0..84207844fd 100644 --- a/runconfig/parse.go +++ b/runconfig/parse.go @@ -53,6 +53,8 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe flVolumes = opts.NewListOpts(nil) flTmpfs = opts.NewListOpts(nil) flBlkioWeightDevice = opts.NewWeightdeviceOpt(opts.ValidateWeightDevice) + flDeviceReadBps = opts.NewThrottledeviceOpt(opts.ValidateThrottleBpsDevice) + flDeviceWriteBps = opts.NewThrottledeviceOpt(opts.ValidateThrottleBpsDevice) flLinks = opts.NewListOpts(opts.ValidateLink) flEnv = opts.NewListOpts(opts.ValidateEnv) flLabels = opts.NewListOpts(opts.ValidateEnv) @@ -113,6 +115,8 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe cmd.Var(&flAttach, []string{"a", "-attach"}, "Attach to STDIN, STDOUT or STDERR") cmd.Var(&flBlkioWeightDevice, []string{"-blkio-weight-device"}, "Block IO weight (relative device weight)") + cmd.Var(&flDeviceReadBps, []string{"-device-read-bps"}, "Limit read rate (bytes per second) from a device") + cmd.Var(&flDeviceWriteBps, []string{"-device-write-bps"}, "Limit write rate (bytes per second) to a device") cmd.Var(&flVolumes, []string{"v", "-volume"}, "Bind mount a volume") cmd.Var(&flTmpfs, []string{"-tmpfs"}, "Mount a tmpfs directory") cmd.Var(&flLinks, []string{"-link"}, "Add link to another container") @@ -338,21 +342,23 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe } resources := Resources{ - CgroupParent: *flCgroupParent, - Memory: flMemory, - MemoryReservation: MemoryReservation, - MemorySwap: memorySwap, - MemorySwappiness: flSwappiness, - KernelMemory: KernelMemory, - CPUShares: *flCPUShares, - CPUPeriod: *flCPUPeriod, - CpusetCpus: *flCpusetCpus, - CpusetMems: *flCpusetMems, - CPUQuota: *flCPUQuota, - BlkioWeight: *flBlkioWeight, - BlkioWeightDevice: flBlkioWeightDevice.GetList(), - Ulimits: flUlimits.GetList(), - Devices: deviceMappings, + CgroupParent: *flCgroupParent, + Memory: flMemory, + MemoryReservation: MemoryReservation, + MemorySwap: memorySwap, + MemorySwappiness: flSwappiness, + KernelMemory: KernelMemory, + CPUShares: *flCPUShares, + CPUPeriod: *flCPUPeriod, + CpusetCpus: *flCpusetCpus, + CpusetMems: *flCpusetMems, + CPUQuota: *flCPUQuota, + BlkioWeight: *flBlkioWeight, + BlkioWeightDevice: flBlkioWeightDevice.GetList(), + BlkioDeviceReadBps: flDeviceReadBps.GetList(), + BlkioDeviceWriteBps: flDeviceWriteBps.GetList(), + Ulimits: flUlimits.GetList(), + Devices: deviceMappings, } config := &Config{