aws / aws/amazon-network-policy-controller-k8s
Filter headless Services from the informer cache via spec.clusterIP field selector
- Dominant language
- Go
- Stars
- 54
- Forks
- 28
- PR merge metrics
- No merged PRs in 30d
Description
## What
Add a server-side field selector to the Service informer so headless Services are never listed, watched, or cached:
```go
&corev1.Service{}: {
Transform: k8s.StripDownServiceTransformFunc,
Field: fields.OneTermNotEqualSelector("spec.clusterIP", corev1.ClusterIPNone),
},
```
This is the same pattern the controller already uses for Pods (`podCacheFieldSelector` in `pkg/config/runtime_config.go`), and the same optimization kubelet adopted in Kubernetes 1.31 to stop watching headless Services (kubernetes/kubernetes#122541, motivated by kubernetes/kubernetes#122394).
## Why
Every consumer of the Service cache already ignores headless Services, so caching them is pure overhead:
- `pkg/resolvers/endpoints.go` — `getMatchingServiceClusterIPs` skips headless Services (no ClusterIP to resolve).
- `pkg/resolvers/policies_for_service.go` — NetworkPolicy and ApplicationNetworkPolicy service-reference resolution early-returns on headless Services.
- `internal/eventhandlers/service.go` — headless Service create/update/delete events still fire the handler, and `GetReferredClusterPoliciesForService` enqueues **all** ClusterNetworkPolicies for reconcile even though headless Services can never contribute endpoints.
Filtering server-side eliminates:
1. Cache memory for headless Services (can be a large fraction of total Services in StatefulSet-heavy clusters).
2. List/watch bandwidth on the apiserver connection.
3. Iteration overhead in `getMatchingServiceClusterIPs`, which lists Services per egress-peer per namespace.
4. Spurious full-CNP reconciles triggered by headless Service churn.
`spec.clusterIP` is immutable, so Services do not transition in/out of the filtered set. For the ExternalName↔ClusterIP type-change edge case, the apiserver synthesizes Added/Deleted watch events, which the existing handlers already handle.
## Constraint
The `spec.clusterIP` field selector for Services requires **Kubernetes 1.31+**. On older apiservers the list/watch fails with `field label not supported` and the informer never syncs. The README currently states a 1.25+ prerequisite, so this needs one of:
- a minimum-version bump to 1.31+, or
- server-version discovery at startup with conditional application of the selector (kubelet-style fallback).
Note: existing `IsServiceHeadless` checks should remain regardless — they cover the unfiltered fallback path and ExternalName Services (`clusterIP == ""`), which the `!= "None"` selector does not exclude.
Contributor guide
Assessment
This issue has not been assessed yet.