How to Manage Events with a Dynamic Informer in Kubernetes

In this guide, you’ll learn:
✔ What a dynamic informer is and how it differs from a static informer.
✔ How to set up a dynamic informer to watch Kubernetes events.
✔ Best practices for managing multiple resources efficiently.
✔ Real-world Golang code examples for dynamic informers.
✔ Common pitfalls and how to avoid them.
By the end, you’ll be able to implement a production-ready dynamic informer that scales with your Kubernetes workloads.
1. What is a Dynamic Informer in Kubernetes?
A dynamic informer is a Kubernetes client-go component that watches unstructured resources (like CRDs) without requiring compile-time schema definitions. Unlike static informers (which require predefined types), dynamic informers use unstructured.Unstructured
to handle arbitrary Kubernetes objects.
Key Use Cases for Dynamic Informers
✅ Watching Custom Resource Definitions (CRDs) dynamically.
✅ Monitoring multiple resource types with a single informer.
✅ Building generic controllers that adapt to new CRDs at runtime.
Dynamic Informer vs. Static Informer
Feature | Static Informer | Dynamic Informer |
---|---|---|
Resource Type | Predefined (e.g., v1.Pod ) | Any (unstructured.Unstructured ) |
Flexibility | Limited to known types | Works with CRDs and unknown resources |
Performance | Optimized for known schemas | Slightly slower due to runtime type checks |
Use Case | Built-in resources (Pods, Deployments) | Custom resources, multi-resource watchers |
2. Setting Up a Dynamic Informer in Golang
To create a dynamic informer, we’ll use:
dynamic.Interface
(fromk8s.io/client-go/dynamic
)informer.GenericInformer
(for unstructured data)cache.SharedIndexInformer
(to cache and index resources)
Step 1: Initialize the Dynamic Client
package main
import (
"k8s.io/client-go/dynamic"
"k8s.io/client-go/tools/clientcmd"
)
func main() {
// Load kubeconfig
config, err := clientcmd.BuildConfigFromFlags("", "/path/to/kubeconfig")
if err != nil {
panic(err)
}
// Create dynamic client
dynamicClient, err := dynamic.NewForConfig(config)
if err != nil {
panic(err)
}
}
Step 2: Define the Resource to Watch (GVR)
Use schema.GroupVersionResource
to specify which resource to watch.
import (
"k8s.io/apimachinery/pkg/runtime/schema"
)
// Example: Watch for changes in a CRD (e.g., "myresources.example.com")
gvr := schema.GroupVersionResource{
Group: "example.com",
Version: "v1",
Resource: "myresources",
}
Step 3: Create the Dynamic Informer
import (
"k8s.io/client-go/informers"
)
// Create a factory for dynamic informers
factory := informers.NewSharedInformerFactoryWithOptions(
dynamicClient,
0, // Resync period (0 = no resync)
informers.WithNamespace("default"), // Optional: Watch a specific namespace
)
// Get a dynamic informer for the GVR
informer := factory.ForResource(gvr)
Step 4: Add Event Handlers
informer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
// Handle newly created resource
log.Println("Resource added:", obj)
},
UpdateFunc: func(oldObj, newObj interface{}) {
// Handle updates
log.Println("Resource updated:", newObj)
},
DeleteFunc: func(obj interface{}) {
// Handle deletions
log.Println("Resource deleted:", obj)
},
})
Step 5: Start the Informer
stopCh := make(chan struct{})
defer close(stopCh)
factory.Start(stopCh)
factory.WaitForCacheSync(stopCh)
// Keep the informer running
select {}
3. Advanced Dynamic Informer Techniques
A. Watching Multiple Resources with One Informer
Use dynamicinformer.NewDynamicSharedInformerFactory
to watch multiple CRDs:
import (
"k8s.io/client-go/dynamic/dynamicinformer"
)
factory := dynamicinformer.NewDynamicSharedInformerFactory(dynamicClient, 0)
// Watch multiple GVRs
informer1 := factory.ForResource(gvr1).Informer()
informer2 := factory.ForResource(gvr2).Informer()
// Add event handlers to each
informer1.AddEventHandler(...)
informer2.AddEventHandler(...)
B. Mocking a Dynamic Informer for Testing
Use fake.NewSimpleDynamicClient
for unit tests:
import (
"k8s.io/client-go/dynamic/fake"
)
// Create a fake dynamic client
fakeClient := fake.NewSimpleDynamicClient(runtime.NewScheme())
// Use in tests
factory := dynamicinformer.NewDynamicSharedInformerFactory(fakeClient, 0)
C. Handling Timeouts in Dynamic Informers
To prevent hanging, use a context with timeout:
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
// Start informer with context
go informer.Run(ctx.Done())
4. Best Practices for Dynamic Informers
🔹 Optimize Performance: Use WithNamespace()
to limit scope.
🔹 Error Handling: Always check cache.WaitForCacheSync()
.
🔹 Logging: Use structured logging (zap
, logrus
) for debugging.
🔹 Rate Limiting: Avoid API throttling with RateLimitingQueue
.
5. Common Pitfalls & Solutions
Issue | Solution |
---|---|
Informer not detecting changes | Ensure AddEventHandler is registered before starting. |
Memory leaks | Always close(stopCh) to clean up informers. |
API throttling | Use client-go 's built-in rate limiter. |
Unstructured parsing errors | Validate with unstructured.Unstructured.GetXXX() . |
Conclusion
Dynamic informers are essential for Kubernetes operators that need to watch CRDs or multiple resource types. By following this guide, you can:
✔ Set up a dynamic informer in Golang.
✔ Watch multiple resources efficiently.
✔ Handle edge cases like timeouts and testing.
Now you’re ready to build scalable, event-driven Kubernetes controllers with dynamic informers! 🚀