Skip to content

Commit c0ccea9

Browse files
author
Julien Pivotto
committed
Support metric_relabel_configs in distributor
Signed-off-by: Julien Pivotto <[email protected]>
1 parent 40d8240 commit c0ccea9

File tree

7 files changed

+116
-0
lines changed

7 files changed

+116
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
* [FEATURE] Shuffle sharding: added support for shuffle-sharding queriers in the query-frontend. When configured (`-frontend.max-queriers-per-tenant` globally, or using per-tenant limit `max_queriers_per_tenant`), each tenants's requests will be handled by different set of queriers. #3113 #3257
5252
* [FEATURE] Shuffle sharding: added support for shuffle-sharding ingesters on the read path. When ingesters shuffle-sharding is enabled and `-querier.shuffle-sharding-ingesters-lookback-period` is set, queriers will fetch in-memory series from the minimum set of required ingesters, selecting only ingesters which may have received series since 'now - lookback period'. #3252
5353
* [FEATURE] Query-frontend: added `compression` config to support results cache with compression. #3217
54+
* [FEATURE] Added support for applying Prometheus relabel configs on series received by the distributor. A `metric_relabel_configs` field has been added to the per-tenant limits configuration. #3329
5455
* [ENHANCEMENT] Allow to specify multiple comma-separated Cortex services to `-target` CLI option (or its respective YAML config option). For example, `-target=all,compactor` can be used to start Cortex single-binary with compactor as well. #3275
5556
* [ENHANCEMENT] Expose additional HTTP configs for the S3 backend client. New flag are listed below: #3244
5657
- `-blocks-storage.s3.http.idle-conn-timeout`

docs/configuration/config-file-reference.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ To specify which configuration file to load, pass the `-config.file` flag at the
2424
* `<string>`: a regular string
2525
* `<url>`: an URL
2626
* `<prefix>`: a CLI flag prefix based on the context (look at the parent configuration block to see which CLI flags prefix should be used)
27+
* `<relabel_config>`: a [prometheus relabeling configuration](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config).
2728
* `<time>`: a timestamp, with available formats: `2006-01-20` (midnight, local timezone), `2006-01-20T15:04` (local timezone), and RFC 3339 formats: `2006-01-20T15:04:05Z` (UTC) or `2006-01-20T15:04:05+07:00` (explicit timezone)
2829

2930
### Use environment variables in the configuration
@@ -2799,6 +2800,11 @@ The `limits_config` configures default and per-tenant limits imposed by Cortex s
27992800
# CLI flag: -distributor.ingestion-tenant-shard-size
28002801
[ingestion_tenant_shard_size: <int> | default = 0]
28012802
2803+
# List of metric relabel configurations. Note that in most situations, it is
2804+
# more effective to use metrics relabeling directly in the Prometheus server,
2805+
# e.g. remote_write.write_relabel_configs.
2806+
[metric_relabel_configs: <relabel_config...> | default = ]
2807+
28022808
# The maximum number of series for which a query can fetch samples from each
28032809
# ingester. This limit is enforced only in the ingesters (when querying samples
28042810
# not flushed to the storage yet) and it's a per-instance limit. This limit is

docs/configuration/config-file-reference.template

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ To specify which configuration file to load, pass the `-config.file` flag at the
2424
* `<string>`: a regular string
2525
* `<url>`: an URL
2626
* `<prefix>`: a CLI flag prefix based on the context (look at the parent configuration block to see which CLI flags prefix should be used)
27+
* `<relabel_config>`: a [prometheus relabeling configuration](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config).
2728
* `<time>`: a timestamp, with available formats: `2006-01-20` (midnight, local timezone), `2006-01-20T15:04` (local timezone), and RFC 3339 formats: `2006-01-20T15:04:05Z` (UTC) or `2006-01-20T15:04:05+07:00` (explicit timezone)
2829

2930
### Use environment variables in the configuration

