Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NETOBSERV-497 allow direct metrics pipeline #266

Merged
merged 10 commits into from
Sep 1, 2022

Conversation

jotak
Copy link
Member

@jotak jotak commented Jul 26, 2022

This PoC creates a new "SimpleProm" Encode stage that directly extracts
metrics from flows without an intermediate Aggregate stage

The rationale is that Prometheus already manages labels aggregations.
It leaves more responsibilities (and more power) to the query side.
For instance, aggregations such as sum/avg etc. would be performed in
PromQL at query time rather than upstream.

Pipelines to generate are simpler as they don't need an "Aggregate" stage, and the prom-encode stage itself has less parameters, e.g.:

	enrichedStage.SimpleEncodePrometheus("prometheus", api.SimplePromEncode{
		Port:   int(b.desired.PrometheusPort),
		Prefix: "netobserv_",
		Metrics: []api.SimplePromMetricsItem{{
			Name:      "bytes_total",
			RecordKey: "Bytes",
			Type:      "counter",
			Labels:    []string{"SrcK8S_Namespace", "DstK8S_Namespace"},
		}, {
			Name:      "packets_total",
			RecordKey: "Packets",
			Type:      "counter",
			Labels:    []string{"SrcK8S_Namespace", "DstK8S_Namespace"},
		}},
	})

@jotak jotak marked this pull request as draft July 26, 2022 12:03
@jotak
Copy link
Member Author

jotak commented Jul 26, 2022

This pipeline (cf go code above) results in creating this metrics, with both src/dest namespaces as labels :

Capture d’écran du 2022-07-26 13-48-36

@codecov-commenter
Copy link

codecov-commenter commented Jul 26, 2022

Codecov Report

Merging #266 (d83b532) into main (aa342a2) will increase coverage by 0.36%.
The diff coverage is 84.07%.

@@            Coverage Diff             @@
##             main     #266      +/-   ##
==========================================
+ Coverage   67.34%   67.70%   +0.36%     
==========================================
  Files          73       74       +1     
  Lines        4281     4357      +76     
==========================================
+ Hits         2883     2950      +67     
- Misses       1214     1219       +5     
- Partials      184      188       +4     
Flag Coverage Δ
unittests 67.70% <84.07%> (+0.36%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
pkg/api/encode_prom.go 100.00% <ø> (ø)
pkg/pipeline/encode/encode_prom.go 76.13% <81.97%> (+0.99%) ⬆️
pkg/confgen/flowlogs2metrics_config.go 75.00% <90.00%> (+1.15%) ⬆️
pkg/confgen/confgen.go 49.20% <100.00%> (ø)
pkg/confgen/encode.go 61.90% <100.00%> (+11.90%) ⬆️
pkg/operational/metrics/metrics.go 52.50% <100.00%> (ø)
pkg/pipeline/utils/timed_cache.go 100.00% <100.00%> (ø)
pkg/test/prom.go 100.00% <100.00%> (ø)
pkg/confgen/grafana_jsonnet.go 47.47% <0.00%> (+3.03%) ⬆️

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

@eranra
Copy link
Collaborator

eranra commented Jul 26, 2022

@jotak how many metrics will be sent to Prometheus? don't you think that this will exhaust Prometheus with too many metrics? the idea of aggregate was to pre-process metrics so that the only thing Prometheus will need to handle is the aggregates. For example in the case of the above Prometheus will get namespace metrics without the need to worry about the underly metrics. Maybe I am missing something here ... we can find time to talk tomorrow maybe?

@jotak
Copy link
Member Author

jotak commented Jul 26, 2022

@eranra I already proposed a call on thursday (check your mail) :)

@eranra
Copy link
Collaborator

eranra commented Jul 26, 2022

@jotak I see that now ... can we move that to an earlier time ... morning EU time --- this overlaps multiple meetings for me

@jotak
Copy link
Member Author

jotak commented Jul 27, 2022

