diff --git a/cmd/agent-sandbox-controller/main.go b/cmd/agent-sandbox-controller/main.go index 73c23f89b..a230955e2 100644 --- a/cmd/agent-sandbox-controller/main.go +++ b/cmd/agent-sandbox-controller/main.go @@ -36,6 +36,7 @@ import ( "sigs.k8s.io/agent-sandbox/controllers" extensionsv1alpha1 "sigs.k8s.io/agent-sandbox/extensions/api/v1alpha1" extensionscontrollers "sigs.k8s.io/agent-sandbox/extensions/controllers" + "sigs.k8s.io/agent-sandbox/internal/goruntime" asmetrics "sigs.k8s.io/agent-sandbox/internal/metrics" //+kubebuilder:scaffold:imports ) @@ -61,6 +62,7 @@ func main() { var sandboxClaimConcurrentWorkers int var sandboxWarmPoolConcurrentWorkers int var sandboxTemplateConcurrentWorkers int + var memlimitRatio float64 flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.") flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") flag.BoolVar(&enableLeaderElection, "leader-elect", true, @@ -86,13 +88,17 @@ func main() { flag.IntVar(&sandboxClaimConcurrentWorkers, "sandbox-claim-concurrent-workers", 1, "Max concurrent reconciles for the SandboxClaim controller") flag.IntVar(&sandboxWarmPoolConcurrentWorkers, "sandbox-warm-pool-concurrent-workers", 1, "Max concurrent reconciles for the SandboxWarmPool controller") flag.IntVar(&sandboxTemplateConcurrentWorkers, "sandbox-template-concurrent-workers", 1, "Max concurrent reconciles for the SandboxTemplate controller") + flag.Float64Var(&memlimitRatio, "auto-gomemlimit-ratio", 0.0, "The ratio of reserved GOMEMLIMIT memory to the detected maximum container or system memory. The value should be greater than 0.0 and less than 1.0. Default: 0.0 (disabled).") opts := zap.Options{ Development: false, } opts.BindFlags(flag.CommandLine) flag.Parse() - ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) + logr := zap.New(zap.UseFlagOptions(&opts)) + ctrl.SetLogger(logr) + + goruntime.SetMemLimit(logr, memlimitRatio) setupLog.Info("Concurrency settings", "sandbox", sandboxConcurrentWorkers, diff --git a/go.mod b/go.mod index 3c6ef5ea9..c8abbc13e 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module sigs.k8s.io/agent-sandbox go 1.26.1 require ( + github.com/KimMachineGun/automemlimit v0.7.5 github.com/go-logr/logr v1.4.3 github.com/google/go-cmp v0.7.0 github.com/prometheus/client_golang v1.23.2 @@ -58,6 +59,7 @@ require ( github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect + github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.67.1 // indirect diff --git a/go.sum b/go.sum index 7cc36e336..e5bd40355 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/KimMachineGun/automemlimit v0.7.5 h1:RkbaC0MwhjL1ZuBKunGDjE/ggwAX43DwZrJqVwyveTk= +github.com/KimMachineGun/automemlimit v0.7.5/go.mod h1:QZxpHaGOQoYvFhv/r4u3U0JTC2ZcOwbSr11UZF46UBM= github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= @@ -105,6 +107,8 @@ github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= diff --git a/internal/goruntime/memory.go b/internal/goruntime/memory.go new file mode 100644 index 000000000..e4a587f86 --- /dev/null +++ b/internal/goruntime/memory.go @@ -0,0 +1,49 @@ +// Copyright 2026 The Kubernetes Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package goruntime + +import ( + "runtime/debug" + + "github.com/KimMachineGun/automemlimit/memlimit" + "github.com/go-logr/logr" +) + +func SetMemLimit(logger logr.Logger, memlimitRatio float64) { + if memlimitRatio >= 1.0 { + memlimitRatio = 1.0 + } else if memlimitRatio <= 0.0 { + memlimitRatio = 0.0 + } + + // the memlimitRatio argument to 0, effectively disabling auto memory limit for all users. + if memlimitRatio == 0.0 { + return + } + + if _, err := memlimit.SetGoMemLimitWithOpts( + memlimit.WithRatio(memlimitRatio), + memlimit.WithProvider( + memlimit.ApplyFallback( + memlimit.FromCgroup, + memlimit.FromSystem, + ), + ), + ); err != nil { + logger.Error(err, "Failed to set GOMEMLIMIT automatically", "component", "automemlimit") + } + + logger.Info("Set GOMEMLIMIT", "value", debug.SetMemoryLimit(-1)) +}