Skip to content

[amazon-cloudwatch-agent-operator] feat(target-allocator): add per-node allocation strategy#398

Open
wenegiemepraise wants to merge 5 commits into
aws:mainfrom
wenegiemepraise:ta-per-node-allocation
Open

[amazon-cloudwatch-agent-operator] feat(target-allocator): add per-node allocation strategy#398
wenegiemepraise wants to merge 5 commits into
aws:mainfrom
wenegiemepraise:ta-per-node-allocation

Conversation

@wenegiemepraise

Copy link
Copy Markdown

Summary

Add a per-node allocation strategy to the Target Allocator so each CloudWatch Agent
(DaemonSet, one per node) scrapes only the ServiceMonitor/PodMonitor targets on its own
node
— eliminating cross-node / cross-AZ scrape traffic. Targets without a resolvable node
fall back to consistent-hashing, so nothing is silently dropped.

Motivation

The fork registers only consistent-hashing, a pure hash of the target URL with no node
awareness — a pod on node A can be scraped by the agent on node B (inter-AZ data-transfer
cost + latency). This ports the upstream OpenTelemetry per-node strategy into the fork.

Changes

  • allocation/per_node.go — node-indexed allocator with consistent-hashing fallback and
    unassigned-target tracking
  • allocation/strategy.go — register per-node
  • collector/collector.go — capture Collector.NodeName from pod.Spec.NodeName
    (skip empty-NodeName pods; handle watch.Modified)
  • CRD allocation-strategy enum (consistent-hashing | per-node)
  • internal/manifests/targetallocator/configmap.go — emit allocation_strategy +
    allocation_fallback_strategy

Dependencies

Testing

  • allocation unit tests (per-node placement, fallback, unassigned tracking)
  • A/B: per-node vs consistent-hashing — per-node yields node-local assignment; flipping to
    consistent-hashing reproduces cross-node scrapes (validated live on EKS).

musa-asad and others added 5 commits June 16, 2026 09:21
…er startup

The target-allocator declared the enable-prometheus-cr-watcher flag name as a
constant but never registered it on the flag set, while the operator passes
--enable-prometheus-cr-watcher whenever PrometheusCR.enabled is true. Because
args are parsed with pflag.ExitOnError, the unregistered flag caused the binary
to print 'unknown flag' and exit(2), putting the target-allocator pod into
CrashLoopBackOff.

This change registers the flag and ORs it with the YAML prometheus_cr.enabled
setting, then fixes three latent defects that were previously unreachable
because the binary crashed first:

- promOperator: set a non-empty Namespace on the synthetic Prometheus object so
  the prometheus-operator config generator no longer panics with
  'namespace can't be empty' in store.ForNamespace.
- promOperator: set EvaluationInterval so the generated config does not render an
  empty global.evaluation_interval, which the prometheus config parser rejects
  with 'empty duration string'.
- main: create and register service-discovery metrics and pass them to
  discovery.NewManager; passing a nil sdMetrics map makes every SD provider fail
  to register, yielding zero discovered targets.

RELEASE_NOTES updated.
Add a regression test asserting that loading a Target Allocator config whose
static scrape job omits scrape_protocols still yields a non-empty
ScrapeProtocols on every loaded scrape config. This is defaulted by the pinned
Prometheus library during yaml.UnmarshalStrict into the prometheus Config type,
so the distributed /scrape_configs payload is never empty and the agent's
prometheus-receiver validation passes. The test fails fast if a future
dependency or load-path change drops this defaulting.
The pod-template restart-trigger sha256 was computed from Spec.Config only,
so a change to Spec.Prometheus (rendered into a separate ConfigMap) left the
pod template byte-identical and the workload controller did not roll the pods.

Fold the serialized Spec.Prometheus (PrometheusConfig.Yaml()) into the hash
input when it is non-empty, so a Prometheus-only change bumps the pod-template
annotation and triggers a rolling restart, matching agent-config behavior.
When no Prometheus config is set the hash input is byte-identical to the agent
config alone, leaving non-Prometheus agents unaffected.
Add a per-node allocation strategy so each CloudWatch agent (DaemonSet, one
per node) scrapes only the ServiceMonitor/PodMonitor targets on its own node,
eliminating cross-node/cross-AZ scrape traffic. Targets that cannot be matched
to a node-local agent (node-less endpoints, nodes without a Ready agent) fall
back to consistent-hashing so they are never silently dropped.

- allocation/per_node.go: perNodeAllocator (node index, consistent-hashing
  fallback ring, unassigned tracking, descriptive logging) + registration.
- allocation/strategy.go: Collector.NodeName, NewCollector(name, node),
  WithFallbackStrategy option, targets_unassigned gauge.
- collector/collector.go: capture pod.Spec.NodeName, skip empty-NodeName pods,
  handle watch.Modified so a collector's node is picked up once scheduled
  (fixes targets being stuck on the fallback after a DaemonSet rollout).
- target/target.go: GetNodeName() from __meta_kubernetes_*_node_name labels.
- config + main: FallbackAllocationStrategy wiring.
- apis/v1alpha1 + CRD: allocationStrategy enum gains "per-node".
- internal/manifests/targetallocator/configmap.go: emit per-node strategy and
  the consistent-hashing fallback from the CR.
# Conflicts:
#	cmd/amazon-cloudwatch-agent-target-allocator/main.go
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants