diff --git a/cmd/machine-config-daemon/start.go b/cmd/machine-config-daemon/start.go index 74213c246d..1e51ce529c 100644 --- a/cmd/machine-config-daemon/start.go +++ b/cmd/machine-config-daemon/start.go @@ -153,16 +153,18 @@ func runStartCmd(_ *cobra.Command, _ []string) { if startOpts.hypershiftDesiredConfigMap != "" { // This is a hypershift-mode daemon ctx := ctrlcommon.CreateControllerContext(ctx, cb) + nodeScopedInformer, nodeScopedInformerStartFunc := ctrlcommon.NewScopedNodeInformerFromClientBuilder(cb, startOpts.nodeName) err := dn.HypershiftConnect( startOpts.nodeName, kubeClient, - ctx.KubeInformerFactory.Core().V1().Nodes(), + nodeScopedInformer, startOpts.hypershiftDesiredConfigMap, ) if err != nil { ctrlcommon.WriteTerminationError(err) } + nodeScopedInformerStartFunc(stopCh) ctx.KubeInformerFactory.Start(stopCh) close(ctx.InformersStarted) @@ -177,6 +179,8 @@ func runStartCmd(_ *cobra.Command, _ []string) { ctrlctx := ctrlcommon.CreateControllerContext(ctx, cb) + nodeScopedInformer, nodeScopedInformerStartFunc := ctrlcommon.NewScopedNodeInformerFromClientBuilder(cb, startOpts.nodeName) + // create the daemon instance. this also initializes kube client items // which need to come from the container and not the chroot. err = dn.ClusterConnect( @@ -184,7 +188,7 @@ func runStartCmd(_ *cobra.Command, _ []string) { kubeClient, ctrlctx.ClientBuilder.MachineConfigClientOrDie(componentName), ctrlctx.InformerFactory.Machineconfiguration().V1().MachineConfigs(), - ctrlctx.KubeInformerFactory.Core().V1().Nodes(), + nodeScopedInformer, ctrlctx.InformerFactory.Machineconfiguration().V1().ControllerConfigs(), ctrlctx.InformerFactory.Machineconfiguration().V1().MachineConfigPools(), ctrlctx.ClientBuilder.OperatorClientOrDie(componentName), @@ -202,6 +206,7 @@ func runStartCmd(_ *cobra.Command, _ []string) { ctrlctx.KubeNamespacedInformerFactory.Start(stopCh) ctrlctx.InformerFactory.Start(stopCh) ctrlctx.OperatorInformerFactory.Start(stopCh) + nodeScopedInformerStartFunc(ctrlctx.Stop) close(ctrlctx.InformersStarted) select { @@ -226,7 +231,7 @@ func runStartCmd(_ *cobra.Command, _ []string) { criClient, ctrlctx.ClientBuilder.MachineConfigClientOrDie(componentName), ctrlctx.InformerFactory.Machineconfiguration().V1().PinnedImageSets(), - ctrlctx.KubeInformerFactory.Core().V1().Nodes(), + nodeScopedInformer, ctrlctx.InformerFactory.Machineconfiguration().V1().MachineConfigPools(), resource.MustParse(constants.MinFreeStorageAfterPrefetch), constants.DefaultCRIOSocketPath, diff --git a/pkg/controller/common/controller_context.go b/pkg/controller/common/controller_context.go index 030e1f2763..2cfd932b4d 100644 --- a/pkg/controller/common/controller_context.go +++ b/pkg/controller/common/controller_context.go @@ -22,6 +22,8 @@ import ( "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/informers" + corev1informers "k8s.io/client-go/informers/core/v1" + "k8s.io/client-go/kubernetes" "k8s.io/klog/v2" "k8s.io/utils/clock" ) @@ -162,3 +164,33 @@ func CreateControllerContext(ctx context.Context, cb *clients.Builder) *Controll RouteInformerFactory: routeSharedInformer, } } + +// Creates a NodeInformer that is bound to a single node. This is for use by +// the MCD to ensure that the MCD only receives watch events for the node that +// it is running on as opposed to all cluster nodes. Doing this helps reduce +// the load on the apiserver. Because the filters are applied to *all* +// informers constructed by the informer factory, we want to ensure that this +// factory is only used to construct a NodeInformer. +// +// Therefore, to ensure that this informer factory is only used for +// constructing a NodeInformer with this specific filter, we only return the +// instantiated NodeInformer instance and a start function. +func NewScopedNodeInformer(kubeclient kubernetes.Interface, nodeName string) (corev1informers.NodeInformer, func(<-chan struct{})) { + sif := informers.NewSharedInformerFactoryWithOptions( + kubeclient, + resyncPeriod()(), + informers.WithTweakListOptions(func(opts *metav1.ListOptions) { + opts.FieldSelector = fields.OneTermEqualSelector("metadata.name", nodeName).String() + }), + ) + + return sif.Core().V1().Nodes(), sif.Start +} + +// Creates a scoped node informer that is bound to a single node from a +// clients.Builder instance. It sets the user-agent for the client to +// node-scoped-informer before instantiating the node informer. Returns the +// instantiated NodeInformer and a start function. +func NewScopedNodeInformerFromClientBuilder(cb *clients.Builder, nodeName string) (corev1informers.NodeInformer, func(<-chan struct{})) { + return NewScopedNodeInformer(cb.KubeClientOrDie("node-scoped-informer"), nodeName) +} diff --git a/pkg/daemon/writer.go b/pkg/daemon/writer.go index 1220d321cd..3bb47354b6 100644 --- a/pkg/daemon/writer.go +++ b/pkg/daemon/writer.go @@ -8,7 +8,6 @@ import ( "k8s.io/client-go/tools/cache" - "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" @@ -86,10 +85,16 @@ func newNodeWriter(nodeName string, stopCh <-chan struct{}) (NodeWriter, error) } klog.Infof("NodeWriter initialized with credentials from %s", nodeWriterKubeconfigPath) - informer := informers.NewSharedInformerFactory(kubeClient, ctrlcommon.DefaultResyncPeriod()()) - nodeInformer := informer.Core().V1().Nodes() - nodeLister := nodeInformer.Lister() - nodeListerSynced := nodeInformer.Informer().HasSynced + // This informer needs to use the a different service account than the rest + // of the MCD, which is bound to the machine-config-daemon service account. + // Consequently, it must use a different informer factory than the parent + // informer factory. However, we can instantiate both that informer factory + // and the node informer in the same way that we instantiate the MCD + // informer. + informer, startFunc := ctrlcommon.NewScopedNodeInformer(kubeClient, nodeName) + nodeInformer := informer.Informer() + nodeLister := informer.Lister() + nodeListerSynced := nodeInformer.HasSynced eventBroadcaster := record.NewBroadcaster() eventBroadcaster.StartLogging(klog.V(2).Infof) @@ -105,12 +110,12 @@ func newNodeWriter(nodeName string, stopCh <-chan struct{}) (NodeWriter, error) kubeClient: kubeClient, } - nodeInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ + nodeInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: nw.handleNodeWriterEvent, UpdateFunc: func(_, newObj interface{}) { nw.handleNodeWriterEvent(newObj) }, }) - informer.Start(stopCh) + startFunc(stopCh) return nw, nil }