@@ -11,6 +11,7 @@ import (
1111
1212 // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
1313 // to ensure that exec-entrypoint and run can make use of them.
14+ "github.com/KimMachineGun/automemlimit/memlimit"
1415 "golang.org/x/sync/errgroup"
1516 _ "k8s.io/client-go/plugin/pkg/client/auth"
1617
@@ -21,6 +22,7 @@ import (
2122 "k8s.io/client-go/rest"
2223 "k8s.io/client-go/tools/clientcmd"
2324 ctrl "sigs.k8s.io/controller-runtime"
25+ "sigs.k8s.io/controller-runtime/pkg/cache"
2426 "sigs.k8s.io/controller-runtime/pkg/client"
2527 "sigs.k8s.io/controller-runtime/pkg/cluster"
2628 "sigs.k8s.io/controller-runtime/pkg/healthz"
@@ -136,6 +138,21 @@ func main() {
136138
137139 ctrl .SetLogger (zap .New (zap .UseFlagOptions (& opts )))
138140
141+ // Set GOMEMLIMIT from the container's cgroup memory limit so the Go GC
142+ // backpressures before the kernel OOMKills the process. Without this the
143+ // runtime is blind to the cgroup limit and lets the heap overshoot during
144+ // allocation-heavy startup (concurrent informer/cache sync across project
145+ // control planes), which is what OOMKilled compute-manager on rollout (#161).
146+ //
147+ // This is a no-op when GOMEMLIMIT is already set in the environment or
148+ // AUTOMEMLIMIT=off, so an explicit manifest override always wins. Off-cgroup
149+ // (e.g. local dev on macOS) the provider errors; we log and continue.
150+ if limit , err := memlimit .SetGoMemLimitWithOpts (memlimit .WithRatio (0.9 )); err != nil {
151+ setupLog .Info ("GOMEMLIMIT not set from cgroup; using Go default" , "reason" , err .Error ())
152+ } else if limit > 0 {
153+ setupLog .Info ("GOMEMLIMIT set from cgroup" , "bytes" , limit )
154+ }
155+
139156 if federationKubeconfig != "" {
140157 loader := clientcmd .NewNonInteractiveDeferredLoadingClientConfig (
141158 & clientcmd.ClientConfigLoadingRules {ExplicitPath : federationKubeconfig },
@@ -193,6 +210,11 @@ func main() {
193210
194211 deploymentCluster , err := cluster .New (cfg , func (o * cluster.Options ) {
195212 o .Scheme = scheme
213+ // Strip managedFields from every cached object. They are pure server-side
214+ // apply bookkeeping the controllers never read, and on large/unstructured
215+ // objects they dominate the per-object footprint. Dropping them shrinks the
216+ // startup cache-sync spike that OOMKilled compute-manager (#161).
217+ o .Cache .DefaultTransform = cache .TransformStripManagedFields ()
196218 })
197219 if err != nil {
198220 setupLog .Error (err , "failed creating local cluster" )
@@ -230,6 +252,7 @@ func main() {
230252
231253 mgr , err := mcmanager .New (cfg , provider , ctrl.Options {
232254 Scheme : scheme ,
255+ Cache : cache.Options {DefaultTransform : cache .TransformStripManagedFields ()},
233256 Metrics : metricsServerOptions ,
234257 WebhookServer : webhookServer ,
235258 HealthProbeBindAddress : probeAddr ,
@@ -405,6 +428,12 @@ func initializeClusterDiscovery(
405428
406429 discoveryManager , err := manager .New (discoveryRestConfig , manager.Options {
407430 Metrics : metricsserver.Options {BindAddress : "0" },
431+ Cache : cache.Options {
432+ // Unstructured objects carry the full managedFields tree in their
433+ // content map, so stripping it here is especially impactful for the
434+ // discovery cache (#161).
435+ DefaultTransform : cache .TransformStripManagedFields (),
436+ },
408437 Client : client.Options {
409438 Cache : & client.CacheOptions {
410439 Unstructured : true ,
@@ -419,6 +448,11 @@ func initializeClusterDiscovery(
419448 ClusterOptions : []cluster.Option {
420449 func (o * cluster.Options ) {
421450 o .Scheme = scheme
451+ // Applied to every per-project cluster cache. These are the
452+ // caches that fan in concurrently at startup, so stripping
453+ // managedFields here is the largest single lever on the
454+ // startup memory spike (#161).
455+ o .Cache .DefaultTransform = cache .TransformStripManagedFields ()
422456 },
423457 },
424458 InternalServiceDiscovery : serverConfig .Discovery .InternalServiceDiscovery ,
@@ -486,6 +520,7 @@ func setupManagementControllers(mgr mcmanager.Manager, federationClient client.C
486520 // directly anywhere a watchable federation cluster source is required.
487521 federationMgr , err := manager .New (federationRestConfig , manager.Options {
488522 Scheme : scheme ,
523+ Cache : cache.Options {DefaultTransform : cache .TransformStripManagedFields ()},
489524 Metrics : metricsserver.Options {BindAddress : "0" },
490525 })
491526 if err != nil {
0 commit comments