diff --git a/daemon/daemon_unix.go b/daemon/daemon_unix.go index 192372393c..69f57b909f 100644 --- a/daemon/daemon_unix.go +++ b/daemon/daemon_unix.go @@ -450,6 +450,9 @@ func verifyContainerResources(resources *containertypes.Resources, sysInfo *sysi if resources.BlkioWeight > 0 && (resources.BlkioWeight < 10 || resources.BlkioWeight > 1000) { return warnings, fmt.Errorf("Range of blkio weight is from 10 to 1000") } + if resources.IOMaximumBandwidth != 0 || resources.IOMaximumIOps != 0 { + return warnings, fmt.Errorf("Invalid QoS settings: %s does not support Maximum IO Bandwidth or Maximum IO IOps", runtime.GOOS) + } if len(resources.BlkioWeightDevice) > 0 && !sysInfo.BlkioWeightDevice { warnings = append(warnings, "Your kernel does not support Block I/O weight_device.") logrus.Warnf("Your kernel does not support Block I/O weight_device. Weight-device discarded.") diff --git a/daemon/daemon_windows.go b/daemon/daemon_windows.go index 83a1caadba..83775c3fd2 100644 --- a/daemon/daemon_windows.go +++ b/daemon/daemon_windows.go @@ -13,18 +13,18 @@ import ( "github.com/Sirupsen/logrus" "github.com/docker/docker/container" "github.com/docker/docker/daemon/graphdriver" + "github.com/docker/docker/daemon/graphdriver/windows" // register the windows graph driver "github.com/docker/docker/dockerversion" "github.com/docker/docker/image" "github.com/docker/docker/layer" - "github.com/docker/docker/pkg/sysinfo" - "github.com/docker/docker/reference" - "github.com/docker/docker/runconfig" - // register the windows graph driver - "github.com/docker/docker/daemon/graphdriver/windows" "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/parsers" + "github.com/docker/docker/pkg/sysinfo" "github.com/docker/docker/pkg/system" + "github.com/docker/docker/reference" + "github.com/docker/docker/runconfig" "github.com/docker/engine-api/types" + pblkiodev "github.com/docker/engine-api/types/blkiodev" containertypes "github.com/docker/engine-api/types/container" "github.com/docker/libnetwork" nwconfig "github.com/docker/libnetwork/config" @@ -107,13 +107,44 @@ func verifyContainerResources(resources *containertypes.Resources, sysInfo *sysi return warnings, fmt.Errorf("Conflicting options: CPU Shares and CPU Percent cannot both be set") } + // TODO Windows: Add more validation of resource settings not supported on Windows + + if resources.BlkioWeight > 0 { + warnings = append(warnings, "Windows does not support Block I/O weight. Weight discarded.") + logrus.Warnf("Windows does not support Block I/O weight. --blkio-weight discarded.") + resources.BlkioWeight = 0 + } + if len(resources.BlkioWeightDevice) > 0 { + warnings = append(warnings, "Windows does not support Block I/O weight_device.") + logrus.Warnf("Windows does not support Block I/O weight_device. --blkio-weight-device discarded.") + resources.BlkioWeightDevice = []*pblkiodev.WeightDevice{} + } + if len(resources.BlkioDeviceReadBps) > 0 { + warnings = append(warnings, "Windows does not support Block read limit in bytes per second.") + logrus.Warnf("Windows does not support Block I/O read limit in bytes per second. --device-read-bps discarded.") + resources.BlkioDeviceReadBps = []*pblkiodev.ThrottleDevice{} + } + if len(resources.BlkioDeviceWriteBps) > 0 { + warnings = append(warnings, "Windows does not support Block write limit in bytes per second.") + logrus.Warnf("Windows does not support Block I/O write limit in bytes per second. --device-write-bps discarded.") + resources.BlkioDeviceWriteBps = []*pblkiodev.ThrottleDevice{} + } + if len(resources.BlkioDeviceReadIOps) > 0 { + warnings = append(warnings, "Windows does not support Block read limit in IO per second.") + logrus.Warnf("Windows does not support Block I/O read limit in IO per second. -device-read-iops discarded.") + resources.BlkioDeviceReadIOps = []*pblkiodev.ThrottleDevice{} + } + if len(resources.BlkioDeviceWriteIOps) > 0 { + warnings = append(warnings, "Windows does not support Block write limit in IO per second.") + logrus.Warnf("Windows does not support Block I/O write limit in IO per second. --device-write-iops discarded.") + resources.BlkioDeviceWriteIOps = []*pblkiodev.ThrottleDevice{} + } return warnings, nil } // verifyPlatformContainerSettings performs platform-specific validation of the // hostconfig and config structures. func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) ([]string, error) { - warnings := []string{} w, err := verifyContainerResources(&hostConfig.Resources, nil) diff --git a/daemon/oci_windows.go b/daemon/oci_windows.go index a3ba893827..4f0c5ea453 100644 --- a/daemon/oci_windows.go +++ b/daemon/oci_windows.go @@ -181,9 +181,9 @@ func (daemon *Daemon) createSpec(c *container.Container) (*libcontainerd.Spec, e //TODO Bandwidth: ..., }, Storage: &windowsoci.Storage{ - //TODO Bps: ..., - //TODO Iops: ..., - //TODO SandboxSize: ..., + Bps: &c.HostConfig.IOMaximumBandwidth, + Iops: &c.HostConfig.IOMaximumIOps, + //TODO SandboxSize: ..., }, } return (*libcontainerd.Spec)(&s), nil diff --git a/docs/reference/api/docker_remote_api.md b/docs/reference/api/docker_remote_api.md index 01a1666ff3..d084942d32 100644 --- a/docs/reference/api/docker_remote_api.md +++ b/docs/reference/api/docker_remote_api.md @@ -115,6 +115,7 @@ This section lists each version from latest to oldest. Each listing includes a * `POST /containers/create` now takes `StorageOpt` field. * `GET /info` now returns `SecurityOptions` field, showing if `apparmor`, `seccomp`, or `selinux` is supported. * `GET /networks` now supports filtering by `label`. +* `POST /containers/create` now takes `MaximumIOps` and `MaximumIOBps` fields. Windows daemon only. ### v1.23 API changes diff --git a/docs/reference/api/docker_remote_api_v1.24.md b/docs/reference/api/docker_remote_api_v1.24.md index b5f9a1ec5f..fadf258321 100644 --- a/docs/reference/api/docker_remote_api_v1.24.md +++ b/docs/reference/api/docker_remote_api_v1.24.md @@ -288,6 +288,8 @@ Create a container "CpuQuota": 50000, "CpusetCpus": "0,1", "CpusetMems": "0,1", + "MaximumIOps": 0, + "MaximumIOBps": 0, "BlkioWeight": 300, "BlkioWeightDevice": [{}], "BlkioDeviceReadBps": [{}], @@ -392,6 +394,8 @@ Json Parameters: - **CpuQuota** - Microseconds of CPU time that the container can get in a CPU period. - **CpusetCpus** - String value containing the `cgroups CpusetCpus` to use. - **CpusetMems** - Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems. + - **MaximumIOps** - Maximum IO absolute rate in terms of IOps. MaximumIOps and MaximumIOBps are mutually exclusive settings. + - **MaximumIOBps** - Maximum IO absolute rate in terms of bytes per second. MaximumIOps and MaximumIOBps are mutually exclusive settings. - **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 (bytes per second) from a device in the form of: `"BlkioDeviceReadBps": [{"Path": "device_path", "Rate": rate}]`, for example: @@ -533,6 +537,8 @@ Return low-level information on the container `id` "ExecIDs": null, "HostConfig": { "Binds": null, + "MaximumIOps": 0, + "MaximumIOBps": 0, "BlkioWeight": 0, "BlkioWeightDevice": [{}], "BlkioDeviceReadBps": [{}], diff --git a/docs/reference/commandline/run.md b/docs/reference/commandline/run.md index c4392d0f05..530b1bfb14 100644 --- a/docs/reference/commandline/run.md +++ b/docs/reference/commandline/run.md @@ -59,6 +59,15 @@ parent = "smn_cli" --log-opt=[] Log driver specific options -m, --memory="" Memory limit --mac-address="" Container MAC address (e.g. 92:d0:c6:0a:29:33) + --io-maxbandwidth="" Maximum IO bandwidth limit for the system drive + (Windows only). The format is ``. + Unit is optional and can be `b` (bytes per second), + `k` (kilobytes per second), `m` (megabytes per second), + or `g` (gigabytes per second). If you omit the unit, + the system uses bytes per second. + --io-maxbandwidth and --io-maxiops are mutually exclusive options. + --io-maxiops=0 Maximum IO per second limit for the system drive (Windows only). + --io-maxbandwidth and --io-maxiops are mutually exclusive options. --memory-reservation="" Memory soft limit --memory-swap="" A positive integer equal to memory plus swap. Specify -1 to enable unlimited swap. --memory-swappiness="" Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100. diff --git a/runconfig/config.go b/runconfig/config.go index d99ebb2090..8145e4b1da 100644 --- a/runconfig/config.go +++ b/runconfig/config.go @@ -62,6 +62,11 @@ func DecodeContainerConfig(src io.Reader) (*container.Config, *container.HostCon if err := ValidateIsolation(hc); err != nil { return nil, nil, nil, err } + + // Validate QoS + if err := ValidateQoS(hc); err != nil { + return nil, nil, nil, err + } return w.Config, hc, w.NetworkingConfig, nil } diff --git a/runconfig/hostconfig_unix.go b/runconfig/hostconfig_unix.go index efc26112e3..97806d6da5 100644 --- a/runconfig/hostconfig_unix.go +++ b/runconfig/hostconfig_unix.go @@ -87,3 +87,21 @@ func ValidateIsolation(hc *container.HostConfig) error { } return nil } + +// ValidateQoS performs platform specific validation of the QoS settings +// a disk can be limited by either Bps or IOps, but not both. +func ValidateQoS(hc *container.HostConfig) error { + // We may not be passed a host config, such as in the case of docker commit + if hc == nil { + return nil + } + + if hc.IOMaximumBandwidth != 0 { + return fmt.Errorf("invalid QoS settings: %s does not support --maximum-bandwidth", runtime.GOOS) + } + + if hc.IOMaximumIOps != 0 { + return fmt.Errorf("invalid QoS settings: %s does not support --maximum-iops", runtime.GOOS) + } + return nil +} diff --git a/runconfig/hostconfig_windows.go b/runconfig/hostconfig_windows.go index bab24e4465..c89d0f70c2 100644 --- a/runconfig/hostconfig_windows.go +++ b/runconfig/hostconfig_windows.go @@ -44,3 +44,17 @@ func ValidateIsolation(hc *container.HostConfig) error { } return nil } + +// ValidateQoS performs platform specific validation of the Qos settings +// a disk can be limited by either Bps or IOps, but not both. +func ValidateQoS(hc *container.HostConfig) error { + // We may not be passed a host config, such as in the case of docker commit + if hc == nil { + return nil + } + + if hc.IOMaximumIOps != 0 && hc.IOMaximumBandwidth != 0 { + return fmt.Errorf("invalid QoS settings: maximum bandwidth and maximum iops cannot both be set") + } + return nil +} diff --git a/runconfig/opts/parse.go b/runconfig/opts/parse.go index a2c09dd2a8..c05603a457 100644 --- a/runconfig/opts/parse.go +++ b/runconfig/opts/parse.go @@ -84,6 +84,8 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host flCpusetCpus = cmd.String([]string{"-cpuset-cpus"}, "", "CPUs in which to allow execution (0-3, 0,1)") flCpusetMems = cmd.String([]string{"-cpuset-mems"}, "", "MEMs in which to allow execution (0-3, 0,1)") flBlkioWeight = cmd.Uint16([]string{"-blkio-weight"}, 0, "Block IO (relative weight), between 10 and 1000") + flIOMaxBandwidth = cmd.String([]string{"-io-maxbandwidth"}, "", "Maximum IO bandwidth limit for the system drive (Windows only)") + flIOMaxIOps = cmd.Uint64([]string{"-io-maxiops"}, 0, "Maximum IOps limit for the system drive (Windows only)") flSwappiness = cmd.Int64([]string{"-memory-swappiness"}, -1, "Tune container memory swappiness (0 to 100)") flNetMode = cmd.String([]string{"-net"}, "default", "Connect a container to a network") flMacAddress = cmd.String([]string{"-mac-address"}, "", "Container MAC address (e.g. 92:d0:c6:0a:29:33)") @@ -210,6 +212,18 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host } } + // TODO FIXME units.RAMInBytes should have a uint64 version + var maxIOBandwidth int64 + if *flIOMaxBandwidth != "" { + maxIOBandwidth, err = units.RAMInBytes(*flIOMaxBandwidth) + if err != nil { + return nil, nil, nil, cmd, err + } + if maxIOBandwidth < 0 { + return nil, nil, nil, cmd, fmt.Errorf("invalid value: %s. Maximum IO Bandwidth must be positive", *flIOMaxBandwidth) + } + } + var binds []string // add any bind targets to the list of container volumes for bind := range flVolumes.GetMap() { @@ -368,6 +382,8 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host BlkioDeviceWriteBps: flDeviceWriteBps.GetList(), BlkioDeviceReadIOps: flDeviceReadIOps.GetList(), BlkioDeviceWriteIOps: flDeviceWriteIOps.GetList(), + IOMaximumIOps: *flIOMaxIOps, + IOMaximumBandwidth: uint64(maxIOBandwidth), Ulimits: flUlimits.GetList(), Devices: deviceMappings, }