Windows: Add cpu count option

Signed-off-by: Darren Stahl <darst@microsoft.com>
This commit is contained in:
Darren Stahl 2016-11-01 13:02:46 -07:00
parent d7d0bc1d58
commit 4e15420b9b
12 changed files with 187 additions and 20 deletions

View file

@ -27,10 +27,13 @@ import (
)
const (
defaultNetworkSpace = "172.16.0.0/12"
platformSupported = true
windowsMinCPUShares = 1
windowsMaxCPUShares = 10000
defaultNetworkSpace = "172.16.0.0/12"
platformSupported = true
windowsMinCPUShares = 1
windowsMaxCPUShares = 10000
windowsMinCPUPercent = 1
windowsMaxCPUPercent = 100
windowsMinCPUCount = 1
)
func getBlkioWeightDevices(config *containertypes.HostConfig) ([]blkiodev.WeightDevice, error) {
@ -80,6 +83,15 @@ func (daemon *Daemon) adaptContainerSettings(hostConfig *containertypes.HostConf
return nil
}
numCPU := int64(sysinfo.NumCPU())
if hostConfig.CPUCount < 0 {
logrus.Warnf("Changing requested CPUCount of %d to minimum allowed of %d", hostConfig.CPUCount, windowsMinCPUCount)
hostConfig.CPUCount = windowsMinCPUCount
} else if hostConfig.CPUCount > numCPU {
logrus.Warnf("Changing requested CPUCount of %d to current number of processors, %d", hostConfig.CPUCount, numCPU)
hostConfig.CPUCount = numCPU
}
if hostConfig.CPUShares < 0 {
logrus.Warnf("Changing requested CPUShares of %d to minimum allowed of %d", hostConfig.CPUShares, windowsMinCPUShares)
hostConfig.CPUShares = windowsMinCPUShares
@ -88,19 +100,42 @@ func (daemon *Daemon) adaptContainerSettings(hostConfig *containertypes.HostConf
hostConfig.CPUShares = windowsMaxCPUShares
}
if hostConfig.CPUPercent < 0 {
logrus.Warnf("Changing requested CPUPercent of %d to minimum allowed of %d", hostConfig.CPUPercent, windowsMinCPUPercent)
hostConfig.CPUPercent = windowsMinCPUPercent
} else if hostConfig.CPUPercent > windowsMaxCPUPercent {
logrus.Warnf("Changing requested CPUPercent of %d to maximum allowed of %d", hostConfig.CPUPercent, windowsMaxCPUPercent)
hostConfig.CPUPercent = windowsMaxCPUPercent
}
return nil
}
func verifyContainerResources(resources *containertypes.Resources, sysInfo *sysinfo.SysInfo) ([]string, error) {
func verifyContainerResources(resources *containertypes.Resources, isHyperv bool) ([]string, error) {
warnings := []string{}
// cpu subsystem checks and adjustments
if resources.CPUPercent < 0 || resources.CPUPercent > 100 {
return warnings, fmt.Errorf("Range of CPU percent is from 1 to 100")
}
if resources.CPUPercent > 0 && resources.CPUShares > 0 {
return warnings, fmt.Errorf("Conflicting options: CPU Shares and CPU Percent cannot both be set")
if !isHyperv {
// The processor resource controls are mutually exclusive on
// Windows Server Containers, the order of precedence is
// CPUCount first, then CPUShares, and CPUPercent last.
if resources.CPUCount > 0 {
if resources.CPUShares > 0 {
warnings = append(warnings, "Conflicting options: CPU count takes priority over CPU shares on Windows Server Containers. CPU shares discarded")
logrus.Warn("Conflicting options: CPU count takes priority over CPU shares on Windows Server Containers. CPU shares discarded")
resources.CPUShares = 0
}
if resources.CPUPercent > 0 {
warnings = append(warnings, "Conflicting options: CPU count takes priority over CPU percent on Windows Server Containers. CPU percent discarded")
logrus.Warn("Conflicting options: CPU count takes priority over CPU percent on Windows Server Containers. CPU percent discarded")
resources.CPUPercent = 0
}
} else if resources.CPUShares > 0 {
if resources.CPUPercent > 0 {
warnings = append(warnings, "Conflicting options: CPU shares takes priority over CPU percent on Windows Server Containers. CPU percent discarded")
logrus.Warn("Conflicting options: CPU shares takes priority over CPU percent on Windows Server Containers. CPU percent discarded")
resources.CPUPercent = 0
}
}
}
if resources.NanoCPUs > 0 && resources.CPUPercent > 0 {
@ -154,7 +189,7 @@ func verifyContainerResources(resources *containertypes.Resources, sysInfo *sysi
func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) ([]string, error) {
warnings := []string{}
w, err := verifyContainerResources(&hostConfig.Resources, nil)
w, err := verifyContainerResources(&hostConfig.Resources, daemon.runAsHyperVContainer(hostConfig))
warnings = append(warnings, w...)
if err != nil {
return warnings, err
@ -388,14 +423,14 @@ func setupDaemonRoot(config *Config, rootDir string, rootUID, rootGID int) error
}
// runasHyperVContainer returns true if we are going to run as a Hyper-V container
func (daemon *Daemon) runAsHyperVContainer(container *container.Container) bool {
if container.HostConfig.Isolation.IsDefault() {
func (daemon *Daemon) runAsHyperVContainer(hostConfig *containertypes.HostConfig) bool {
if hostConfig.Isolation.IsDefault() {
// Container is set to use the default, so take the default from the daemon configuration
return daemon.defaultIsolation.IsHyperV()
}
// Container is requesting an isolation mode. Honour it.
return container.HostConfig.Isolation.IsHyperV()
return hostConfig.Isolation.IsHyperV()
}
@ -403,7 +438,7 @@ func (daemon *Daemon) runAsHyperVContainer(container *container.Container) bool
// container start to call mount.
func (daemon *Daemon) conditionalMountOnStart(container *container.Container) error {
// We do not mount if a Hyper-V container
if !daemon.runAsHyperVContainer(container) {
if !daemon.runAsHyperVContainer(container.HostConfig) {
return daemon.Mount(container)
}
return nil
@ -413,7 +448,7 @@ func (daemon *Daemon) conditionalMountOnStart(container *container.Container) er
// during the cleanup of a container to unmount.
func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.Container) error {
// We do not unmount if a Hyper-V container
if !daemon.runAsHyperVContainer(container) {
if !daemon.runAsHyperVContainer(container.HostConfig) {
return daemon.Unmount(container)
}
return nil

View file

@ -86,11 +86,13 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
if c.HostConfig.NanoCPUs > 0 {
cpuPercent = uint8(c.HostConfig.NanoCPUs * 100 / int64(sysinfo.NumCPU()) / 1e9)
}
cpuCount := uint64(c.HostConfig.CPUCount)
memoryLimit := uint64(c.HostConfig.Memory)
s.Windows.Resources = &specs.WindowsResources{
CPU: &specs.WindowsCPUResources{
Percent: &cpuPercent,
Shares: &cpuShares,
Count: &cpuCount,
},
Memory: &specs.WindowsMemoryResources{
Limit: &memoryLimit,

View file

@ -166,6 +166,7 @@ This section lists each version from latest to oldest. Each listing includes a
* `GET /nodes` and `GET /node/(id or name)` now return `Addr` as part of a node's `Status`, which is the address that that node connects to the manager from.
* The `HostConfig` field now includes `NanoCPUs` that represents CPU quota in units of 10<sup>-9</sup> CPUs.
* `GET /info` now returns more structured information about security options.
* The `HostConfig` field now includes `CpuCount` that represents the number of CPUs available for execution by the container. Windows daemon only.
### v1.24 API changes

View file

@ -303,6 +303,7 @@ Create a container
"MemoryReservation": 0,
"KernelMemory": 0,
"NanoCPUs": 500000,
"CpuCount": 4,
"CpuPercent": 80,
"CpuShares": 512,
"CpuPeriod": 100000,
@ -427,7 +428,14 @@ Create a container
- **MemoryReservation** - Memory soft limit in bytes.
- **KernelMemory** - Kernel memory limit in bytes.
- **NanoCPUs** - CPU quota in units of 10<sup>-9</sup> CPUs.
- **CpuPercent** - An integer value containing the usable percentage of the available CPUs. (Windows daemon only)
- **CpuCount** - An integer value containing the number of usable CPUs.
Windows daemon only. On Windows Server containers,
the processor resource controls are mutually exclusive, the order of precedence
is CPUCount first, then CPUShares, and CPUPercent last.
- **CpuPercent** - An integer value containing the usable percentage of
the available CPUs. Windows daemon only. On Windows Server containers,
the processor resource controls are mutually exclusive, the order of precedence
is CPUCount first, then CPUShares, and CPUPercent last.
- **CpuShares** - An integer value containing the container's CPU Shares
(ie. the relative weight vs other containers).
- **CpuPeriod** - The length of a CPU period in microseconds.
@ -623,6 +631,7 @@ Return low-level information on the container `id`
"ContainerIDFile": "",
"CpusetCpus": "",
"CpusetMems": "",
"CpuCount": 4,
"CpuPercent": 80,
"CpuShares": 0,
"CpuPeriod": 100000,

View file

@ -31,6 +31,9 @@ Options:
--cap-drop value Drop Linux capabilities (default [])
--cgroup-parent string Optional parent cgroup for the container
--cidfile string Write the container ID to the file
--cpu-count int The number of CPUs available for execution by the container.
Windows daemon only. On Windows Server containers, this is
approximated as a percentage of total CPU usage.
--cpu-percent int CPU percent (Windows only)
--cpu-period int Limit CPU CFS (Completely Fair Scheduler) period
--cpu-quota int Limit CPU CFS (Completely Fair Scheduler) quota

View file

@ -29,7 +29,14 @@ Options:
--cap-drop value Drop Linux capabilities (default [])
--cgroup-parent string Optional parent cgroup for the container
--cidfile string Write the container ID to the file
--cpu-percent int CPU percent (Windows only)
--cpu-count int The number of CPUs available for execution by the container.
Windows daemon only. On Windows Server containers, this is
approximated as a percentage of total CPU usage.
--cpu-percent int Limit percentage of CPU available for execution
by the container. Windows daemon only.
The processor resource controls are mutually
exclusive, the order of precedence is CPUCount
first, then CPUShares, and CPUPercent last.
--cpu-period int Limit CPU CFS (Completely Fair Scheduler) period
--cpu-quota int Limit CPU CFS (Completely Fair Scheduler) quota
-c, --cpu-shares int CPU shares (relative weight)

View file

@ -4766,3 +4766,67 @@ func (s *DockerSuite) TestRunMount(c *check.C) {
}
}
}
func (s *DockerSuite) TestRunWindowsWithCPUCount(c *check.C) {
testRequires(c, DaemonIsWindows)
out, _ := dockerCmd(c, "run", "--cpu-count=1", "--name", "test", "busybox", "echo", "testing")
c.Assert(strings.TrimSpace(out), checker.Equals, "testing")
out = inspectField(c, "test", "HostConfig.CPUCount")
c.Assert(out, check.Equals, "1")
}
func (s *DockerSuite) TestRunWindowsWithCPUShares(c *check.C) {
testRequires(c, DaemonIsWindows)
out, _ := dockerCmd(c, "run", "--cpu-shares=1000", "--name", "test", "busybox", "echo", "testing")
c.Assert(strings.TrimSpace(out), checker.Equals, "testing")
out = inspectField(c, "test", "HostConfig.CPUShares")
c.Assert(out, check.Equals, "1000")
}
func (s *DockerSuite) TestRunWindowsWithCPUPercent(c *check.C) {
testRequires(c, DaemonIsWindows)
out, _ := dockerCmd(c, "run", "--cpu-percent=80", "--name", "test", "busybox", "echo", "testing")
c.Assert(strings.TrimSpace(out), checker.Equals, "testing")
out = inspectField(c, "test", "HostConfig.CPUPercent")
c.Assert(out, check.Equals, "80")
}
func (s *DockerSuite) TestRunProcessIsolationWithCPUCountCPUSharesAndCPUPercent(c *check.C) {
testRequires(c, DaemonIsWindows, IsolationIsProcess)
out, _ := dockerCmd(c, "run", "--cpu-count=1", "--cpu-shares=1000", "--cpu-percent=80", "--name", "test", "busybox", "echo", "testing")
c.Assert(strings.TrimSpace(out), checker.Contains, "WARNING: Conflicting options: CPU count takes priority over CPU shares on Windows Server Containers. CPU shares discarded")
c.Assert(strings.TrimSpace(out), checker.Contains, "WARNING: Conflicting options: CPU count takes priority over CPU percent on Windows Server Containers. CPU percent discarded")
c.Assert(strings.TrimSpace(out), checker.Contains, "testing")
out = inspectField(c, "test", "HostConfig.CPUCount")
c.Assert(out, check.Equals, "1")
out = inspectField(c, "test", "HostConfig.CPUShares")
c.Assert(out, check.Equals, "0")
out = inspectField(c, "test", "HostConfig.CPUPercent")
c.Assert(out, check.Equals, "0")
}
func (s *DockerSuite) TestRunHypervIsolationWithCPUCountCPUSharesAndCPUPercent(c *check.C) {
testRequires(c, DaemonIsWindows, IsolationIsHyperv)
out, _ := dockerCmd(c, "run", "--cpu-count=1", "--cpu-shares=1000", "--cpu-percent=80", "--name", "test", "busybox", "echo", "testing")
c.Assert(strings.TrimSpace(out), checker.Contains, "testing")
out = inspectField(c, "test", "HostConfig.CPUCount")
c.Assert(out, check.Equals, "1")
out = inspectField(c, "test", "HostConfig.CPUShares")
c.Assert(out, check.Equals, "1000")
out = inspectField(c, "test", "HostConfig.CPUPercent")
c.Assert(out, check.Equals, "80")
}

View file

@ -218,6 +218,18 @@ var (
},
"Test requires containers are not pausable.",
}
IsolationIsHyperv = testRequirement{
func() bool {
return daemonPlatform == "windows" && isolation == "hyperv"
},
"Test requires a Windows daemon running default isolation mode of hyperv.",
}
IsolationIsProcess = testRequirement{
func() bool {
return daemonPlatform == "windows" && isolation == "process"
},
"Test requires a Windows daemon running default isolation mode of process.",
}
)
// testRequires checks if the environment satisfies the requirements

View file

@ -110,6 +110,9 @@ func (clnt *client) Create(containerID string, checkpoint string, checkpointDir
if spec.Windows.Resources != nil {
if spec.Windows.Resources.CPU != nil {
if spec.Windows.Resources.CPU.Count != nil {
configuration.ProcessorCount = uint32(*spec.Windows.Resources.CPU.Count)
}
if spec.Windows.Resources.CPU.Shares != nil {
configuration.ProcessorWeight = uint64(*spec.Windows.Resources.CPU.Shares)
}

View file

@ -15,6 +15,8 @@ docker-create - Create a new container
[**--cap-drop**[=*[]*]]
[**--cgroup-parent**[=*CGROUP-PATH*]]
[**--cidfile**[=*CIDFILE*]]
[**--cpu-count**[=*0*]]
[**--cpu-percent**[=*0*]]
[**--cpu-period**[=*0*]]
[**--cpu-quota**[=*0*]]
[**--cpu-rt-period**[=*0*]]
@ -124,6 +126,18 @@ The initial status of the container created with **docker create** is 'created'.
**--cidfile**=""
Write the container ID to the file
**--cpu-count**=*0*
Limit the number of CPUs available for execution by the container.
On Windows Server containers, this is approximated as a percentage of total CPU usage.
On Windows Server containers, the processor resource controls are mutually exclusive, the order of precedence is CPUCount first, then CPUShares, and CPUPercent last.
**--cpu-percent**=*0*
Limit the percentage of CPU available for execution by a container running on a Windows daemon.
On Windows Server containers, the processor resource controls are mutually exclusive, the order of precedence is CPUCount first, then CPUShares, and CPUPercent last.
**--cpu-period**=*0*
Limit the CPU CFS (Completely Fair Scheduler) period

View file

@ -15,6 +15,8 @@ docker-run - Run a command in a new container
[**--cap-drop**[=*[]*]]
[**--cgroup-parent**[=*CGROUP-PATH*]]
[**--cidfile**[=*CIDFILE*]]
[**--cpu-count**[=*0*]]
[**--cpu-percent**[=*0*]]
[**--cpu-period**[=*0*]]
[**--cpu-quota**[=*0*]]
[**--cpu-rt-period**[=*0*]]
@ -174,6 +176,18 @@ division of CPU shares:
**--cidfile**=""
Write the container ID to the file
**--cpu-count**=*0*
Limit the number of CPUs available for execution by the container.
On Windows Server containers, this is approximated as a percentage of total CPU usage.
On Windows Server containers, the processor resource controls are mutually exclusive, the order of precedence is CPUCount first, then CPUShares, and CPUPercent last.
**--cpu-percent**=*0*
Limit the percentage of CPU available for execution by a container running on a Windows daemon.
On Windows Server containers, the processor resource controls are mutually exclusive, the order of precedence is CPUCount first, then CPUShares, and CPUPercent last.
**--cpu-period**=*0*
Limit the CPU CFS (Completely Fair Scheduler) period

View file

@ -73,6 +73,7 @@ type ContainerOptions struct {
kernelMemory string
user string
workingDir string
cpuCount int64
cpuShares int64
cpuPercent int64
cpuPeriod int64
@ -227,6 +228,7 @@ func AddFlags(flags *pflag.FlagSet) *ContainerOptions {
flags.StringVar(&copts.containerIDFile, "cidfile", "", "Write the container ID to the file")
flags.StringVar(&copts.cpusetCpus, "cpuset-cpus", "", "CPUs in which to allow execution (0-3, 0,1)")
flags.StringVar(&copts.cpusetMems, "cpuset-mems", "", "MEMs in which to allow execution (0-3, 0,1)")
flags.Int64Var(&copts.cpuCount, "cpu-count", 0, "CPU count (Windows only)")
flags.Int64Var(&copts.cpuPercent, "cpu-percent", 0, "CPU percent (Windows only)")
flags.Int64Var(&copts.cpuPeriod, "cpu-period", 0, "Limit CPU CFS (Completely Fair Scheduler) period")
flags.Int64Var(&copts.cpuQuota, "cpu-quota", 0, "Limit CPU CFS (Completely Fair Scheduler) quota")
@ -529,6 +531,7 @@ func Parse(flags *pflag.FlagSet, copts *ContainerOptions) (*container.Config, *c
KernelMemory: kernelMemory,
OomKillDisable: &copts.oomKillDisable,
NanoCPUs: copts.cpus.Value(),
CPUCount: copts.cpuCount,
CPUPercent: copts.cpuPercent,
CPUShares: copts.cpuShares,
CPUPeriod: copts.cpuPeriod,