pkg/distributor/distributor.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/prometheus/client_golang/prometheus/promauto"
1616
"github.com/prometheus/common/model"
1717
"github.com/prometheus/prometheus/pkg/labels"
18+
"github.com/prometheus/prometheus/pkg/relabel"
1819
"github.com/prometheus/prometheus/scrape"
1920
"github.com/weaveworks/common/httpgrpc"
2021
"github.com/weaveworks/common/instrument"
@@ -447,6 +448,11 @@ func (d *Distributor) Push(ctx context.Context, req *client.WriteRequest) (*clie
447448
latestSampleTimestampMs = util.Max64(latestSampleTimestampMs, ts.Samples[len(ts.Samples)-1].TimestampMs)
448449
}
449450

451+
if mrc := d.limits.MetricRelabelConfigs(userID); len(mrc) > 0 {
452+
l := relabel.Process(client.FromLabelAdaptersToLabels(ts.Labels), mrc...)
453+
ts.Labels = client.FromLabelsToLabelAdapters(l)
454+
}
455+
450456
// If we found both the cluster and replica labels, we only want to include the cluster label when
451457
// storing series in Cortex. If we kept the replica label we would end up with another series for the same
452458
// series we're trying to dedupe when HA tracking moves over to a different replica.

pkg/distributor/distributor_test.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/prometheus/client_golang/prometheus/testutil"
1919
"github.com/prometheus/common/model"
2020
"github.com/prometheus/prometheus/pkg/labels"
21+
"github.com/prometheus/prometheus/pkg/relabel"
2122
"github.com/stretchr/testify/assert"
2223
"github.com/stretchr/testify/require"
2324
"github.com/weaveworks/common/httpgrpc"
@@ -1720,6 +1721,80 @@ func TestSortLabels(t *testing.T) {
17201721
})
17211722
}
17221723

