Repository navigation
[amazon-cloudwatch-agent-operator] Route SM/PM by cloudwatch.aws/scraper annotation - #399
wenegiemepraise wants to merge 5 commits into
Conversation
| // not so annotated. This lets a heavy/singleton monitor be routed to the central cluster-scraper | ||
| // agent while all others stay on the per-node agent. | ||
| // +optional | ||
| ScraperRole string `json:"scraperRole,omitempty"` |
There was a problem hiding this comment.
scraperRole has no enum validation, so a typo like cluster-scaper quietly falls back to the default role and that monitor gets scraped by nobody. The chart hardcodes the right value today, but could we add +kubebuilder:validation:Enum=cluster-scraper and regenerate the CRD?
| if !annotationRoleMatches(w.scraperRole, annotations) { | ||
| return false | ||
| } | ||
| if w.scraperRole == clusterScraperRole { |
There was a problem hiding this comment.
selectsMonitor logs an Info line for every monitor the cluster-scraper claims, and LoadConfig reruns on each reconcile, so a big annotated set means a lot of repeated lines. Low risk, but could we log only on membership change or drop it to V(1)?
| // TestLoadConfigScraperRouting exercises the annotation filter through the real LoadConfig path | ||
| // (matching TestLoadConfig's harness): an annotated ServiceMonitor is discovered by the | ||
| // cluster-scraper role and excluded by the default role. | ||
| func TestLoadConfigScraperRouting(t *testing.T) { |
There was a problem hiding this comment.
The unannotated and PodMonitor cases never actually go through LoadConfig even though PodMonitor discovery mirrors ServiceMonitor, and the informer wait is a HasSynced spin loop with no deadline that hangs until CI kills it. Could we add both LoadConfig cases and use cache.WaitForCacheSync with a timeout?
| // Allocator, based on its scraperRole. cluster-scraper role selects only monitors annotated | ||
| // cloudwatch.aws/scraper: cluster-scraper; the default role (empty) selects only monitors that are | ||
| // not so annotated, so the two roles partition monitors with no overlap and no gap. | ||
| func annotationRoleMatches(scraperRole string, annotations map[string]string) bool { |
There was a problem hiding this comment.
Routing is binary here (only cluster-scraper routes, everything else falls to per-node), so a future gpu-scraper would silently land on per-node. Could we add a note that annotationRoleMatches needs to become an explicit role to value match before a third role shows up?
| } | ||
|
|
||
| if len(params.OtelCol.Spec.TargetAllocator.PrometheusCR.ScraperRole) > 0 { | ||
| taConfig["scraper_role"] = params.OtelCol.Spec.TargetAllocator.PrometheusCR.ScraperRole |
There was a problem hiding this comment.
The operator writes the scraper_role key, which an older Target Allocator image rejects as unknown and then CrashLoopBackOffs on, taking the cluster-scraper path down. Could we make sure the chart pins the operator and Target Allocator images from this commit or newer together?
|
Checklist posted for the PR author to move into the PR description under a PR Checklist
|
329e93f to
e4fe2d5
Compare
Routes a ServiceMonitor/PodMonitor to the central cluster-scraper agent via the cloudwatch.aws/scraper: cluster-scraper annotation on the monitor CR. The cluster-scraper gets its own Target Allocator and prometheusCR with scraperRole: cluster-scraper and consistent-hashing allocation; the per-node agent keeps the default (empty) role and per-node allocation. The Target Allocator ClusterRole/ClusterRoleBinding render once (all Target Allocators share the target-allocator-service-acct service account), so a second Target-Allocator-bearing agent no longer produces a duplicate-named object. The bundled CRD's scraperRole field carries the enum: [cluster-scraper] constraint generated by the operator. Companion: aws/amazon-cloudwatch-agent-operator#399.
e4fe2d5 to
cd3e40b
Compare
Routes a ServiceMonitor/PodMonitor to the central cluster-scraper agent via the cloudwatch.aws/scraper: cluster-scraper annotation on the monitor CR. The cluster-scraper gets its own Target Allocator and prometheusCR with scraperRole: cluster-scraper and consistent-hashing allocation; the per-node agent keeps the default (empty) role and per-node allocation. The Target Allocator ClusterRole/ClusterRoleBinding render once (all Target Allocators share the target-allocator-service-acct service account), so a second Target-Allocator-bearing agent no longer produces a duplicate-named object. The bundled CRD's scraperRole field carries the enum: [cluster-scraper] constraint generated by the operator. Companion: aws/amazon-cloudwatch-agent-operator#399.
Routes a ServiceMonitor/PodMonitor to the central cluster-scraper agent via the cloudwatch.aws/scraper: cluster-scraper annotation on the monitor CR. The cluster-scraper gets its own Target Allocator and prometheusCR with scraperRole: cluster-scraper and consistent-hashing allocation; the per-node agent keeps the default (empty) role and per-node allocation. The Target Allocator ClusterRole/ClusterRoleBinding render once (all Target Allocators share the target-allocator-service-acct service account), so a second Target-Allocator-bearing agent no longer produces a duplicate-named object. The bundled CRD's scraperRole field carries the enum: [cluster-scraper] constraint generated by the operator. Companion: aws/amazon-cloudwatch-agent-operator#399.
…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. (cherry picked from commit 1376451)
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. (cherry picked from commit 0405f4d)
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. (cherry picked from commit d61d693)
Adds a per-node allocation strategy so each CloudWatch Agent scrapes only the ServiceMonitor/PodMonitor targets on its own node, eliminating cross-node and cross-AZ scrape traffic. Targets with no resolvable node fall back to consistent-hashing so nothing is silently dropped, and unassigned targets are surfaced via a gauge. Also changes collector registration for ALL strategies, including the existing consistent-hashing default: the initial List now skips pods with an empty spec.NodeName, watch.Modified is handled so a pod scheduled after Add is picked up, and pods are dropped as soon as they carry a DeletionTimestamp rather than on Deleted. This releases a terminating agent's targets immediately instead of at the end of its grace period, at the cost of more target churn during rollouts. Verified: go build ./... clean; make impi and make checklicense pass; unit tests pass across the target-allocator and manifests packages. (cherry picked from commit 1595a87)
Partitions ServiceMonitor/PodMonitor discovery across CloudWatch Agents by the cloudwatch.aws/scraper annotation on the monitor CR. A monitor annotated cluster-scraper is scraped only by the cluster-scraper agent's Target Allocator; all others only by the per-node agent's. The two roles are complementary, so every monitor is owned by exactly one agent with no double-scrape and no gap. Adds spec.targetAllocator.prometheusCR.scraperRole (Target Allocator config key scraper_role) and a client-side annotation filter applied during monitor discovery in LoadConfig. Note: scraperRole is constrained by an enum with no empty member, so the default per-node role is expressed by omitting the field; setting scraperRole: "" explicitly is rejected by the API server. Companion: aws-observability/helm-charts#339. Verified: go build ./... clean; make impi and make checklicense pass; unit tests pass across the target-allocator and manifests packages. (cherry picked from commit cd3e40b)
cd3e40b to
afa7000
Compare
Routes a ServiceMonitor/PodMonitor to the central cluster-scraper agent via the cloudwatch.aws/scraper: cluster-scraper annotation on the monitor CR. The cluster-scraper gets its own Target Allocator and prometheusCR with scraperRole: cluster-scraper and consistent-hashing allocation; the per-node agent keeps the default (empty) role and per-node allocation. The Target Allocator ClusterRole/ClusterRoleBinding render once (all Target Allocators share the target-allocator-service-acct service account), so a second Target-Allocator-bearing agent no longer produces a duplicate-named object. The bundled CRD's scraperRole field carries the enum: [cluster-scraper] constraint generated by the operator. Companion: aws/amazon-cloudwatch-agent-operator#399. otelContainerInsights.prometheusScrape.enabled defaults to false until operator and Target Allocator images with prometheusCR discovery, per-node allocation and scraper_role are released; enabling it requires pinning those images together. The per-node prometheusCR pipeline applies the same unit normalization as the cluster-scraper one, so a monitor's units do not depend on which agent scrapes it.
Routes a ServiceMonitor/PodMonitor to the central cluster-scraper agent via the cloudwatch.aws.amazon.com/scraper: cluster-scraper annotation on the monitor CR. The cluster-scraper gets its own Target Allocator and prometheusCR with scraperRole: cluster-scraper and consistent-hashing allocation; the per-node agent keeps the default (empty) role and per-node allocation. The Target Allocator ClusterRole/ClusterRoleBinding render once (all Target Allocators share the target-allocator-service-acct service account), so a second Target-Allocator-bearing agent no longer produces a duplicate-named object. The bundled CRD's scraperRole field carries the enum: [cluster-scraper] constraint generated by the operator. Companion: aws/amazon-cloudwatch-agent-operator#399. otelContainerInsights.prometheusScrape.enabled defaults to false until operator and Target Allocator images with prometheusCR discovery, per-node allocation and scraper_role are released; enabling it requires pinning those images together. The per-node prometheusCR pipeline applies the same unit normalization as the cluster-scraper one, so a monitor's units do not depend on which agent scrapes it.
Routes a ServiceMonitor/PodMonitor to the central cluster-scraper agent via the cloudwatch.aws.amazon.com/scraper: cluster-scraper annotation on the monitor CR. The cluster-scraper gets its own Target Allocator and prometheusCR with scraperRole: cluster-scraper and consistent-hashing allocation; the per-node agent keeps the default (empty) role and per-node allocation. The Target Allocator ClusterRole/ClusterRoleBinding render once (all Target Allocators share the target-allocator-service-acct service account), so a second Target-Allocator-bearing agent no longer produces a duplicate-named object. The bundled CRD's scraperRole field carries the enum: [cluster-scraper] constraint generated by the operator. Companion: aws/amazon-cloudwatch-agent-operator#399. otelContainerInsights.prometheusScrape.enabled defaults to false until operator and Target Allocator images with prometheusCR discovery, per-node allocation and scraper_role are released; enabling it requires pinning those images together. The per-node prometheusCR pipeline applies the same unit normalization as the cluster-scraper one, so a monitor's units do not depend on which agent scrapes it.
Routes a ServiceMonitor/PodMonitor to the central cluster-scraper agent via the cloudwatch.aws.amazon.com/scraper: cluster-scraper annotation on the monitor CR. The cluster-scraper gets its own Target Allocator and prometheusCR with scraperRole: cluster-scraper and consistent-hashing allocation; the per-node agent keeps the default (empty) role and per-node allocation. The Target Allocator ClusterRole/ClusterRoleBinding render once (all Target Allocators share the target-allocator-service-acct service account), so a second Target-Allocator-bearing agent no longer produces a duplicate-named object. The bundled CRD's scraperRole field carries the enum: [cluster-scraper] constraint generated by the operator. Companion: aws/amazon-cloudwatch-agent-operator#399. otelContainerInsights.prometheusScrape.enabled defaults to false until operator and Target Allocator images with prometheusCR discovery, per-node allocation and scraper_role are released; enabling it requires pinning those images together. The per-node prometheusCR pipeline applies the same unit normalization as the cluster-scraper one, so a monitor's units do not depend on which agent scrapes it. otelContainerInsights.prometheusScrape.crds.install defaults to never: the ServiceMonitor/PodMonitor CRDs are a prerequisite (installed by prometheus-operator, kube-prometheus-stack, or the two kubectl apply commands in values.yaml), and the Target Allocator starts discovery once they exist. auto and always remain available for direct helm install/upgrade, where the skip-if-exists lookup and helm.sh/resource-policy: keep apply. Rendered without cluster access (helm template, Argo CD, Flux, the EKS add-on), lookup returns nothing, so the chart does not install the CRDs by default.
Routes a ServiceMonitor/PodMonitor to the central cluster-scraper agent via the cloudwatch.aws.amazon.com/scraper: cluster-scraper annotation on the monitor CR. The cluster-scraper gets its own Target Allocator and prometheusCR with scraperRole: cluster-scraper and consistent-hashing allocation; the per-node agent keeps the default (empty) role and per-node allocation. The Target Allocator ClusterRole/ClusterRoleBinding render once (all Target Allocators share the target-allocator-service-acct service account), so a second Target-Allocator-bearing agent no longer produces a duplicate-named object. The bundled CRD's scraperRole field carries the enum: [cluster-scraper] constraint generated by the operator. Companion: aws/amazon-cloudwatch-agent-operator#399. otelContainerInsights.prometheusScrape.enabled defaults to false until operator and Target Allocator images with prometheusCR discovery, per-node allocation and scraper_role are released; enabling it requires pinning those images together. The per-node prometheusCR pipeline applies the same unit normalization as the cluster-scraper one, so a monitor's units do not depend on which agent scrapes it. otelContainerInsights.prometheusScrape.crds.install defaults to never: the ServiceMonitor/PodMonitor CRDs are a prerequisite (installed by prometheus-operator, kube-prometheus-stack, or the two kubectl apply commands in values.yaml), and the Target Allocator starts discovery once they exist. auto and always remain available for direct helm install/upgrade, where the skip-if-exists lookup and helm.sh/resource-policy: keep apply. Rendered without cluster access (helm template, Argo CD, Flux, the EKS add-on), lookup returns nothing, so the chart does not install the CRDs by default.
Routes a ServiceMonitor/PodMonitor to the central cluster-scraper agent via the cloudwatch.aws.amazon.com/scraper: cluster-scraper annotation on the monitor CR. The cluster-scraper gets its own Target Allocator and prometheusCR with scraperRole: cluster-scraper and consistent-hashing allocation; the per-node agent keeps the default (empty) role and per-node allocation. The Target Allocator ClusterRole/ClusterRoleBinding render once (all Target Allocators share the target-allocator-service-acct service account), so a second Target-Allocator-bearing agent no longer produces a duplicate-named object. The bundled CRD's scraperRole field carries the enum: [cluster-scraper] constraint generated by the operator. Companion: aws/amazon-cloudwatch-agent-operator#399 and #435. otelContainerInsights.prometheusScrape.enabled defaults to false until operator and Target Allocator images with prometheusCR discovery, per-node allocation and scraper_role are released; enabling it requires pinning those images together. The per-node prometheusCR pipeline applies the same unit normalization as the cluster-scraper one, so a monitor's units do not depend on which agent scrapes it. The chart does not install the ServiceMonitor/PodMonitor CRDs. When prometheusScrape is enabled, it starts the operator with --manage-prometheus-crds and grants it customresourcedefinitions create/get/list/watch, so the operator creates the CRDs only if they are missing (amazon-cloudwatch-agent-operator#435). A copy installed by prometheus-operator, kube-prometheus-stack, Argo CD or kubectl is left unchanged, and the operator never deletes the CRDs, so uninstalling the chart or the EKS add-on leaves them and every ServiceMonitor/PodMonitor in place.
Routes a ServiceMonitor/PodMonitor to the central cluster-scraper agent via the cloudwatch.aws.amazon.com/scraper: cluster-scraper annotation on the monitor CR. The cluster-scraper gets its own Target Allocator and prometheusCR with scraperRole: cluster-scraper and consistent-hashing allocation; the per-node agent keeps the default (empty) role and per-node allocation. The Target Allocator ClusterRole/ClusterRoleBinding render once (all Target Allocators share the target-allocator-service-acct service account), so a second Target-Allocator-bearing agent no longer produces a duplicate-named object. The bundled CRD's scraperRole field carries the enum: [cluster-scraper] constraint generated by the operator. Companion: aws/amazon-cloudwatch-agent-operator#399 and #435. otelContainerInsights.prometheusScrape.enabled defaults to false until operator and Target Allocator images with prometheusCR discovery, per-node allocation and scraper_role are released; enabling it requires pinning those images together. The per-node prometheusCR pipeline applies the same unit normalization as the cluster-scraper one, so a monitor's units do not depend on which agent scrapes it. The chart does not install the ServiceMonitor/PodMonitor CRDs. When prometheusScrape is enabled, it starts the operator with --manage-prometheus-crds and grants it customresourcedefinitions create/get/list/watch, so the operator creates the CRDs only if they are missing (amazon-cloudwatch-agent-operator#435). A copy installed by prometheus-operator, kube-prometheus-stack, Argo CD or kubectl is left unchanged, and the operator never deletes the CRDs, so uninstalling the chart or the EKS add-on leaves them and every ServiceMonitor/PodMonitor in place.
…PrometheusMonitor Adds a top-level otelPrometheusMonitor.enabled switch (default false) that scrapes the targets selected by ServiceMonitor and PodMonitor objects (monitoring.coreos.com/v1) through the Target Allocator's prometheusCR discovery and sends them to CloudWatch over OTLP. It works with otelContainerInsights on or off. Monitors annotated cloudwatch.aws.amazon.com/scraper: cluster-scraper are scraped by the cluster-scraper agent, which gets its own Target Allocator with scraperRole: cluster-scraper and consistent-hashing allocation. The per-node agent keeps the default (empty) role and per-node allocation. The allocation strategies are set internally. With otelContainerInsights off, each agent's config is the SM/PM pipeline alone, cut from the same template that defines it for otelContainerInsights, so the shared processor, exporter and extension definitions are identical either way and no other otelContainerInsights pipeline renders. The cluster-scraper agent renders when either feature is on. The SM/PM series carry cloudwatch.solution k8s-otel-prometheus-monitors and cloudwatch.pipeline prometheus-monitors, and both pipelines apply the same unit normalization, so a monitor's units do not depend on which agent scrapes it. The Target Allocator ClusterRole/ClusterRoleBinding render once (all Target Allocators share the target-allocator-service-acct service account), so a second Target-Allocator-bearing agent no longer produces a duplicate-named object. The bundled CRD's scraperRole field carries the enum: [cluster-scraper] constraint generated by the operator. Companion: aws/amazon-cloudwatch-agent-operator#399 and #435. otelPrometheusMonitor.enabled defaults to false until operator and Target Allocator images with prometheusCR discovery, per-node allocation, scraper_role and --manage-prometheus-crds are released; enabling it requires pinning those images together. The chart does not install the ServiceMonitor/PodMonitor CRDs. When otelPrometheusMonitor is enabled, it starts the operator with --manage-prometheus-crds and grants it customresourcedefinitions create/get/list/watch, so the operator creates the CRDs only if they are missing (amazon-cloudwatch-agent-operator#435). A copy installed by prometheus-operator, kube-prometheus-stack, Argo CD or kubectl is left unchanged, and the operator never deletes the CRDs, so uninstalling the chart or the EKS add-on leaves them and every ServiceMonitor/PodMonitor in place.
…PrometheusMonitor Adds a top-level otelPrometheusMonitor.enabled switch (default false) that scrapes the targets selected by ServiceMonitor and PodMonitor objects (monitoring.coreos.com/v1) through the Target Allocator's prometheusCR discovery and sends them to CloudWatch over OTLP. It works with otelContainerInsights on or off. Monitors annotated cloudwatch.aws.amazon.com/scraper: cluster-scraper are scraped by the cluster-scraper agent, which gets its own Target Allocator with scraperRole: cluster-scraper and consistent-hashing allocation. The per-node agent keeps the default (empty) role and per-node allocation. The allocation strategies are set internally. With otelContainerInsights off, each agent's config is the SM/PM pipeline alone, cut from the same template that defines it for otelContainerInsights, so the shared processor, exporter and extension definitions are identical either way and no other otelContainerInsights pipeline renders. The cluster-scraper agent renders when either feature is on. The SM/PM series carry cloudwatch.solution k8s-otel-prometheus-monitors and cloudwatch.pipeline prometheus-monitors, and both pipelines apply the same unit normalization, so a monitor's units do not depend on which agent scrapes it. The Target Allocator ClusterRole/ClusterRoleBinding render once (all Target Allocators share the target-allocator-service-acct service account), so a second Target-Allocator-bearing agent no longer produces a duplicate-named object. The bundled CRD's scraperRole field carries the enum: [cluster-scraper] constraint generated by the operator. Companion: aws/amazon-cloudwatch-agent-operator#399 and #435. otelPrometheusMonitor.enabled defaults to false until operator and Target Allocator images with prometheusCR discovery, per-node allocation, scraper_role and --manage-prometheus-crds are released; enabling it requires pinning those images together. The chart does not install the ServiceMonitor/PodMonitor CRDs. When otelPrometheusMonitor is enabled, it starts the operator with --manage-prometheus-crds and grants it customresourcedefinitions create/get/list/watch, so the operator creates the CRDs only if they are missing (amazon-cloudwatch-agent-operator#435). A copy installed by prometheus-operator, kube-prometheus-stack, Argo CD or kubectl is left unchanged, and the operator never deletes the CRDs, so uninstalling the chart or the EKS add-on leaves them and every ServiceMonitor/PodMonitor in place.
Summary
Route ServiceMonitor/PodMonitor discovery across CloudWatch agents by the
cloudwatch.aws/scraperannotation on the monitor CR. A monitor annotatedcloudwatch.aws/scraper: cluster-scraperis scraped only by the cluster-scraper agent's TargetAllocator; all others only by the per-node agent's.
What
spec.targetAllocator.prometheusCR.scraperRolefield (Target Allocator configscraper_role).annotationRoleMatches) applied during monitordiscovery in
LoadConfig.cluster-scraperrole keeps only annotated monitors; the empty(default) role keeps only unannotated ones — complementary, so every monitor is owned by exactly
one agent (no double-scrape, no gap).
Why annotation (not label)
The routing key is behavioural config, not identity. Both agents' TAs already list all monitors
into their cache and filter client-side, and the two roles' selections union to the full set — so
server-side label filtering would gain nothing here. Filtering by annotation is a few lines in the
existing
ListAllcallback.Testing
go build ./...and unit tests pass, incl. newTestAnnotationRoleMatches(8 cases + partition invariant).cluster-scraper TA
/jobs; an unannotated one only in the per-node TA/jobs.GOPROXY=direct GOSUMDB=off(offline proxy); CI is the authoritative gate.Dependencies / stacking
Stacks on the per-node allocation operator work (#398,
ta-per-node-allocation). Companion helm PR:[amazon-cloudwatch-observability] Route SM/PM to cluster-scraper by annotation.