Skip to content

awsnodeagent_policy_programming_latency_seconds records inaccurate observations due to stale last-change-trigger-time annotation #644

Description

@mharmer-canva

What happened:

The end-to-end policy programming latency histograms awsnodeagent_policy_programming_latency_seconds and awsnodeagent_cluster_policy_programming_latency_seconds (added in #516) record large numbers of multi-minute observations on clusters whose dataplane is demonstrably healthy. Mechanism, confirmed against the code in both repos:

  • The Network Policy Controller stamps networking.k8s.aws/last-change-trigger-time on a PolicyEndpoint in setLastChangeTriggerTime() (pkg/policyendpoints/manager.go, added change timestamp in PE and CPE annotation amazon-network-policy-controller-k8s#213) only when processing a genuine change — but PolicyEndpoints are also rewritten at other times (background rewrites, mass rewrites during node churn) without the annotation being refreshed.
  • The agent's observePolicyProgrammingLatency() (controllers/policyendpoints_controller.go, emitting latency metrics based on PE and CPE updates in NPC #516) computes time.Since(triggerTime) and calls Observe() on every PE reconcile. The only guard is trackerStartTime (annotations predating agent start are skipped); there is no per-PE comparison against the previously-seen annotation value. Since every agent watches every PE, each rewrite of a PE carrying an old annotation produces one observation of (now − old annotation) per agent, landing in the top (≥300s / +Inf) buckets.

What you expected to happen:

An observation should be emitted only when a PE write actually carries a new trigger annotation: the agent should compare the annotation to the previously-seen value for that PE and skip unchanged ones. And/or the controller should refresh or clear the annotation on non-trigger rewrites so it never accompanies a write it did not trigger. kube-proxy's network_programming_duration_seconds handling of EndpointsLastChangeTriggerTime — which is cited as the pattern being followed — is the prior art: it only measures when the trigger time changes.

How to reproduce it (as minimally and precisely as possible):

  1. EKS Kubernetes 1.35, VPC CNI v1.22.3 with network policy enabled (aws-network-policy-agent v1.3.7, EKS-managed network policy controller).
  2. Create a NetworkPolicy whose selector membership rarely changes (e.g. a static ingress policy for a stable Deployment).
  3. Wait for, or induce, PolicyEndpoint rewrites that involve no selector-membership change (node churn / controller rewrite cycle).
  4. Observe resourceVersion advances while the annotation stays fixed:
kubectl get policyendpoint <pe> -o jsonpath='{.metadata.resourceVersion} {.metadata.annotations.networking\.k8s\.aws/last-change-trigger-time}'
  1. Scrape awsnodeagent_policy_programming_latency_seconds on any node: each such rewrite adds one observation of (now − annotation) per agent.

Environment:
Kubernetes version (use kubectl version): 1.35
CNI Version: 1.22.3
Network Policy Agent Version: 1.3.7

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions