Skip to content

Commit e3ea44f

Browse files
committed
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>
1 parent a330338 commit e3ea44f

2 files changed

Lines changed: 73 additions & 33 deletions

File tree

docs/configuration/arguments.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -268,16 +268,18 @@ HA tracking has two of its own flags:
268268
- `distributor.ha-tracker.replica`
269269
Prometheus label to look for in samples to identify a Prometheus HA replica. (default "`__replica__`")
270270

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:
272274

273275
```yaml
274276
global:
275277
external_labels:
276278
cluster: clustername
277-
__replica__: $POD_NAME
279+
__replica__: replica-a
278280
```
279281

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).
281283

282284
It also talks to a KVStore and has its own copies of the same flags used by the Distributor to connect to the ring.
283285
- `distributor.ha-tracker.failover-timeout`

docs/guides/ha-pair-handling.md

Lines changed: 68 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -21,25 +21,43 @@ Now we do the same leader election process for T2.
2121

2222
### Client Side
2323

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__`.
2525

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:
3027

31-
and
28+
Prometheus / replica A (`prometheus-a`):
3229

30+
```yaml
31+
global:
32+
external_labels:
33+
cluster: prom-team1
34+
__replica__: replica-a
3335
```
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
3644
```
3745

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.
3949

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:
4151

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.
4361

4462
### Server Side
4563

@@ -73,41 +91,61 @@ For further configuration file documentation, see the [distributor section](../c
7391

7492
For flag configuration, see the [distributor flags](../configuration/arguments.md#ha-tracker) having `ha-tracker` in them.
7593

76-
## Remote Read
94+
## Remote Write replica label
7795

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`:
8197

82-
```
98+
```yaml
8399
global:
84100
external_labels:
85101
cluster: prom-team1
86102
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
91107
```
92108

93-
and
109+
and on the second Prometheus:
94110

95-
```
111+
```yaml
96112
global:
97113
external_labels:
98114
cluster: prom-team1
99115
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
104120
```
105121

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.
109147

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)).
111149

112150
## Accept multiple HA pairs in single request
113151
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

Comments
 (0)