btw we would also need to check how to combine that with confgen ; maybe adding a config flag in metrics definitions to tell if we want to use the "direct prom" approach or the "aggregate + prom" approach.

@eranra
Copy link
Collaborator

eranra commented Jul 27, 2022

@jotak another thing that I want to think about is if there is a way to combine the code so we will not have two options that are doing almost the same thing based on two code bases,. Maybe there is a way to split the code / reuse the code so that we do not duplicate the code but split the functions so that they are used in both the direct version and the split version

@jotak
Copy link
Member Author

jotak commented Jul 27, 2022

Also, for the record: we need to check if there is an internal cache in prom client to clean from time to time (similar to the expiry mechanism implemented in FLP caches)

@KalmanMeth
Copy link
Collaborator

KalmanMeth commented Jul 28, 2022

In encode_prom, we implemented a cache to clean up items that are inactive, and we encapsulated it in utils.timed_cache.
The Cleanup callback function in encode_prom.go does the cleanup in the prometheus client.

@KalmanMeth
Copy link
Collaborator

The encode_prom stage does not depend on the extract_aggregate stage. The confgenerator builds a config that uses extract_aggregate that feeds encode_prom , but encode_prom can be used by itself without aggregation. Once you add in the cache to SimpleEncodeProm it becomes essentially the existing encode_prom.

@jotak
Copy link
Member Author

jotak commented Jul 28, 2022

@KalmanMeth yes, I see that now. Maybe with the exception of the histogram values, right?

@jotak
Copy link
Member Author

jotak commented Jul 28, 2022

@KalmanMeth I think the existing PromEncode is almost working well as a independent stage, like what I was trying to do, I see however two issues:

  • The PromMetricsFilter in PromEncode api prevents from exposing any metrics we'd like
  • Histogram are not working

I believe these are changes added later, without having in mind that the prom-encode stage could be used independently?

In my last commit I've added twice a test of exposed metrics, see eb99bc0 ; one time for the existing PromEncode, one time for the new PoC prom encode.
I would expect the test should work on both, but it only works on the SimpleProm one because of the two issues mentioned.

@jotak
Copy link
Member Author

jotak commented Jul 28, 2022

So, rather than having a new Encode implementation, I will try to fix the existing one.

@jotak jotak force-pushed the poc-simple-prom branch 3 times, most recently from 57936bb to 1140493 Compare July 29, 2022 09:32
@jotak
Copy link
Member Author

jotak commented Jul 29, 2022

last commit: now focusing on fixing the existing PromEncode to work fine without Aggregation. Also spending some time on optimization (e.g. decreasing number of allocs)

@jotak
Copy link
Member Author

jotak commented Jul 29, 2022

I see another issue with the current Aggregation stage: we cannot mix "direct" PromEncode stages and "non-direct" (ie. following Aggregation) in confgen: we would need to fork before Aggregation, something like :

transform -> aggregate
aggregate -> prom1
transform -> prom2

But confgen doesn't support that. Also, having 2 prom stages means we need to deal with port conflicts.
So at the moment, it's something like all or nothing : either we have only "aggregate+prom" in the pipeline, or we have only "direct-prom", but not a mix of them.

@jotak
Copy link
Member Author

jotak commented Aug 2, 2022

FYI, jira created : https://issues.redhat.com/browse/NETOBSERV-497

Copy link
Collaborator

@ronensc ronensc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks promising. It feels like the code is cleaner.
@jotak I'm amazed by how fast you've done this work 🤩

Comment on lines +181 to +185
exposed := test.ReadExposedMetrics(t)

