build: Add per-step CPU and memory resource limits
Port of moby/buildkit#6569 to buildx. Adds --memory, --memory-swap, --cpu-shares, --cpu-period, --cpu-quota, --cpuset-cpus, and --cpuset-mems flags to build, plus the equivalent bake target attributes and compose x-bake fields. Signed-off-by: Jiří Moravčík <jiri.moravcik@gmail.com>
This commit is contained in:
+51
-22
@@ -89,6 +89,11 @@ type buildOptions struct {
|
||||
target string
|
||||
ulimits *dockeropts.UlimitOpt
|
||||
|
||||
resources []string
|
||||
// legacyResources is kept separate from resources because the pflag
|
||||
// StringArray for --resource resets its backing slice on first use.
|
||||
legacyResources []string
|
||||
|
||||
attests []string
|
||||
sbom string
|
||||
provenance string
|
||||
@@ -133,6 +138,7 @@ func (o *buildOptions) toOptions() (*BuildOptions, error) {
|
||||
Tags: o.tags,
|
||||
Target: o.target,
|
||||
Ulimits: o.ulimits,
|
||||
Resources: append(o.legacyResources, o.resources...),
|
||||
Builder: o.builder,
|
||||
NoCache: o.noCache,
|
||||
Pull: o.pull,
|
||||
@@ -234,6 +240,20 @@ func (o *buildOptions) toDisplayMode() (progressui.DisplayMode, error) {
|
||||
return progress, nil
|
||||
}
|
||||
|
||||
// legacyResourceValue is a pflag.Value that appends a "<key>=value" entry to
|
||||
// the --resource list, wiring the hidden legacy flags into the same code path.
|
||||
type legacyResourceValue struct {
|
||||
key string
|
||||
resources *[]string
|
||||
}
|
||||
|
||||
func (v *legacyResourceValue) String() string { return "" }
|
||||
func (v *legacyResourceValue) Type() string { return "string" }
|
||||
func (v *legacyResourceValue) Set(s string) error {
|
||||
*v.resources = append(*v.resources, v.key+"="+s)
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
commandNameAttribute = attribute.Key("command.name")
|
||||
commandOptionsHash = attribute.Key("command.options.hash")
|
||||
@@ -583,6 +603,8 @@ func buildCmd(dockerCli command.Cli, rootOpts *rootOptions, debugger debuggerOpt
|
||||
options.ulimits = dockeropts.NewUlimitOpt(nil)
|
||||
flags.Var(options.ulimits, "ulimit", "Ulimit options")
|
||||
|
||||
flags.StringArrayVar(&options.resources, "resource", []string{}, `Resource limits for build containers (format: "memory=2g", "cpu-quota=50000")`)
|
||||
|
||||
flags.StringArrayVar(&options.attests, "attest", []string{}, `Attestation parameters (format: "type=sbom,generator=image")`)
|
||||
flags.StringVar(&options.sbom, "sbom", "", `Shorthand for "--attest=type=sbom"`)
|
||||
flags.StringVar(&options.provenance, "provenance", "", `Shorthand for "--attest=type=provenance"`)
|
||||
@@ -595,12 +617,33 @@ func buildCmd(dockerCli command.Cli, rootOpts *rootOptions, debugger debuggerOpt
|
||||
var ignore string
|
||||
var ignoreSlice []string
|
||||
var ignoreBool bool
|
||||
var ignoreInt int64
|
||||
|
||||
flags.StringVar(&options.callFunc, "print", "", "Print result of information request (e.g., outline, targets)")
|
||||
cobrautil.MarkFlagsExperimental(flags, "print")
|
||||
flags.MarkHidden("print")
|
||||
|
||||
// Legacy per-resource flags, hidden and superseded by --resource.
|
||||
flags.VarP(&legacyResourceValue{key: "memory", resources: &options.legacyResources}, "memory", "m", "Memory limit")
|
||||
flags.MarkHidden("memory")
|
||||
|
||||
flags.VarP(&legacyResourceValue{key: "memory-swap", resources: &options.legacyResources}, "memory-swap", "", `Swap limit equal to memory plus swap: "-1" to enable unlimited swap`)
|
||||
flags.MarkHidden("memory-swap")
|
||||
|
||||
flags.VarP(&legacyResourceValue{key: "cpu-shares", resources: &options.legacyResources}, "cpu-shares", "c", "CPU shares (relative weight)")
|
||||
flags.MarkHidden("cpu-shares")
|
||||
|
||||
flags.VarP(&legacyResourceValue{key: "cpu-period", resources: &options.legacyResources}, "cpu-period", "", "Limit the CPU CFS (Completely Fair Scheduler) period")
|
||||
flags.MarkHidden("cpu-period")
|
||||
|
||||
flags.VarP(&legacyResourceValue{key: "cpu-quota", resources: &options.legacyResources}, "cpu-quota", "", "Limit the CPU CFS (Completely Fair Scheduler) quota")
|
||||
flags.MarkHidden("cpu-quota")
|
||||
|
||||
flags.VarP(&legacyResourceValue{key: "cpuset-cpus", resources: &options.legacyResources}, "cpuset-cpus", "", `CPUs in which to allow execution ("0-3", "0,1")`)
|
||||
flags.MarkHidden("cpuset-cpus")
|
||||
|
||||
flags.VarP(&legacyResourceValue{key: "cpuset-mems", resources: &options.legacyResources}, "cpuset-mems", "", `MEMs in which to allow execution ("0-3", "0,1")`)
|
||||
flags.MarkHidden("cpuset-mems")
|
||||
|
||||
flags.BoolVar(&ignoreBool, "compress", false, "Compress the build context using gzip")
|
||||
flags.MarkHidden("compress")
|
||||
|
||||
@@ -617,27 +660,6 @@ func buildCmd(dockerCli command.Cli, rootOpts *rootOptions, debugger debuggerOpt
|
||||
flags.SetAnnotation("squash", "flag-warn", []string{"experimental flag squash is removed with BuildKit. You should squash inside build using a multi-stage Dockerfile for efficiency."})
|
||||
cobrautil.MarkFlagsExperimental(flags, "squash")
|
||||
|
||||
flags.StringVarP(&ignore, "memory", "m", "", "Memory limit")
|
||||
flags.MarkHidden("memory")
|
||||
|
||||
flags.StringVar(&ignore, "memory-swap", "", `Swap limit equal to memory plus swap: "-1" to enable unlimited swap`)
|
||||
flags.MarkHidden("memory-swap")
|
||||
|
||||
flags.Int64VarP(&ignoreInt, "cpu-shares", "c", 0, "CPU shares (relative weight)")
|
||||
flags.MarkHidden("cpu-shares")
|
||||
|
||||
flags.Int64Var(&ignoreInt, "cpu-period", 0, "Limit the CPU CFS (Completely Fair Scheduler) period")
|
||||
flags.MarkHidden("cpu-period")
|
||||
|
||||
flags.Int64Var(&ignoreInt, "cpu-quota", 0, "Limit the CPU CFS (Completely Fair Scheduler) quota")
|
||||
flags.MarkHidden("cpu-quota")
|
||||
|
||||
flags.StringVar(&ignore, "cpuset-cpus", "", `CPUs in which to allow execution ("0-3", "0,1")`)
|
||||
flags.MarkHidden("cpuset-cpus")
|
||||
|
||||
flags.StringVar(&ignore, "cpuset-mems", "", `MEMs in which to allow execution ("0-3", "0,1")`)
|
||||
flags.MarkHidden("cpuset-mems")
|
||||
|
||||
flags.BoolVar(&ignoreBool, "rm", true, "Remove intermediate containers after a successful build")
|
||||
flags.MarkHidden("rm")
|
||||
|
||||
@@ -990,6 +1012,7 @@ type BuildOptions struct {
|
||||
Tags []string
|
||||
Target string
|
||||
Ulimits *dockeropts.UlimitOpt
|
||||
Resources []string
|
||||
Builder string
|
||||
NoCache bool
|
||||
Pull bool
|
||||
@@ -1014,6 +1037,11 @@ func RunBuild(ctx context.Context, dockerCli command.Cli, in *BuildOptions, inSt
|
||||
contexts[name] = build.NamedContext{Path: path}
|
||||
}
|
||||
|
||||
resourceLimits, err := build.ParseResourceLimits(in.Resources)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
opts := build.Options{
|
||||
Inputs: build.Inputs{
|
||||
ContextPath: in.ContextPath,
|
||||
@@ -1034,6 +1062,7 @@ func RunBuild(ctx context.Context, dockerCli command.Cli, in *BuildOptions, inSt
|
||||
Tags: in.Tags,
|
||||
Target: in.Target,
|
||||
Ulimits: in.Ulimits,
|
||||
ResourceLimits: resourceLimits,
|
||||
GroupRef: in.GroupRef,
|
||||
ProvenanceResponseMode: confutil.ParseMetadataProvenance(in.ProvenanceResponseMode),
|
||||
}
|
||||
|
||||
@@ -110,6 +110,13 @@ type configOutput struct {
|
||||
|
||||
ShmSize string `json:",omitempty"`
|
||||
Ulimit string `json:",omitempty"`
|
||||
Memory string `json:",omitempty"`
|
||||
MemorySwap string `json:",omitempty"`
|
||||
CPUShares string `json:",omitempty"`
|
||||
CPUPeriod string `json:",omitempty"`
|
||||
CPUQuota string `json:",omitempty"`
|
||||
CPUSetCPUs string `json:",omitempty"`
|
||||
CPUSetMems string `json:",omitempty"`
|
||||
CacheMountNS string `json:",omitempty"`
|
||||
DockerfileCheckConfig string `json:",omitempty"`
|
||||
SourceDateEpoch string `json:",omitempty"`
|
||||
@@ -391,6 +398,13 @@ workers0:
|
||||
|
||||
readAttr(attrs, "shm-size", &out.Config.ShmSize, nil)
|
||||
readAttr(attrs, "ulimit", &out.Config.Ulimit, nil)
|
||||
readAttr(attrs, "memory", &out.Config.Memory, nil)
|
||||
readAttr(attrs, "memswap", &out.Config.MemorySwap, nil)
|
||||
readAttr(attrs, "cpushares", &out.Config.CPUShares, nil)
|
||||
readAttr(attrs, "cpuperiod", &out.Config.CPUPeriod, nil)
|
||||
readAttr(attrs, "cpuquota", &out.Config.CPUQuota, nil)
|
||||
readAttr(attrs, "cpusetcpus", &out.Config.CPUSetCPUs, nil)
|
||||
readAttr(attrs, "cpusetmems", &out.Config.CPUSetMems, nil)
|
||||
readAttr(attrs, "build-arg:BUILDKIT_CACHE_MOUNT_NS", &out.Config.CacheMountNS, nil)
|
||||
readAttr(attrs, "build-arg:BUILDKIT_DOCKERFILE_CHECK", &out.Config.DockerfileCheckConfig, nil)
|
||||
readAttr(attrs, "build-arg:"+epoch.SourceDateEpochEnv, &out.Config.SourceDateEpoch, nil)
|
||||
@@ -570,6 +584,27 @@ workers0:
|
||||
if out.Config.Ulimit != "" {
|
||||
fmt.Fprintf(tw, "Resource Limits:\t%s\n", out.Config.Ulimit)
|
||||
}
|
||||
if out.Config.Memory != "" {
|
||||
fmt.Fprintf(tw, "Memory:\t%s\n", out.Config.Memory)
|
||||
}
|
||||
if out.Config.MemorySwap != "" {
|
||||
fmt.Fprintf(tw, "Memory Swap:\t%s\n", out.Config.MemorySwap)
|
||||
}
|
||||
if out.Config.CPUShares != "" {
|
||||
fmt.Fprintf(tw, "CPU Shares:\t%s\n", out.Config.CPUShares)
|
||||
}
|
||||
if out.Config.CPUPeriod != "" {
|
||||
fmt.Fprintf(tw, "CPU Period:\t%s\n", out.Config.CPUPeriod)
|
||||
}
|
||||
if out.Config.CPUQuota != "" {
|
||||
fmt.Fprintf(tw, "CPU Quota:\t%s\n", out.Config.CPUQuota)
|
||||
}
|
||||
if out.Config.CPUSetCPUs != "" {
|
||||
fmt.Fprintf(tw, "CPUSet CPUs:\t%s\n", out.Config.CPUSetCPUs)
|
||||
}
|
||||
if out.Config.CPUSetMems != "" {
|
||||
fmt.Fprintf(tw, "CPUSet Mems:\t%s\n", out.Config.CPUSetMems)
|
||||
}
|
||||
if out.Config.CacheMountNS != "" {
|
||||
fmt.Fprintf(tw, "Cache Mount Namespace:\t%s\n", out.Config.CacheMountNS)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user