I think the keyword in that comment is shareable or in other words reusable rules. Meaning you (often) preserve more labels while using ignoring compared to on and the results will be (usually) a rule with more of it's original labels left intact, so it can be reused for more scenarios.
Imagine these time series:
instance_cpu_time_ns{app="lion", proc="web", rev="34d0f99", env="prod", job="cluster-manager"}
instance_cpu_time_ns{app="elephant", proc="worker", rev="34d0f99", env="prod", job="cluster-manager"}
instance_cpu_time_ns{app="turtle", proc="api", rev="4d3a513", env="prod", job="cluster-manager"}
instance_cpu_time_ns{app="fox", proc="widget", rev="4d3a513", env="prod", job="cluster-manager"}
...
A query with ignoring(rev) leaves out all the other labels in the result, compared with the same query with on(app).
But the result of on and ignoring would be identical if you use them with mutually exclusive set of labels, like the example you are mentioning.
monitoring - How can I 'join' two metrics in a Prometheus query? - Stack Overflow
promql - join prometheus queries while keeping data from the left side - Stack Overflow
Error when using group_left in Prometheus - Stack Overflow
Prometheus add extra label on join request without using group statement? - Stack Overflow
You can use the argument list of group_left to include extra labels from the right operand (parentheses and indents for clarity):
(
max(consul_health_service_status{status="critical"})
by (service_name,status,node) == 1
)
+ on(service_name,node) group_left(env)
(
0 * consul_service_tags
)
The important part here is the operation + on(service_name,node) group_left(env):
- the
+is "abused" as a join operator (fine since0 * consul_service_tagsalways has the value 0) group_left(env)is the modifier that includes the extra labelenvfrom the right (consul_service_tags)
It is a good practice in Prometheus ecosystem to expose additional labels, which can be joined to multiple metrics, via a separate info-like metric as explained in this article. For example, consul_service_tags metric exposes a set of tags, which can be joined to metrics via (service_name, node) labels.
The join is usually performed via on() and group_left() modifiers applied to * operation. The * doesn't modify values for time series on the left side because info-like metrics usually have constant 1 values. The on() modifier is used for limiting the labels used for finding matching time series on the left and the right side of *. The group_left() modifier is used for adding additional labels from time series on the right side of *. See these docs for details.
For example, the following PromQL query adds env label from consul_service_tags metric to consul_health_service_status metric with the same set of (service_name, node) labels:
consul_health_service_status
* on(service_name, node) group_left(env)
consul_service_tags
Additional label filters can be added to consul_health_service_status if needed. For example, the following query returns only time series with status="critical" label:
consul_health_service_status{status="critical"}
* on(service_name, node) group_left(env)
consul_service_tags
The RHS has no instance label, so it's trying to match all those series to one on the LHS. Try max by (node, instance) (kube_node_labels{label_grid="true"})
The group_left() modifier expects that the right-hand side of * operator (and any other operator) contains only a single time series per each label=value set specified inside on() modifier. Otherwise it returns duplicate series for the match group error. See these docs for more details.
The solution is to specify the proper labels inside on() modifier, so every label=value set for these labels would have only a single time series on the right-hand side of * operator. The instance label is a good candidate to put inside on() modifier. The only issue is that the dcgm_gpu_utilization and kube_node_labels are collected from different targets with different TCP port numbers. So they have different instance label values (see these docs explaining how instance label is generated). This breaks matching rules for * operator, so the following query returns nothing:
floor(avg_over_time(dcgm_gpu_utilization{cluster_name="researchers"}[5m]))
* on (instance) group_left(node)
kube_node_labels{label_grid="true"}
This can be fixed by stripping the port number from instance label at both sides of * operator with the help of label_replace function:
label_replace(
floor(avg_over_time(dcgm_gpu_utilization{cluster_name="researchers"}[5m])),
"hostname",
"$1",
"instance",
"([^:]+):.+"
)
* on (hostname) group_left(node)
label_replace(
kube_node_labels{label_grid="true"},
"hostname",
"$1",
"instance",
"([^:]+):.+"
)
This query extracts hostname part from instance labels, puts it into a hostname label and then joins the left-hand side and the right-hand side time series on this label.