1724+
func TestDistributor_Push_Relabel(t *testing.T) {
1725+
ctx = user.InjectOrgID(context.Background(), "user")
1726+
1727+
type testcase struct {
1728+
inputSeries labels.Labels
1729+
expectedSeries labels.Labels
1730+
metricRelabelConfigs []*relabel.Config
1731+
}
1732+
1733+
cases := []testcase{
1734+
// No relabel config.
1735+
{
1736+
inputSeries: labels.Labels{
1737+
{Name: "__name__", Value: "foo"},
1738+
{Name: "cluster", Value: "one"},
1739+
},
1740+
expectedSeries: labels.Labels{
1741+
{Name: "__name__", Value: "foo"},
1742+
{Name: "cluster", Value: "one"},
1743+
},
1744+
},
1745+
{
1746+
inputSeries: labels.Labels{
1747+
{Name: "__name__", Value: "foo"},
1748+
{Name: "cluster", Value: "one"},
1749+
},
1750+
expectedSeries: labels.Labels{
1751+
{Name: "__name__", Value: "foo"},
1752+
{Name: "cluster", Value: "two"},
1753+
},
1754+
metricRelabelConfigs: []*relabel.Config{
1755+
{
1756+
SourceLabels: []model.LabelName{"cluster"},
1757+
Action: relabel.DefaultRelabelConfig.Action,
1758+
Regex: relabel.DefaultRelabelConfig.Regex,
1759+
TargetLabel: "cluster",
1760+
Replacement: "two",
1761+
},
1762+
},
1763+
},
1764+
}
1765+
1766+
for _, tc := range cases {
1767+
var err error
1768+
var limits validation.Limits
1769+
flagext.DefaultValues(&limits)
1770+
limits.MetricRelabelConfigs = tc.metricRelabelConfigs
1771+
1772+
ds, ingesters, r := prepare(t, prepConfig{
1773+
numIngesters: 2,
1774+
happyIngesters: 2,
1775+
numDistributors: 1,
1776+
shardByAllLabels: true,
1777+
limits: &limits,
1778+
})
1779+
defer stopAll(ds, r)
1780+
1781+
// Push the series to the distributor
1782+
req := mockWriteRequest(tc.inputSeries, 1, 1)
1783+
_, err = ds[0].Push(ctx, req)
1784+
require.NoError(t, err)
1785+
1786+
// Since each test pushes only 1 series, we do expect the ingester
1787+
// to have received exactly 1 series
1788+
for i := range ingesters {
1789+
timeseries := ingesters[i].series()
1790+
assert.Equal(t, 1, len(timeseries))
1791+
for _, v := range timeseries {
1792+
assert.Equal(t, tc.expectedSeries, client.FromLabelAdaptersToLabels(v.Labels))
1793+
}
1794+
}
1795+
}
1796+
}
1797+
17231798
func countMockIngestersCalls(ingesters []mockIngester, name string) int {
17241799
count := 0
17251800
for i := 0; i < len(ingesters); i++ {

pkg/util/validation/limits.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"flag"
66
"time"
77

8+
"github.com/prometheus/prometheus/pkg/relabel"
9+
810
"github.com/cortexproject/cortex/pkg/util/flagext"
911
)
1012

@@ -46,6 +48,7 @@ type Limits struct {
4648
EnforceMetadataMetricName bool `yaml:"enforce_metadata_metric_name"`
4749
EnforceMetricName bool `yaml:"enforce_metric_name"`
4850
IngestionTenantShardSize int `yaml:"ingestion_tenant_shard_size"`
51+
MetricRelabelConfigs []*relabel.Config `yaml:"metric_relabel_configs,omitempty" doc:"nocli|description=List of metric relabel configurations. Note that in most situations, it is more effective to use metrics relabeling directly in the Prometheus server, e.g. remote_write.write_relabel_configs."`
4952

5053
// Ingester enforced limits.
5154
// Series
@@ -372,6 +375,11 @@ func (o *Overrides) EvaluationDelay(userID string) time.Duration {
372375
return o.getOverridesForUser(userID).RulerEvaluationDelay
373376
}
374377

378+
// MetricRelabelConfigs returns the metric relabel configs for a given user.
379+
func (o *Overrides) MetricRelabelConfigs(userID string) []*relabel.Config {
380+
return o.getOverridesForUser(userID).MetricRelabelConfigs
381+
}
382+
375383
// RulerTenantShardSize returns shard size (number of rulers) used by this tenant when using shuffle-sharding strategy.
376384
func (o *Overrides) RulerTenantShardSize(userID string) int {
377385
return o.getOverridesForUser(userID).RulerTenantShardSize

tools/doc-generator/parser.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,16 @@ func parseConfig(block *configBlock, cfg interface{}, flags map[uintptr]*flag.Fl
193193
if err != nil {
194194
return nil, errors.Wrapf(err, "config=%s.%s", t.PkgPath(), t.Name())
195195
}
196+
if fieldFlag == nil {
197+
block.Add(&configEntry{
198+
kind: "field",
199+
name: fieldName,
200+
required: isFieldRequired(field),
201+
fieldDesc: getFieldDescription(field, ""),
202+
fieldType: fieldType,
203+
})
204+
continue
205+
}
196206

197207
block.Add(&configEntry{
198208
kind: "field",
@@ -243,6 +253,8 @@ func getFieldType(t reflect.Type) (string, error) {
243253
return "string", nil
244254
case "flagext.StringSliceCSV":
245255
return "string", nil
256+
case "[]*relabel.Config":
257+
return "relabel_config...", nil
246258
}
247259

248260
// Fallback to auto-detection of built-in data types
@@ -297,6 +309,9 @@ func getFieldType(t reflect.Type) (string, error) {
297309
}
298310

299311
func getFieldFlag(field reflect.StructField, fieldValue reflect.Value, flags map[uintptr]*flag.Flag) (*flag.Flag, error) {
312+
if isAbsentInCLI(field) {
313+
return nil, nil
314+
}
300315
fieldPtr := fieldValue.Addr().Pointer()
301316
fieldFlag, ok := flags[fieldPtr]
302317
if !ok {
@@ -395,6 +410,10 @@ func isFieldHidden(f reflect.StructField) bool {
395410
return getDocTagFlag(f, "hidden")
396411
}
397412

413+
func isAbsentInCLI(f reflect.StructField) bool {
414+
return getDocTagFlag(f, "nocli")
415+
}
416+
398417
func isFieldRequired(f reflect.StructField) bool {
399418
return getDocTagFlag(f, "required")
400419
}

0 commit comments

Comments
 (0)