for _, expected := range tt.expectedEncode {
require.Contains(t, exposed, expected)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍
I like the idea of checking the exposed metrics rather than PrevRecords

Comment on lines +88 to +89
tc.mu.RLock()
defer tc.mu.RUnlock()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch

pkg/pipeline/encode/encode_prom.go Outdated Show resolved Hide resolved
pkg/pipeline/encode/encode_prom.go Outdated Show resolved Hide resolved
pkg/pipeline/encode/encode_prom.go Outdated Show resolved Hide resolved
Comment on lines +70 to +76
var errorsCounter = operationalMetrics.NewCounterVec(prometheus.CounterOpts{
Name: "encode_prom_errors",
Help: "Total errors during metrics generation",
}, []string{"error", "metric", "key"})

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

reg := prometheus.NewRegistry()
prometheus.DefaultRegisterer = reg
prometheus.DefaultGatherer = reg
http.DefaultServeMux = http.NewServeMux()
Copy link
Collaborator

@ronensc ronensc Aug 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the purpose of setting http.DefaultServeMux = http.NewServeMux()?

Copy link
Member Author

@jotak jotak Aug 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is because in encode_prom, we run http.Handle("/metrics", promhttp.Handler())
which creates a handler on the default, global mux/router. When this is called several times, an error would be fired (something like "cannot register /metrics, route already exists").

I don't remember exactly in which case that happened, maybe it was just in the benchmark I created below.

BTW it also shows that the prom_encode stage will need to be refactored if I some point we want to be able to define more than one prom-encodes in a pipeline. I mentioned that in this jira: https://issues.redhat.com/browse/NETOBSERV-498

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jotak thanks. I tried commenting out that line and see what happens. It failed on the second unit test of encode_prom_test.go because of the multiple registration that you have described. So resetting the DefaultServeMux to a new instance on each unit test solves this problem.

@ronensc ronensc requested a review from KalmanMeth August 2, 2022 11:07
@jotak jotak changed the title PoC simpler metrics pipeline NETOBSERV-497 allow direct metrics pipeline Aug 2, 2022
@jotak jotak marked this pull request as ready for review August 2, 2022 12:52
require.Contains(t, exposed, `test_packets_total{dstIP="10.0.0.1",srcIP="20.0.0.2"} 2`)
require.Contains(t, exposed, `test_packets_total{dstIP="30.0.0.3",srcIP="10.0.0.1"} 2`)
require.Contains(t, exposed, `test_latency_seconds_bucket{dstIP="10.0.0.1",srcIP="20.0.0.2",le="0.025"} 0`)
require.Contains(t, exposed, `test_latency_seconds_bucket{dstIP="10.0.0.1",srcIP="20.0.0.2",le="0.05"} 1`)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where are these buckets defined? Are they a default?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, prom client defines default: DefBuckets = []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10}

This hasn't changed from the previous implementation


for i := 0; i < b.N; i++ {
prom.Encode(hundredFlows())
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jotak I learned a lot from this re-write of encode_prom and its test.

@Amoghrd
Copy link
Contributor

Amoghrd commented Aug 23, 2022

/ok-to-test

This PoC creates a new "SimpleProm" Encode stage that directly extracts
metrics from flows without an intermediate Aggregate stage

The rationale is that Prometheus already manages labels aggregations.
It leaves more responsibilities (and more power) to the query side.
For instance, aggregations such as sum/avg etc. would be performed in
PromQL at query time rather than upstream.
TTL test doesn't pass on the existing encode_prom
Added type "AggHistogram" to differentiate histograms initiated from
Aggregate stage from histograms to create by only by prom.

Make the Filter mechanism optional (it doesn't make sense when metrics
aren't prepared from the Aggregation stage)

In confgen, detect automatically when AggHistogram should be used, so
that the user doesn't have to worry about it.

Modify aggregate_prom_test to read metrics output from the HTTP handler
rather than prevrecords.

Fixed race in timed_cache (only tests could be impacted)

Also, some performance improvement in EncodeProm (benchmark: divide by 2
ns/op, almost by 3 allocs/op) by having a more efficient cache key
building, removing prevrecords, and a few other things
Similar to the "count" operation in Aggregation stage, but that can be
done directly via promencode
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants