Browse Source

Merge pull request #21384 from Microsoft/10662-IOResourceControls

Add IO Resource Controls for Windows
John Howard 9 years ago
parent
commit
a8c5ba7517

+ 3 - 0
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.")

+ 37 - 6
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)

+ 3 - 3
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

+ 1 - 0
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
 

+ 6 - 0
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": [{}],

+ 9 - 0
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 `<number><unit>`.
+                                    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.

+ 5 - 0
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
 }
 

+ 18 - 0
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
+}

+ 14 - 0
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
+}

+ 16 - 0
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,
 	}