mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #11410 from cpuguy83/10191_build_resources
Allow setting resource constraints for build
This commit is contained in:
commit
2cde817458
11 changed files with 151 additions and 4 deletions
|
@ -90,6 +90,10 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
||||||
forceRm := cmd.Bool([]string{"-force-rm"}, false, "Always remove intermediate containers")
|
forceRm := cmd.Bool([]string{"-force-rm"}, false, "Always remove intermediate containers")
|
||||||
pull := cmd.Bool([]string{"-pull"}, false, "Always attempt to pull a newer version of the image")
|
pull := cmd.Bool([]string{"-pull"}, false, "Always attempt to pull a newer version of the image")
|
||||||
dockerfileName := cmd.String([]string{"f", "-file"}, "", "Name of the Dockerfile (Default is 'PATH/Dockerfile')")
|
dockerfileName := cmd.String([]string{"f", "-file"}, "", "Name of the Dockerfile (Default is 'PATH/Dockerfile')")
|
||||||
|
flMemoryString := cmd.String([]string{"m", "-memory"}, "", "Memory limit")
|
||||||
|
flMemorySwap := cmd.String([]string{"-memory-swap"}, "", "Total memory (memory + swap), '-1' to disable swap")
|
||||||
|
flCpuShares := cmd.Int64([]string{"c", "-cpu-shares"}, 0, "CPU shares (relative weight)")
|
||||||
|
flCpuSetCpus := cmd.String([]string{"-cpuset-cpus"}, "", "CPUs in which to allow execution (0-3, 0,1)")
|
||||||
|
|
||||||
cmd.Require(flag.Exact, 1)
|
cmd.Require(flag.Exact, 1)
|
||||||
|
|
||||||
|
@ -242,6 +246,28 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
||||||
Action: "Sending build context to Docker daemon",
|
Action: "Sending build context to Docker daemon",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var memory int64
|
||||||
|
if *flMemoryString != "" {
|
||||||
|
parsedMemory, err := units.RAMInBytes(*flMemoryString)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
memory = parsedMemory
|
||||||
|
}
|
||||||
|
|
||||||
|
var memorySwap int64
|
||||||
|
if *flMemorySwap != "" {
|
||||||
|
if *flMemorySwap == "-1" {
|
||||||
|
memorySwap = -1
|
||||||
|
} else {
|
||||||
|
parsedMemorySwap, err := units.RAMInBytes(*flMemorySwap)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
memorySwap = parsedMemorySwap
|
||||||
|
}
|
||||||
|
}
|
||||||
// Send the build context
|
// Send the build context
|
||||||
v := &url.Values{}
|
v := &url.Values{}
|
||||||
|
|
||||||
|
@ -283,6 +309,11 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
||||||
v.Set("pull", "1")
|
v.Set("pull", "1")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
v.Set("cpusetcpus", *flCpuSetCpus)
|
||||||
|
v.Set("cpushares", strconv.FormatInt(*flCpuShares, 10))
|
||||||
|
v.Set("memory", strconv.FormatInt(memory, 10))
|
||||||
|
v.Set("memswap", strconv.FormatInt(memorySwap, 10))
|
||||||
|
|
||||||
v.Set("dockerfile", *dockerfileName)
|
v.Set("dockerfile", *dockerfileName)
|
||||||
|
|
||||||
cli.LoadConfigFile()
|
cli.LoadConfigFile()
|
||||||
|
|
|
@ -1082,6 +1082,10 @@ func postBuild(eng *engine.Engine, version version.Version, w http.ResponseWrite
|
||||||
job.Setenv("forcerm", r.FormValue("forcerm"))
|
job.Setenv("forcerm", r.FormValue("forcerm"))
|
||||||
job.SetenvJson("authConfig", authConfig)
|
job.SetenvJson("authConfig", authConfig)
|
||||||
job.SetenvJson("configFile", configFile)
|
job.SetenvJson("configFile", configFile)
|
||||||
|
job.Setenv("memswap", r.FormValue("memswap"))
|
||||||
|
job.Setenv("memory", r.FormValue("memory"))
|
||||||
|
job.Setenv("cpusetcpus", r.FormValue("cpusetcpus"))
|
||||||
|
job.Setenv("cpushares", r.FormValue("cpushares"))
|
||||||
|
|
||||||
if err := job.Run(); err != nil {
|
if err := job.Run(); err != nil {
|
||||||
if !job.Stdout.Used() {
|
if !job.Stdout.Used() {
|
||||||
|
|
|
@ -125,6 +125,12 @@ type Builder struct {
|
||||||
context tarsum.TarSum // the context is a tarball that is uploaded by the client
|
context tarsum.TarSum // the context is a tarball that is uploaded by the client
|
||||||
contextPath string // the path of the temporary directory the local context is unpacked to (server side)
|
contextPath string // the path of the temporary directory the local context is unpacked to (server side)
|
||||||
noBaseImage bool // indicates that this build does not start from any base image, but is being built from an empty file system.
|
noBaseImage bool // indicates that this build does not start from any base image, but is being built from an empty file system.
|
||||||
|
|
||||||
|
// Set resource restrictions for build containers
|
||||||
|
cpuSetCpus string
|
||||||
|
cpuShares int64
|
||||||
|
memory int64
|
||||||
|
memorySwap int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the builder with the context. This is the lynchpin of this package. This
|
// Run the builder with the context. This is the lynchpin of this package. This
|
||||||
|
@ -156,6 +162,7 @@ func (b *Builder) Run(context io.Reader) (string, error) {
|
||||||
|
|
||||||
// some initializations that would not have been supplied by the caller.
|
// some initializations that would not have been supplied by the caller.
|
||||||
b.Config = &runconfig.Config{}
|
b.Config = &runconfig.Config{}
|
||||||
|
|
||||||
b.TmpContainers = map[string]struct{}{}
|
b.TmpContainers = map[string]struct{}{}
|
||||||
|
|
||||||
for i, n := range b.dockerfile.Children {
|
for i, n := range b.dockerfile.Children {
|
||||||
|
|
|
@ -34,6 +34,7 @@ import (
|
||||||
"github.com/docker/docker/pkg/tarsum"
|
"github.com/docker/docker/pkg/tarsum"
|
||||||
"github.com/docker/docker/pkg/urlutil"
|
"github.com/docker/docker/pkg/urlutil"
|
||||||
"github.com/docker/docker/registry"
|
"github.com/docker/docker/registry"
|
||||||
|
"github.com/docker/docker/runconfig"
|
||||||
"github.com/docker/docker/utils"
|
"github.com/docker/docker/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -537,10 +538,17 @@ func (b *Builder) create() (*daemon.Container, error) {
|
||||||
}
|
}
|
||||||
b.Config.Image = b.image
|
b.Config.Image = b.image
|
||||||
|
|
||||||
|
hostConfig := &runconfig.HostConfig{
|
||||||
|
CpuShares: b.cpuShares,
|
||||||
|
CpusetCpus: b.cpuSetCpus,
|
||||||
|
Memory: b.memory,
|
||||||
|
MemorySwap: b.memorySwap,
|
||||||
|
}
|
||||||
|
|
||||||
config := *b.Config
|
config := *b.Config
|
||||||
|
|
||||||
// Create the container
|
// Create the container
|
||||||
c, warnings, err := b.Daemon.Create(b.Config, nil, "")
|
c, warnings, err := b.Daemon.Create(b.Config, hostConfig, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,10 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) engine.Status {
|
||||||
rm = job.GetenvBool("rm")
|
rm = job.GetenvBool("rm")
|
||||||
forceRm = job.GetenvBool("forcerm")
|
forceRm = job.GetenvBool("forcerm")
|
||||||
pull = job.GetenvBool("pull")
|
pull = job.GetenvBool("pull")
|
||||||
|
memory = job.GetenvInt64("memory")
|
||||||
|
memorySwap = job.GetenvInt64("memswap")
|
||||||
|
cpuShares = job.GetenvInt64("cpushares")
|
||||||
|
cpuSetCpus = job.Getenv("cpusetcpus")
|
||||||
authConfig = ®istry.AuthConfig{}
|
authConfig = ®istry.AuthConfig{}
|
||||||
configFile = ®istry.ConfigFile{}
|
configFile = ®istry.ConfigFile{}
|
||||||
tag string
|
tag string
|
||||||
|
@ -145,6 +149,10 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) engine.Status {
|
||||||
AuthConfig: authConfig,
|
AuthConfig: authConfig,
|
||||||
AuthConfigFile: configFile,
|
AuthConfigFile: configFile,
|
||||||
dockerfileName: dockerfileName,
|
dockerfileName: dockerfileName,
|
||||||
|
cpuShares: cpuShares,
|
||||||
|
cpuSetCpus: cpuSetCpus,
|
||||||
|
memory: memory,
|
||||||
|
memorySwap: memorySwap,
|
||||||
}
|
}
|
||||||
|
|
||||||
id, err := builder.Run(context)
|
id, err := builder.Run(context)
|
||||||
|
|
|
@ -14,6 +14,11 @@ docker-build - Build a new image from the source code at PATH
|
||||||
[**-q**|**--quiet**[=*false*]]
|
[**-q**|**--quiet**[=*false*]]
|
||||||
[**--rm**[=*true*]]
|
[**--rm**[=*true*]]
|
||||||
[**-t**|**--tag**[=*TAG*]]
|
[**-t**|**--tag**[=*TAG*]]
|
||||||
|
[**-m**|**--memory**[=*MEMORY*]]
|
||||||
|
[**--memory-swap**[=*MEMORY-SWAP*]]
|
||||||
|
[**-c**|**--cpu-shares**[=*0*]]
|
||||||
|
[**--cpuset-cpus**[=*CPUSET-CPUS*]]
|
||||||
|
|
||||||
PATH | URL | -
|
PATH | URL | -
|
||||||
|
|
||||||
# DESCRIPTION
|
# DESCRIPTION
|
||||||
|
|
|
@ -31,7 +31,7 @@ docker-run - Run a command in a new container
|
||||||
[**--lxc-conf**[=*[]*]]
|
[**--lxc-conf**[=*[]*]]
|
||||||
[**--log-driver**[=*[]*]]
|
[**--log-driver**[=*[]*]]
|
||||||
[**-m**|**--memory**[=*MEMORY*]]
|
[**-m**|**--memory**[=*MEMORY*]]
|
||||||
[**--memory-swap**[=*MEMORY-SWAP]]
|
[**--memory-swap**[=*MEMORY-SWAP*]]
|
||||||
[**--mac-address**[=*MAC-ADDRESS*]]
|
[**--mac-address**[=*MAC-ADDRESS*]]
|
||||||
[**--name**[=*NAME*]]
|
[**--name**[=*NAME*]]
|
||||||
[**--net**[=*"bridge"*]]
|
[**--net**[=*"bridge"*]]
|
||||||
|
|
|
@ -60,13 +60,18 @@ You can set ulimit settings to be used within the container.
|
||||||
`GET /info`
|
`GET /info`
|
||||||
|
|
||||||
**New!**
|
**New!**
|
||||||
This endpoint now returns `SystemTime`, `HttpProxy`,`HttpsProxy` and `NoProxy`.
|
This endpoint now returns `SystemTime`, `HttpProxy`,`HttpsProxy` and `NoProxy`.
|
||||||
|
|
||||||
`GET /images/json`
|
`GET /images/json`
|
||||||
|
|
||||||
**New!**
|
**New!**
|
||||||
Added a `RepoDigests` field to include image digest information.
|
Added a `RepoDigests` field to include image digest information.
|
||||||
|
|
||||||
|
`POST /build`
|
||||||
|
|
||||||
|
**New!**
|
||||||
|
Builds can now set resource constraints for all containers created for the build.
|
||||||
|
|
||||||
## v1.17
|
## v1.17
|
||||||
|
|
||||||
### Full Documentation
|
### Full Documentation
|
||||||
|
|
|
@ -1156,6 +1156,10 @@ Query Parameters:
|
||||||
- **pull** - attempt to pull the image even if an older image exists locally
|
- **pull** - attempt to pull the image even if an older image exists locally
|
||||||
- **rm** - remove intermediate containers after a successful build (default behavior)
|
- **rm** - remove intermediate containers after a successful build (default behavior)
|
||||||
- **forcerm** - always remove intermediate containers (includes rm)
|
- **forcerm** - always remove intermediate containers (includes rm)
|
||||||
|
- **memory** - set memory limit for build
|
||||||
|
- **memswap** - Total memory (memory + swap), `-1` to disable swap
|
||||||
|
- **cpushares** - CPU shares (relative weight)
|
||||||
|
- **cpusetcpus** - CPUs in which to allow exection, e.g., `0-3`, `0,1`
|
||||||
|
|
||||||
Request Headers:
|
Request Headers:
|
||||||
|
|
||||||
|
|
|
@ -515,6 +515,10 @@ is returned by the `docker attach` command to its caller too:
|
||||||
-q, --quiet=false Suppress the verbose output generated by the containers
|
-q, --quiet=false Suppress the verbose output generated by the containers
|
||||||
--rm=true Remove intermediate containers after a successful build
|
--rm=true Remove intermediate containers after a successful build
|
||||||
-t, --tag="" Repository name (and optionally a tag) for the image
|
-t, --tag="" Repository name (and optionally a tag) for the image
|
||||||
|
-m, --memory="" Memory limit for all build containers
|
||||||
|
--memory-swap="" Total memory (memory + swap), `-1` to disable swap
|
||||||
|
-c, --cpu-shares CPU Shares (relative weight)
|
||||||
|
--cpuset-cpus="" CPUs in which to allow exection, e.g. `0-3`, `0,1`
|
||||||
|
|
||||||
Builds Docker images from a Dockerfile and a "context". A build's context is
|
Builds Docker images from a Dockerfile and a "context". A build's context is
|
||||||
the files located in the specified `PATH` or `URL`. The build process can
|
the files located in the specified `PATH` or `URL`. The build process can
|
||||||
|
|
|
@ -4455,7 +4455,7 @@ func TestBuildExoticShellInterpolation(t *testing.T) {
|
||||||
|
|
||||||
_, err := buildImage(name, `
|
_, err := buildImage(name, `
|
||||||
FROM busybox
|
FROM busybox
|
||||||
|
|
||||||
ENV SOME_VAR a.b.c
|
ENV SOME_VAR a.b.c
|
||||||
|
|
||||||
RUN [ "$SOME_VAR" = 'a.b.c' ]
|
RUN [ "$SOME_VAR" = 'a.b.c' ]
|
||||||
|
@ -5276,3 +5276,74 @@ RUN [ "/hello" ]`, map[string]string{})
|
||||||
|
|
||||||
logDone("build - RUN with one JSON arg")
|
logDone("build - RUN with one JSON arg")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBuildResourceConstraintsAreUsed(t *testing.T) {
|
||||||
|
name := "testbuildresourceconstraints"
|
||||||
|
defer deleteAllContainers()
|
||||||
|
defer deleteImages(name)
|
||||||
|
|
||||||
|
ctx, err := fakeContext(`
|
||||||
|
FROM hello-world:frozen
|
||||||
|
RUN ["/hello"]
|
||||||
|
`, map[string]string{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command(dockerBinary, "build", "--rm=false", "--memory=64m", "--memory-swap=-1", "--cpuset-cpus=1", "--cpu-shares=100", "-t", name, ".")
|
||||||
|
cmd.Dir = ctx.Dir
|
||||||
|
|
||||||
|
out, _, err := runCommandWithOutput(cmd)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err, out)
|
||||||
|
}
|
||||||
|
out, _, err = dockerCmd(t, "ps", "-lq")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
cID := stripTrailingCharacters(out)
|
||||||
|
|
||||||
|
type hostConfig struct {
|
||||||
|
Memory float64 // Use float64 here since the json decoder sees it that way
|
||||||
|
MemorySwap int
|
||||||
|
CpusetCpus string
|
||||||
|
CpuShares int
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg, err := inspectFieldJSON(cID, "HostConfig")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var c1 hostConfig
|
||||||
|
if err := json.Unmarshal([]byte(cfg), &c1); err != nil {
|
||||||
|
t.Fatal(err, cfg)
|
||||||
|
}
|
||||||
|
mem := int64(c1.Memory)
|
||||||
|
if mem != 67108864 || c1.MemorySwap != -1 || c1.CpusetCpus != "1" || c1.CpuShares != 100 {
|
||||||
|
t.Fatalf("resource constraints not set properly:\nMemory: %d, MemSwap: %d, CpusetCpus: %s, CpuShares: %d",
|
||||||
|
mem, c1.MemorySwap, c1.CpusetCpus, c1.CpuShares)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure constraints aren't saved to image
|
||||||
|
_, _, err = dockerCmd(t, "run", "--name=test", name)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
cfg, err = inspectFieldJSON("test", "HostConfig")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
var c2 hostConfig
|
||||||
|
if err := json.Unmarshal([]byte(cfg), &c2); err != nil {
|
||||||
|
t.Fatal(err, cfg)
|
||||||
|
}
|
||||||
|
mem = int64(c2.Memory)
|
||||||
|
if mem == 67108864 || c2.MemorySwap == -1 || c2.CpusetCpus == "1" || c2.CpuShares == 100 {
|
||||||
|
t.Fatalf("resource constraints leaked from build:\nMemory: %d, MemSwap: %d, CpusetCpus: %s, CpuShares: %d",
|
||||||
|
mem, c2.MemorySwap, c2.CpusetCpus, c2.CpuShares)
|
||||||
|
}
|
||||||
|
|
||||||
|
logDone("build - resource constraints applied")
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue