You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs: improve HA tracker setup and verification guide
## Summary
Clarifies Prometheus HA pair labeling (no env expansion in external_labels), recommends write_relabel for `__replica__`, documents Alertmanager duplicate notifications, and adds verification metrics/status page guidance. Cross-links arguments and the HA pair guide.
Fixes#3011
Signed-off-by: Vedant Madane <6527493+VedantMadane@users.noreply.github.com>
Copy file name to clipboardExpand all lines: docs/configuration/arguments.md
+5-3Lines changed: 5 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -268,16 +268,18 @@ HA tracking has two of its own flags:
268
268
- `distributor.ha-tracker.replica`
269
269
Prometheus label to look for in samples to identify a Prometheus HA replica. (default "`__replica__`")
270
270
271
-
It's reasonable to assume people probably already have a `cluster` label, or something similar. If not, they should add one along with `__replica__` via external labels in their Prometheus config. If you stick to these default values, your Prometheus config could look like this (`POD_NAME` is an environment variable which must be set by you):
271
+
It's reasonable to assume people probably already have a `cluster` label, or something similar. If not, they should add one along with `__replica__` via external labels in their Prometheus config. Prometheus does **not** expand environment variables in `external_labels`; `$POD_NAME` is only useful if your config management substitutes it before Prometheus loads the file. Prefer a concrete per-instance value, or set `__replica__` only on `remote_write` via `write_relabel_configs` (see [HA pair handling](../guides/ha-pair-handling.md)).
272
+
273
+
Example with static replica ids:
272
274
273
275
```yaml
274
276
global:
275
277
external_labels:
276
278
cluster: clustername
277
-
__replica__: $POD_NAME
279
+
__replica__: replica-a
278
280
```
279
281
280
-
HA Tracking looks for the two labels (which can be overridden per user).
282
+
HA Tracking looks for the two labels (which can be overridden per user). For a full setup guide including Alertmanager deduplication and verification metrics, see [Config for sending HA Pairs data to Cortex](../guides/ha-pair-handling.md).
281
283
282
284
It also talks to a KVStore and has its own copies of the same flags used by the Distributor to connect to the ring.
Copy file name to clipboardExpand all lines: docs/guides/ha-pair-handling.md
+68-30Lines changed: 68 additions & 30 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -21,25 +21,43 @@ Now we do the same leader election process for T2.
21
21
22
22
### Client Side
23
23
24
-
So for Cortex to achieve this, we need 2 identifiers for each process, one identifier for the cluster (T1 or T2, etc.) and one identifier to identify the replica in the cluster (a or b). The easiest way to do this is by setting external labels; the default labels are `cluster` and `__replica__`. For example:
24
+
So for Cortex to achieve this, we need 2 identifiers for each process, one identifier for the cluster (T1 or T2, etc.) and one identifier to identify the replica in the cluster (a or b). The easiest way to do this is by setting external labels; the default labels are `cluster` and `__replica__`.
25
25
26
-
```
27
-
cluster: prom-team1
28
-
__replica__: replica1 (or pod-name)
29
-
```
26
+
Deploy **two Prometheus servers** (or Prometheus pods) that scrape the same targets. Give them the **same**`cluster` value and a **different**`__replica__` value:
30
27
31
-
and
28
+
Prometheus / replica A (`prometheus-a`):
32
29
30
+
```yaml
31
+
global:
32
+
external_labels:
33
+
cluster: prom-team1
34
+
__replica__: replica-a
33
35
```
34
-
cluster: prom-team1
35
-
__replica__: replica2
36
+
37
+
Prometheus / replica B (`prometheus-b`):
38
+
39
+
```yaml
40
+
global:
41
+
external_labels:
42
+
cluster: prom-team1
43
+
__replica__: replica-b
36
44
```
37
45
38
-
Note: These are external labels and have nothing to do with remote_write config.
46
+
Both should `remote_write` to the same Cortex distributor endpoint.
47
+
48
+
**Important:** Prometheus does **not** expand environment variables inside `external_labels`. A value like `$POD_NAME` or `${POD_NAME}` is taken literally unless your deployment injects the concrete value when rendering the config (for example via a Kubernetes Downward API + config template, or a config reloader). Setting `__replica__: $POD_NAME` in a static file will **not** give each pod a unique replica id.
39
49
40
-
These two label names are configurable per-tenant within Cortex and should be set to something sensible. For example, the cluster label is already used by some workloads, and you should set the label to be something else that uniquely identifies the cluster. Good examples for this label-name would be `team`, `cluster`, `prometheus`, etc.
50
+
Practical ways to set a unique replica label:
41
51
42
-
The replica label should be set so that the value for each prometheus is unique in that cluster. Note: Cortex drops this label when ingesting data but preserves the cluster label. This way, your timeseries won't change when replicas change.
52
+
* Hard-code a distinct value per Prometheus instance (`replica-a` / `replica-b`).
53
+
* Template the config so the pod name or StatefulSet ordinal is written into `external_labels`.
54
+
* Prefer attaching `__replica__` only on the remote_write path with `write_relabel_configs` (see [Remote Write replica label](#remote-write-replica-label) below). That avoids putting the replica label on local alerts and on `remote_read` queries.
55
+
56
+
Note: These HA labels are Prometheus external labels (or write-relabel labels). They are separate from other `remote_write` settings such as URL, auth, and queue config.
57
+
58
+
These two label names are configurable per-tenant within Cortex (`-distributor.ha-tracker.cluster` and `-distributor.ha-tracker.replica`, see [HA Tracker flags](../configuration/arguments.md#ha-tracker)) and should be set to something sensible. For example, the `cluster` label is already used by some workloads, and you should set the label name to something else that uniquely identifies the Prometheus HA pair. Good examples for this label name would be `team`, `cluster`, `prometheus`, etc.
59
+
60
+
The replica label value must be unique among Prometheus servers in that HA pair. Cortex **drops** the replica label when ingesting samples but **keeps** the cluster label. This way, your time series identity does not change when the elected replica fails over.
43
61
44
62
### Server Side
45
63
@@ -73,41 +91,61 @@ For further configuration file documentation, see the [distributor section](../c
73
91
74
92
For flag configuration, see the [distributor flags](../configuration/arguments.md#ha-tracker) having `ha-tracker` in them.
75
93
76
-
## Remote Read
94
+
## Remote Write replica label
77
95
78
-
If you plan to use remote_read, you can't have the `__replica__` label in the
79
-
external section. Instead, you will need to add it only on the remote_write
80
-
section of your prometheus.yml.
96
+
If you plan to use `remote_read`, or you want HA Prometheus pairs **without** duplicating Alertmanager notifications, do **not** put the `__replica__` label in `global.external_labels`. Add it only on the `remote_write` path via `write_relabel_configs`:
81
97
82
-
```
98
+
```yaml
83
99
global:
84
100
external_labels:
85
101
cluster: prom-team1
86
102
remote_write:
87
-
- url: https://cortex/api/v1/push
88
-
write_relabel_configs:
89
-
- target_label: __replica__
90
-
replacement: 1
103
+
- url: https://cortex/api/v1/push
104
+
write_relabel_configs:
105
+
- target_label: __replica__
106
+
replacement: replica-a
91
107
```
92
108
93
-
and
109
+
and on the second Prometheus:
94
110
95
-
```
111
+
```yaml
96
112
global:
97
113
external_labels:
98
114
cluster: prom-team1
99
115
remote_write:
100
-
- url: https://cortex/api/v1/push
101
-
write_relabel_configs:
102
-
- target_label: __replica__
103
-
replacement: replica2
116
+
- url: https://cortex/api/v1/push
117
+
write_relabel_configs:
118
+
- target_label: __replica__
119
+
replacement: replica-b
104
120
```
105
121
106
-
When Prometheus is executing remote read queries, it will add the external
107
-
labels to the query. In this case, if it asks for the `__replica__` label,
108
-
Cortex will not return any data.
122
+
When Prometheus runs `remote_read` queries, it attaches `global.external_labels` to the selectors. If `__replica__` is a global external label, the query includes that label, and Cortex will not return the deduplicated series (the replica label was dropped at ingest). Therefore `__replica__` should only be added for remote write.
123
+
124
+
## Avoiding duplicate Alertmanager notifications
125
+
126
+
Cortex HA deduplication applies to **samples ingested via remote_write**. It does **not** stop each Prometheus replica from evaluating rules and sending its own alerts.
127
+
128
+
If both replicas send alerts to the same Alertmanager and their `external_labels` differ (for example different `__replica__` values in `global.external_labels`), Alertmanager treats them as distinct label sets and you get **duplicate notifications**.
129
+
130
+
Mitigations:
131
+
132
+
1. **Recommended:** keep a stable `cluster` (and any other shared labels) in `global.external_labels`, and attach `__replica__` only with `remote_write.write_relabel_configs` as shown above. Both replicas then send alerts with the same label set, so Alertmanager can deduplicate them.
133
+
2. Send alerts from only one replica (or from the Cortex ruler) instead of from every Prometheus HA member.
134
+
3. If replica labels must remain on alerts, configure Alertmanager inhibition / grouping so parallel notifications are suppressed — this is harder to get right than (1).
135
+
136
+
## Verifying the HA tracker
137
+
138
+
After enabling the tracker on distributors:
139
+
140
+
1. Open the distributor HA status page: [`GET /distributor/ha_tracker`](../api/_index.md#ha-tracker-status) (also served at `/ha-tracker`). You should see one elected replica per user/cluster pair.
141
+
2. Scrape distributor metrics (names may be prefixed depending on your registry configuration, commonly with `cortex_`):
142
+
* `ha_tracker_elected_replica_changes_total` — increases when leadership fails over.
143
+
* `ha_tracker_elected_replica_timestamp_seconds` — last update time for the elected replica.
144
+
* `ha_tracker_user_replica_group_count` — number of HA clusters tracked per tenant.
145
+
* `ha_tracker_kv_store_cas_total` — KV compare-and-swap traffic for elections.
146
+
3. From a test query in Grafana/Cortex, confirm series do **not** include the replica label and that values are not roughly 2× what a single Prometheus would produce.
109
147
110
-
Therefore, the `__replica__` label should only be added for remote write.
148
+
If every sample is rejected or nothing is elected, check that both HA labels are present on written samples, the KV backend is shared by all distributors, and `accept_ha_samples` / `enable_ha_tracker` are enabled (see [Server Side](#server-side)).
111
149
112
150
## Accept multiple HA pairs in single request
113
151
Let's assume there are two teams (T1 and T2), and each team operates two Prometheus for the HA (T1.a, T1.b for T1 and
0 commit comments