From e17e8f2a7fb61a4de920fba561d5394f993a3fd8 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 13 Nov 2023 15:12:09 -0600 Subject: [PATCH] factory: strip Managed Fields to reduce memory usage We don't care about them, so why cache them and use a ton of memory. Inspired by https://github.com/kubernetes/kubernetes/pull/118455 Signed-off-by: Dan Williams --- go-controller/pkg/factory/factory.go | 30 ++++++++++++++++++++--- go-controller/pkg/factory/factory_test.go | 7 +++--- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/go-controller/pkg/factory/factory.go b/go-controller/pkg/factory/factory.go index 13c68a0ea17..b5693d0526b 100644 --- a/go-controller/pkg/factory/factory.go +++ b/go-controller/pkg/factory/factory.go @@ -57,6 +57,7 @@ import ( kapi "k8s.io/api/core/v1" discovery "k8s.io/api/discovery/v1" knet "k8s.io/api/networking/v1" + "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/labels" @@ -191,6 +192,29 @@ func NewMasterWatchFactory(ovnClientset *util.OVNMasterClientset) (*WatchFactory return wf, nil } +// Informer transform to trim object fields for memory efficiency. +func informerObjectTrim(obj interface{}) (interface{}, error) { + if accessor, err := meta.Accessor(obj); err == nil { + accessor.SetManagedFields(nil) + } + if pod, ok := obj.(*kapi.Pod); ok { + pod.Spec.Volumes = []kapi.Volume{} + for i := range pod.Spec.Containers { + pod.Spec.Containers[i].Command = nil + pod.Spec.Containers[i].Args = nil + pod.Spec.Containers[i].Env = nil + pod.Spec.Containers[i].VolumeMounts = nil + } + } else if node, ok := obj.(*kapi.Node); ok { + node.Status.Images = nil + node.Status.VolumesInUse = nil + node.Status.VolumesAttached = nil + node.Status.Capacity = nil + node.Status.Allocatable = nil + } + return obj, nil +} + // NewOVNKubeControllerWatchFactory initializes a new watch factory for the ovnkube controller process func NewOVNKubeControllerWatchFactory(ovnClientset *util.OVNKubeControllerClientset) (*WatchFactory, error) { // resync time is 12 hours, none of the resources being watched in ovn-kubernetes have @@ -200,7 +224,7 @@ func NewOVNKubeControllerWatchFactory(ovnClientset *util.OVNKubeControllerClient // the downside of making it tight (like 10 minutes) is needless spinning on all resources // However, AddEventHandlerWithResyncPeriod can specify a per handler resync period wf := &WatchFactory{ - iFactory: informerfactory.NewSharedInformerFactory(ovnClientset.KubeClient, resyncInterval), + iFactory: informerfactory.NewSharedInformerFactoryWithOptions(ovnClientset.KubeClient, resyncInterval, informerfactory.WithTransform(informerObjectTrim)), anpFactory: anpinformerfactory.NewSharedInformerFactory(ovnClientset.ANPClient, resyncInterval), eipFactory: egressipinformerfactory.NewSharedInformerFactory(ovnClientset.EgressIPClient, resyncInterval), efFactory: egressfirewallinformerfactory.NewSharedInformerFactory(ovnClientset.EgressFirewallClient, resyncInterval), @@ -427,7 +451,7 @@ func (wf *WatchFactory) Start() error { // of the localPodSelector or figure out how to deal with selecting all pods everywhere. func NewNodeWatchFactory(ovnClientset *util.OVNNodeClientset, nodeName string) (*WatchFactory, error) { wf := &WatchFactory{ - iFactory: informerfactory.NewSharedInformerFactory(ovnClientset.KubeClient, resyncInterval), + iFactory: informerfactory.NewSharedInformerFactoryWithOptions(ovnClientset.KubeClient, resyncInterval, informerfactory.WithTransform(informerObjectTrim)), egressServiceFactory: egressserviceinformerfactory.NewSharedInformerFactory(ovnClientset.EgressServiceClient, resyncInterval), eipFactory: egressipinformerfactory.NewSharedInformerFactory(ovnClientset.EgressIPClient, resyncInterval), apbRouteFactory: adminbasedpolicyinformerfactory.NewSharedInformerFactory(ovnClientset.AdminPolicyRouteClient, resyncInterval), @@ -545,7 +569,7 @@ func NewNodeWatchFactory(ovnClientset *util.OVNNodeClientset, nodeName string) ( // mode process. func NewClusterManagerWatchFactory(ovnClientset *util.OVNClusterManagerClientset) (*WatchFactory, error) { wf := &WatchFactory{ - iFactory: informerfactory.NewSharedInformerFactory(ovnClientset.KubeClient, resyncInterval), + iFactory: informerfactory.NewSharedInformerFactoryWithOptions(ovnClientset.KubeClient, resyncInterval, informerfactory.WithTransform(informerObjectTrim)), eipFactory: egressipinformerfactory.NewSharedInformerFactory(ovnClientset.EgressIPClient, resyncInterval), cpipcFactory: ocpcloudnetworkinformerfactory.NewSharedInformerFactory(ovnClientset.CloudNetworkClient, resyncInterval), egressServiceFactory: egressserviceinformerfactory.NewSharedInformerFactoryWithOptions(ovnClientset.EgressServiceClient, resyncInterval), diff --git a/go-controller/pkg/factory/factory_test.go b/go-controller/pkg/factory/factory_test.go index cf97ec32c3b..d00a0de6182 100644 --- a/go-controller/pkg/factory/factory_test.go +++ b/go-controller/pkg/factory/factory_test.go @@ -2085,6 +2085,8 @@ var _ = Describe("Watch Factory Operations", func() { }) pods = append(pods, pod) + podCopy := pod.DeepCopy() + podCopy2 := pod.DeepCopy() // Pod doesn't pass filter; shouldn't be added podWatch.Add(pod) @@ -2093,7 +2095,6 @@ var _ = Describe("Watch Factory Operations", func() { // Update pod to pass filter; should be treated as add. Need // to deep-copy pod when modifying because it's a pointer all // the way through when using FakeClient - podCopy := pod.DeepCopy() podCopy.ObjectMeta.Labels["blah"] = "foobar" pods = []*v1.Pod{podCopy} equalPod = podCopy @@ -2101,8 +2102,8 @@ var _ = Describe("Watch Factory Operations", func() { Eventually(c.getAdded, 2).Should(Equal(1)) // Update pod to fail filter; should be treated as delete - pod.ObjectMeta.Labels["blah"] = "baz" - podWatch.Modify(pod) + podCopy2.ObjectMeta.Labels["blah"] = "baz" + podWatch.Modify(podCopy2) Eventually(c.getDeleted, 2).Should(Equal(1)) Consistently(c.getAdded, 2).Should(Equal(1)) Consistently(c.getUpdated, 2).Should(Equal(0))