diff --git a/api/client/build.go b/api/client/build.go index 6a2778f1a5..d2799ce900 100644 --- a/api/client/build.go +++ b/api/client/build.go @@ -19,6 +19,7 @@ import ( "github.com/docker/docker/api" "github.com/docker/docker/graph/tags" + "github.com/docker/docker/opts" "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/fileutils" "github.com/docker/docker/pkg/httputils" @@ -28,6 +29,7 @@ import ( "github.com/docker/docker/pkg/progressreader" "github.com/docker/docker/pkg/streamformatter" "github.com/docker/docker/pkg/symlink" + "github.com/docker/docker/pkg/ulimit" "github.com/docker/docker/pkg/units" "github.com/docker/docker/pkg/urlutil" "github.com/docker/docker/registry" @@ -60,6 +62,11 @@ func (cli *DockerCli) CmdBuild(args ...string) error { 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)") flCgroupParent := cmd.String([]string{"-cgroup-parent"}, "", "Optional parent cgroup for the container") + + ulimits := make(map[string]*ulimit.Ulimit) + flUlimits := opts.NewUlimitOpt(ulimits) + cmd.Var(flUlimits, []string{"-ulimit"}, "Ulimit options") + cmd.Require(flag.Exact, 1) cmd.ParseFlags(args, true) @@ -277,6 +284,13 @@ func (cli *DockerCli) CmdBuild(args ...string) error { v.Set("dockerfile", *dockerfileName) + ulimitsVar := flUlimits.GetList() + ulimitsJson, err := json.Marshal(ulimitsVar) + if err != nil { + return err + } + v.Set("ulimits", string(ulimitsJson)) + headers := http.Header(make(map[string][]string)) buf, err := json.Marshal(cli.configFile.AuthConfigs) if err != nil { diff --git a/api/server/server.go b/api/server/server.go index 5d30dae872..1ad1249a5b 100644 --- a/api/server/server.go +++ b/api/server/server.go @@ -34,6 +34,7 @@ import ( "github.com/docker/docker/pkg/sockets" "github.com/docker/docker/pkg/stdcopy" "github.com/docker/docker/pkg/streamformatter" + "github.com/docker/docker/pkg/ulimit" "github.com/docker/docker/pkg/version" "github.com/docker/docker/runconfig" "github.com/docker/docker/utils" @@ -1294,6 +1295,15 @@ func (s *Server) postBuild(version version.Version, w http.ResponseWriter, r *ht buildConfig.CPUSetMems = r.FormValue("cpusetmems") buildConfig.CgroupParent = r.FormValue("cgroupparent") + var buildUlimits = []*ulimit.Ulimit{} + ulimitsJson := r.FormValue("ulimits") + if ulimitsJson != "" { + if err := json.NewDecoder(strings.NewReader(ulimitsJson)).Decode(&buildUlimits); err != nil { + return err + } + buildConfig.Ulimits = buildUlimits + } + // Job cancellation. Note: not all job types support this. if closeNotifier, ok := w.(http.CloseNotifier); ok { finished := make(chan struct{}) diff --git a/builder/evaluator.go b/builder/evaluator.go index c46c7cd277..0a59a05d4e 100644 --- a/builder/evaluator.go +++ b/builder/evaluator.go @@ -37,6 +37,7 @@ import ( "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/pkg/symlink" "github.com/docker/docker/pkg/tarsum" + "github.com/docker/docker/pkg/ulimit" "github.com/docker/docker/runconfig" "github.com/docker/docker/utils" ) @@ -129,6 +130,7 @@ type builder struct { cgroupParent string memory int64 memorySwap int64 + ulimits []*ulimit.Ulimit cancelled <-chan struct{} // When closed, job was cancelled. diff --git a/builder/internals.go b/builder/internals.go index 4fc773c38d..a8e14160a9 100644 --- a/builder/internals.go +++ b/builder/internals.go @@ -615,6 +615,7 @@ func (b *builder) create() (*daemon.Container, error) { CgroupParent: b.cgroupParent, Memory: b.memory, MemorySwap: b.memorySwap, + Ulimits: b.ulimits, } config := *b.Config diff --git a/builder/job.go b/builder/job.go index 626a9bcae1..38b7ddcd6a 100644 --- a/builder/job.go +++ b/builder/job.go @@ -21,6 +21,7 @@ import ( "github.com/docker/docker/pkg/progressreader" "github.com/docker/docker/pkg/streamformatter" "github.com/docker/docker/pkg/stringid" + "github.com/docker/docker/pkg/ulimit" "github.com/docker/docker/pkg/urlutil" "github.com/docker/docker/registry" "github.com/docker/docker/runconfig" @@ -62,6 +63,7 @@ type Config struct { CPUSetCpus string CPUSetMems string CgroupParent string + Ulimits []*ulimit.Ulimit AuthConfigs map[string]cliconfig.AuthConfig Stdout io.Writer @@ -205,6 +207,7 @@ func Build(d *daemon.Daemon, buildConfig *Config) error { cgroupParent: buildConfig.CgroupParent, memory: buildConfig.Memory, memorySwap: buildConfig.MemorySwap, + ulimits: buildConfig.Ulimits, cancelled: buildConfig.WaitCancelled(), id: stringid.GenerateRandomID(), } diff --git a/contrib/completion/bash/docker b/contrib/completion/bash/docker index 1cc7a20c48..85959f4e77 100755 --- a/contrib/completion/bash/docker +++ b/contrib/completion/bash/docker @@ -395,7 +395,7 @@ _docker_build() { case "$cur" in -*) - COMPREPLY=( $( compgen -W "--cgroup-parent --cpuset-cpus --cpuset-mems --cpu-shares -c --cpu-period --cpu-quota --file -f --force-rm --help --memory -m --memory-swap --no-cache --pull --quiet -q --rm --tag -t" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "--cgroup-parent --cpuset-cpus --cpuset-mems --cpu-shares -c --cpu-period --cpu-quota --file -f --force-rm --help --memory -m --memory-swap --no-cache --pull --quiet -q --rm --tag -t --ulimit" -- "$cur" ) ) ;; *) local counter="$(__docker_pos_first_nonflag '--cgroup-parent|--cpuset-cpus|--cpuset-mems|--cpu-shares|-c|--cpu-period|--cpu-quota|--file|-f|--memory|-m|--memory-swap|--tag|-t')" diff --git a/docs/reference/commandline/build.md b/docs/reference/commandline/build.md index e6fefc1f20..037003d9be 100644 --- a/docs/reference/commandline/build.md +++ b/docs/reference/commandline/build.md @@ -28,6 +28,7 @@ weight=1 --cpuset-mems="" MEMs in which to allow execution, e.g. `0-3`, `0,1` --cpuset-cpus="" CPUs in which to allow execution, e.g. `0-3`, `0,1` --cgroup-parent="" Optional parent cgroup for the container + --ulimit=[] Ulimit options 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 refer @@ -245,5 +246,8 @@ the command line. When `docker build` is run with the `--cgroup-parent` option the containers used in the build will be run with the [corresponding `docker run` -flag](/reference/run/#specifying-custom-cgroups). +flag](/reference/run/#specifying-custom-cgroups). +Using the `--ulimit` option with `docker build` will cause each build step's +container to be started using those [`--ulimit` +flag values](/reference/run/#setting-ulimits-in-a-container). diff --git a/integration-cli/docker_cli_build_unix_test.go b/integration-cli/docker_cli_build_unix_test.go index b12d2c9bc4..fc7bd925a6 100644 --- a/integration-cli/docker_cli_build_unix_test.go +++ b/integration-cli/docker_cli_build_unix_test.go @@ -6,6 +6,7 @@ import ( "encoding/json" "strings" + "github.com/docker/docker/pkg/ulimit" "github.com/go-check/check" ) @@ -21,7 +22,7 @@ func (s *DockerSuite) TestBuildResourceConstraintsAreUsed(c *check.C) { c.Fatal(err) } - dockerCmdInDir(c, ctx.Dir, "build", "--no-cache", "--rm=false", "--memory=64m", "--memory-swap=-1", "--cpuset-cpus=0", "--cpuset-mems=0", "--cpu-shares=100", "--cpu-quota=8000", "-t", name, ".") + dockerCmdInDir(c, ctx.Dir, "build", "--no-cache", "--rm=false", "--memory=64m", "--memory-swap=-1", "--cpuset-cpus=0", "--cpuset-mems=0", "--cpu-shares=100", "--cpu-quota=8000", "--ulimit", "nofile=42", "-t", name, ".") out, _ := dockerCmd(c, "ps", "-lq") @@ -34,6 +35,7 @@ func (s *DockerSuite) TestBuildResourceConstraintsAreUsed(c *check.C) { CpusetMems string CPUShares int64 CPUQuota int64 + Ulimits []*ulimit.Ulimit } cfg, err := inspectFieldJSON(cID, "HostConfig") @@ -45,9 +47,9 @@ func (s *DockerSuite) TestBuildResourceConstraintsAreUsed(c *check.C) { if err := json.Unmarshal([]byte(cfg), &c1); err != nil { c.Fatal(err, cfg) } - if c1.Memory != 67108864 || c1.MemorySwap != -1 || c1.CpusetCpus != "0" || c1.CpusetMems != "0" || c1.CPUShares != 100 || c1.CPUQuota != 8000 { - c.Fatalf("resource constraints not set properly:\nMemory: %d, MemSwap: %d, CpusetCpus: %s, CpusetMems: %s, CPUShares: %d, CPUQuota: %d", - c1.Memory, c1.MemorySwap, c1.CpusetCpus, c1.CpusetMems, c1.CPUShares, c1.CPUQuota) + if c1.Memory != 67108864 || c1.MemorySwap != -1 || c1.CpusetCpus != "0" || c1.CpusetMems != "0" || c1.CPUShares != 100 || c1.CPUQuota != 8000 || c1.Ulimits[0].Name != "nofile" || c1.Ulimits[0].Hard != 42 { + c.Fatalf("resource constraints not set properly:\nMemory: %d, MemSwap: %d, CpusetCpus: %s, CpusetMems: %s, CPUShares: %d, CPUQuota: %d, Ulimits: %s", + c1.Memory, c1.MemorySwap, c1.CpusetCpus, c1.CpusetMems, c1.CPUShares, c1.CPUQuota, c1.Ulimits[0]) } // Make sure constraints aren't saved to image @@ -61,9 +63,10 @@ func (s *DockerSuite) TestBuildResourceConstraintsAreUsed(c *check.C) { if err := json.Unmarshal([]byte(cfg), &c2); err != nil { c.Fatal(err, cfg) } - if c2.Memory == 67108864 || c2.MemorySwap == -1 || c2.CpusetCpus == "0" || c2.CpusetMems == "0" || c2.CPUShares == 100 || c2.CPUQuota == 8000 { - c.Fatalf("resource constraints leaked from build:\nMemory: %d, MemSwap: %d, CpusetCpus: %s, CpusetMems: %s, CPUShares: %d, CPUQuota: %d", - c2.Memory, c2.MemorySwap, c2.CpusetCpus, c2.CpusetMems, c2.CPUShares, c2.CPUQuota) + if c2.Memory == 67108864 || c2.MemorySwap == -1 || c2.CpusetCpus == "0" || c2.CpusetMems == "0" || c2.CPUShares == 100 || c2.CPUQuota == 8000 || c2.Ulimits != nil { + c.Fatalf("resource constraints leaked from build:\nMemory: %d, MemSwap: %d, CpusetCpus: %s, CpusetMems: %s, CPUShares: %d, CPUQuota: %d, Ulimits: %s", + c2.Memory, c2.MemorySwap, c2.CpusetCpus, c2.CpusetMems, c2.CPUShares, c2.CPUQuota, c2.Ulimits) + } } diff --git a/man/docker-build.1.md b/man/docker-build.1.md index 2d9e694d43..a8714b775a 100644 --- a/man/docker-build.1.md +++ b/man/docker-build.1.md @@ -22,6 +22,7 @@ docker-build - Build a new image from the source code at PATH [**--cpuset-cpus**[=*CPUSET-CPUS*]] [**--cpuset-mems**[=*CPUSET-MEMS*]] [**--cgroup-parent**[=*CGROUP-PARENT*]] +[**--ulimit**[=*[]*]] PATH | URL | - @@ -142,6 +143,12 @@ two memory nodes. If the path is not absolute, the path is considered relative to the `cgroups` path of the init process. Cgroups are created if they do not already exist. +**--ulimit**=[] + Ulimit options + + For more information about `ulimit` see [Setting ulimits in a +container](https://docs.docker.com/reference/commandline/run/#setting-ulimits-in-a-container) + # EXAMPLES ## Building an image using a Dockerfile located inside the current directory