Browse code

Add IO Resource Controls for Windows

Signed-off-by: Darren Stahl <darst@microsoft.com>

Darren Stahl authored on 2016/02/25 10:51:46
Showing 10 changed files
... ...
@@ -450,6 +450,9 @@ func verifyContainerResources(resources *containertypes.Resources, sysInfo *sysi
450 450
 	if resources.BlkioWeight > 0 && (resources.BlkioWeight < 10 || resources.BlkioWeight > 1000) {
451 451
 		return warnings, fmt.Errorf("Range of blkio weight is from 10 to 1000")
452 452
 	}
453
+	if resources.IOMaximumBandwidth != 0 || resources.IOMaximumIOps != 0 {
454
+		return warnings, fmt.Errorf("Invalid QoS settings: %s does not support Maximum IO Bandwidth or Maximum IO IOps", runtime.GOOS)
455
+	}
453 456
 	if len(resources.BlkioWeightDevice) > 0 && !sysInfo.BlkioWeightDevice {
454 457
 		warnings = append(warnings, "Your kernel does not support Block I/O weight_device.")
455 458
 		logrus.Warnf("Your kernel does not support Block I/O weight_device. Weight-device discarded.")
... ...
@@ -13,18 +13,18 @@ import (
13 13
 	"github.com/Sirupsen/logrus"
14 14
 	"github.com/docker/docker/container"
15 15
 	"github.com/docker/docker/daemon/graphdriver"
16
+	"github.com/docker/docker/daemon/graphdriver/windows" // register the windows graph driver
16 17
 	"github.com/docker/docker/dockerversion"
17 18
 	"github.com/docker/docker/image"
18 19
 	"github.com/docker/docker/layer"
19
-	"github.com/docker/docker/pkg/sysinfo"
20
-	"github.com/docker/docker/reference"
21
-	"github.com/docker/docker/runconfig"
22
-	// register the windows graph driver
23
-	"github.com/docker/docker/daemon/graphdriver/windows"
24 20
 	"github.com/docker/docker/pkg/idtools"
25 21
 	"github.com/docker/docker/pkg/parsers"
22
+	"github.com/docker/docker/pkg/sysinfo"
26 23
 	"github.com/docker/docker/pkg/system"
24
+	"github.com/docker/docker/reference"
25
+	"github.com/docker/docker/runconfig"
27 26
 	"github.com/docker/engine-api/types"
27
+	pblkiodev "github.com/docker/engine-api/types/blkiodev"
28 28
 	containertypes "github.com/docker/engine-api/types/container"
29 29
 	"github.com/docker/libnetwork"
30 30
 	nwconfig "github.com/docker/libnetwork/config"
... ...
@@ -107,13 +107,44 @@ func verifyContainerResources(resources *containertypes.Resources, sysInfo *sysi
107 107
 		return warnings, fmt.Errorf("Conflicting options: CPU Shares and CPU Percent cannot both be set")
108 108
 	}
109 109
 
110
+	// TODO Windows: Add more validation of resource settings not supported on Windows
111
+
112
+	if resources.BlkioWeight > 0 {
113
+		warnings = append(warnings, "Windows does not support Block I/O weight. Weight discarded.")
114
+		logrus.Warnf("Windows does not support Block I/O weight. --blkio-weight discarded.")
115
+		resources.BlkioWeight = 0
116
+	}
117
+	if len(resources.BlkioWeightDevice) > 0 {
118
+		warnings = append(warnings, "Windows does not support Block I/O weight_device.")
119
+		logrus.Warnf("Windows does not support Block I/O weight_device. --blkio-weight-device discarded.")
120
+		resources.BlkioWeightDevice = []*pblkiodev.WeightDevice{}
121
+	}
122
+	if len(resources.BlkioDeviceReadBps) > 0 {
123
+		warnings = append(warnings, "Windows does not support Block read limit in bytes per second.")
124
+		logrus.Warnf("Windows does not support Block I/O read limit in bytes per second. --device-read-bps discarded.")
125
+		resources.BlkioDeviceReadBps = []*pblkiodev.ThrottleDevice{}
126
+	}
127
+	if len(resources.BlkioDeviceWriteBps) > 0 {
128
+		warnings = append(warnings, "Windows does not support Block write limit in bytes per second.")
129
+		logrus.Warnf("Windows does not support Block I/O write limit in bytes per second. --device-write-bps discarded.")
130
+		resources.BlkioDeviceWriteBps = []*pblkiodev.ThrottleDevice{}
131
+	}
132
+	if len(resources.BlkioDeviceReadIOps) > 0 {
133
+		warnings = append(warnings, "Windows does not support Block read limit in IO per second.")
134
+		logrus.Warnf("Windows does not support Block I/O read limit in IO per second. -device-read-iops discarded.")
135
+		resources.BlkioDeviceReadIOps = []*pblkiodev.ThrottleDevice{}
136
+	}
137
+	if len(resources.BlkioDeviceWriteIOps) > 0 {
138
+		warnings = append(warnings, "Windows does not support Block write limit in IO per second.")
139
+		logrus.Warnf("Windows does not support Block I/O write limit in IO per second. --device-write-iops discarded.")
140
+		resources.BlkioDeviceWriteIOps = []*pblkiodev.ThrottleDevice{}
141
+	}
110 142
 	return warnings, nil
111 143
 }
112 144
 
113 145
 // verifyPlatformContainerSettings performs platform-specific validation of the
114 146
 // hostconfig and config structures.
115 147
 func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) ([]string, error) {
116
-
117 148
 	warnings := []string{}
118 149
 
119 150
 	w, err := verifyContainerResources(&hostConfig.Resources, nil)
... ...
@@ -181,9 +181,9 @@ func (daemon *Daemon) createSpec(c *container.Container) (*libcontainerd.Spec, e
181 181
 		//TODO Bandwidth: ...,
182 182
 		},
183 183
 		Storage: &windowsoci.Storage{
184
-		//TODO Bps: ...,
185
-		//TODO Iops: ...,
186
-		//TODO SandboxSize: ...,
184
+			Bps:  &c.HostConfig.IOMaximumBandwidth,
185
+			Iops: &c.HostConfig.IOMaximumIOps,
186
+			//TODO SandboxSize: ...,
187 187
 		},
188 188
 	}
189 189
 	return (*libcontainerd.Spec)(&s), nil
... ...
@@ -115,6 +115,7 @@ This section lists each version from latest to oldest.  Each listing includes a
115 115
 * `POST /containers/create` now takes `StorageOpt` field.
116 116
 * `GET /info` now returns `SecurityOptions` field, showing if `apparmor`, `seccomp`, or `selinux` is supported.
117 117
 * `GET /networks` now supports filtering by `label`.
118
+* `POST /containers/create` now takes `MaximumIOps` and `MaximumIOBps` fields. Windows daemon only.
118 119
 
119 120
 ### v1.23 API changes
120 121
 
... ...
@@ -288,6 +288,8 @@ Create a container
288 288
              "CpuQuota": 50000,
289 289
              "CpusetCpus": "0,1",
290 290
              "CpusetMems": "0,1",
291
+             "MaximumIOps": 0,
292
+             "MaximumIOBps": 0,
291 293
              "BlkioWeight": 300,
292 294
              "BlkioWeightDevice": [{}],
293 295
              "BlkioDeviceReadBps": [{}],
... ...
@@ -392,6 +394,8 @@ Json Parameters:
392 392
     -   **CpuQuota** - Microseconds of CPU time that the container can get in a CPU period.
393 393
     -   **CpusetCpus** - String value containing the `cgroups CpusetCpus` to use.
394 394
     -   **CpusetMems** - Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems.
395
+    -   **MaximumIOps** - Maximum IO absolute rate in terms of IOps. MaximumIOps and MaximumIOBps are mutually exclusive settings.
396
+    -   **MaximumIOBps** - Maximum IO absolute rate in terms of bytes per second. MaximumIOps and MaximumIOBps are mutually exclusive settings.
395 397
     -   **BlkioWeight** - Block IO weight (relative weight) accepts a weight value between 10 and 1000.
396 398
     -   **BlkioWeightDevice** - Block IO weight (relative device weight) in the form of:        `"BlkioWeightDevice": [{"Path": "device_path", "Weight": weight}]`
397 399
     -   **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`
533 533
 		"ExecIDs": null,
534 534
 		"HostConfig": {
535 535
 			"Binds": null,
536
+			"MaximumIOps": 0,
537
+			"MaximumIOBps": 0,
536 538
 			"BlkioWeight": 0,
537 539
 			"BlkioWeightDevice": [{}],
538 540
 			"BlkioDeviceReadBps": [{}],
... ...
@@ -59,6 +59,15 @@ parent = "smn_cli"
59 59
       --log-opt=[]                  Log driver specific options
60 60
       -m, --memory=""               Memory limit
61 61
       --mac-address=""              Container MAC address (e.g. 92:d0:c6:0a:29:33)
62
+      --io-maxbandwidth=""          Maximum IO bandwidth limit for the system drive
63
+                                    (Windows only). The format is `<number><unit>`.
64
+                                    Unit is optional and can be `b` (bytes per second),
65
+                                    `k` (kilobytes per second), `m` (megabytes per second),
66
+                                    or `g` (gigabytes per second). If you omit the unit,
67
+                                    the system uses bytes per second.
68
+                                    --io-maxbandwidth and --io-maxiops are mutually exclusive options.
69
+      --io-maxiops=0                Maximum IO per second limit for the system drive (Windows only).
70
+                                    --io-maxbandwidth and --io-maxiops are mutually exclusive options.
62 71
       --memory-reservation=""       Memory soft limit
63 72
       --memory-swap=""              A positive integer equal to memory plus swap. Specify -1 to enable unlimited swap.
64 73
       --memory-swappiness=""        Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100.
... ...
@@ -62,6 +62,11 @@ func DecodeContainerConfig(src io.Reader) (*container.Config, *container.HostCon
62 62
 	if err := ValidateIsolation(hc); err != nil {
63 63
 		return nil, nil, nil, err
64 64
 	}
65
+
66
+	// Validate QoS
67
+	if err := ValidateQoS(hc); err != nil {
68
+		return nil, nil, nil, err
69
+	}
65 70
 	return w.Config, hc, w.NetworkingConfig, nil
66 71
 }
67 72
 
... ...
@@ -87,3 +87,21 @@ func ValidateIsolation(hc *container.HostConfig) error {
87 87
 	}
88 88
 	return nil
89 89
 }
90
+
91
+// ValidateQoS performs platform specific validation of the QoS settings
92
+// a disk can be limited by either Bps or IOps, but not both.
93
+func ValidateQoS(hc *container.HostConfig) error {
94
+	// We may not be passed a host config, such as in the case of docker commit
95
+	if hc == nil {
96
+		return nil
97
+	}
98
+
99
+	if hc.IOMaximumBandwidth != 0 {
100
+		return fmt.Errorf("invalid QoS settings: %s does not support --maximum-bandwidth", runtime.GOOS)
101
+	}
102
+
103
+	if hc.IOMaximumIOps != 0 {
104
+		return fmt.Errorf("invalid QoS settings: %s does not support --maximum-iops", runtime.GOOS)
105
+	}
106
+	return nil
107
+}
... ...
@@ -44,3 +44,17 @@ func ValidateIsolation(hc *container.HostConfig) error {
44 44
 	}
45 45
 	return nil
46 46
 }
47
+
48
+// ValidateQoS performs platform specific validation of the Qos settings
49
+// a disk can be limited by either Bps or IOps, but not both.
50
+func ValidateQoS(hc *container.HostConfig) error {
51
+	// We may not be passed a host config, such as in the case of docker commit
52
+	if hc == nil {
53
+		return nil
54
+	}
55
+
56
+	if hc.IOMaximumIOps != 0 && hc.IOMaximumBandwidth != 0 {
57
+		return fmt.Errorf("invalid QoS settings: maximum bandwidth and maximum iops cannot both be set")
58
+	}
59
+	return nil
60
+}
... ...
@@ -84,6 +84,8 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
84 84
 		flCpusetCpus        = cmd.String([]string{"-cpuset-cpus"}, "", "CPUs in which to allow execution (0-3, 0,1)")
85 85
 		flCpusetMems        = cmd.String([]string{"-cpuset-mems"}, "", "MEMs in which to allow execution (0-3, 0,1)")
86 86
 		flBlkioWeight       = cmd.Uint16([]string{"-blkio-weight"}, 0, "Block IO (relative weight), between 10 and 1000")
87
+		flIOMaxBandwidth    = cmd.String([]string{"-io-maxbandwidth"}, "", "Maximum IO bandwidth limit for the system drive (Windows only)")
88
+		flIOMaxIOps         = cmd.Uint64([]string{"-io-maxiops"}, 0, "Maximum IOps limit for the system drive (Windows only)")
87 89
 		flSwappiness        = cmd.Int64([]string{"-memory-swappiness"}, -1, "Tune container memory swappiness (0 to 100)")
88 90
 		flNetMode           = cmd.String([]string{"-net"}, "default", "Connect a container to a network")
89 91
 		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
210 210
 		}
211 211
 	}
212 212
 
213
+	// TODO FIXME units.RAMInBytes should have a uint64 version
214
+	var maxIOBandwidth int64
215
+	if *flIOMaxBandwidth != "" {
216
+		maxIOBandwidth, err = units.RAMInBytes(*flIOMaxBandwidth)
217
+		if err != nil {
218
+			return nil, nil, nil, cmd, err
219
+		}
220
+		if maxIOBandwidth < 0 {
221
+			return nil, nil, nil, cmd, fmt.Errorf("invalid value: %s. Maximum IO Bandwidth must be positive", *flIOMaxBandwidth)
222
+		}
223
+	}
224
+
213 225
 	var binds []string
214 226
 	// add any bind targets to the list of container volumes
215 227
 	for bind := range flVolumes.GetMap() {
... ...
@@ -368,6 +382,8 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
368 368
 		BlkioDeviceWriteBps:  flDeviceWriteBps.GetList(),
369 369
 		BlkioDeviceReadIOps:  flDeviceReadIOps.GetList(),
370 370
 		BlkioDeviceWriteIOps: flDeviceWriteIOps.GetList(),
371
+		IOMaximumIOps:        *flIOMaxIOps,
372
+		IOMaximumBandwidth:   uint64(maxIOBandwidth),
371 373
 		Ulimits:              flUlimits.GetList(),
372 374
 		Devices:              deviceMappings,
373 375
 	}