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

Prometheus remote write endpoint #16609

Merged
merged 34 commits into from
Mar 12, 2020
Merged

Conversation

ChrsMark
Copy link
Member

@ChrsMark ChrsMark commented Feb 26, 2020

What does this PR do?

This PR implements Prometheus remote write as a new metricset.

Why is it important?

How to test this PR locally

Start metricbeat

  1. Enable metricbeat Prometheus module
  2. config:
- module: prometheus
  metricsets: ["remote_write"]
  host: "localhost"
  port: "9201"
  1. start metricbeat and expect to see:
2020-02-27T17:49:14.724+0200	DEBUG	[module]	module/wrapper.go:189	Starting metricSetWrapper[module=prometheus, name=remote_write, host=]
2020-02-27T17:49:14.724+0200	INFO	remote_write/remote_write.go:92	Starting HTTP server on localhost:9201

Sending metrics from Prometheus

Use this forked project: https://github.com/ChrsMark/dockprom.

  1. Download the project.
  2. Add to `prometheus/prometheus.yml:
remote_write:
  - url: "http://host.docker.internal:9201/write"
  1. docker-compose up

With that metrics should be collected by metricbeat and reported.

Extra: Test with https

  1. Generate ssl keys:
openssl genrsa -out my_key.key 2048
openssl req -x509 -new -nodes -key my_key.key -sha256 -days 1825 -out my_key.pem
  1. Configure Prometheus to use https for remote_write: (edit /prometheus/prometheus.yml of the above docker-compose project)
remote_write:
  - url: "https://host.docker.internal:9201/write"
    tls_config:
        cert_file: "/etc/prometheus/my_key.pem"
        key_file: "/etc/prometheus/my_key.key"
        insecure_skip_verify: true
  1. Move my_key.key and my_key.pem under /prometheus folder in the docker-compose project.
  2. Configure Metricbeat to start HTTPS server using my_key.key and my_key.pem.
- module: prometheus
  metricsets: ["remote_write"]
  host: "localhost"
  port: "9201"

  # Secure settings for the server using TLS/SSL:
  ssl.certificate: "/Users/some/pki/my_key.pem"
  ssl.key: "/Users/some/pki/my_key.key"

Related issues

Notes about changes

This PR changes HttpServer so as to accept handlerFunc from metricset on server initialisation. This makes it reusable from different metricsets and not only server metricset of http module. In order to confirm that server metricset of http module is not broken I run INTEGRATION_TESTS=1 BEAT_STRICT_PERMS=false nosetests ./module/http/test_http.py successfully which shows that functionality is preserved.

@ChrsMark ChrsMark added in progress Pull request is currently in progress. Metricbeat Metricbeat [zube]: In Progress Team:Integrations Label for the Integrations team Team:Platforms Label for the Integrations - Platforms team labels Feb 26, 2020
@ChrsMark ChrsMark requested a review from a team as a code owner February 26, 2020 15:40
@ChrsMark ChrsMark self-assigned this Feb 26, 2020
@ChrsMark ChrsMark changed the title [WIP] Prometheus remote endpoint [WIP] Prometheus remote write endpoint Feb 27, 2020
Signed-off-by: chrismark <chrismarkou92@gmail.com>
Signed-off-by: chrismark <chrismarkou92@gmail.com>
Signed-off-by: chrismark <chrismarkou92@gmail.com>
Signed-off-by: chrismark <chrismarkou92@gmail.com>
@ChrsMark
Copy link
Member Author

ChrsMark commented Feb 28, 2020

For this we will need to vendor github.com/prometheus/prometheus/prompb. Would this be a problem for the go-modules-porting @kvch?

update: I guess I could wait for #15853 and make use of go modules.

@kvch
Copy link
Contributor

kvch commented Feb 28, 2020

If it is not urgent, please wait until go modules is merged. It is a huge effort to keep go-modules up to date, so I am pushing to get it in as soon as possible.

@ChrsMark
Copy link
Member Author

If it is not urgent, please wait until go modules is merged. It is a huge effort to keep go-modules up to date, so I am pushing to get it in as soon as possible.

Cool! No problem on waiting!

Signed-off-by: chrismark <chrismarkou92@gmail.com>
Signed-off-by: chrismark <chrismarkou92@gmail.com>
Signed-off-by: chrismark <chrismarkou92@gmail.com>
Signed-off-by: chrismark <chrismarkou92@gmail.com>
@ChrsMark
Copy link
Member Author

ChrsMark commented Mar 5, 2020

@exekias any feedback at this point would be more than welcome. It is becoming quite big but we can split it in smaller PRs like so:

  1. first PR to add a working metricset
  2. refactoring code to utilize http helpers
  3. system tests

Copy link
Contributor

@exekias exekias left a comment

Choose a reason for hiding this comment

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

Left a few comments, I like where things are going!

@@ -0,0 +1,577 @@
# -*- coding: utf-8 -*-
Copy link
Contributor

Choose a reason for hiding this comment

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

is this file needed?

Copy link
Member Author

Choose a reason for hiding this comment

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

metricbeat/module/prometheus/_meta/config.yml Show resolved Hide resolved
return p.labels.String()
}

func samplesToEvents(metrics model.Samples) map[string]mb.Event {
Copy link
Contributor

Choose a reason for hiding this comment

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

I understand Samples will always be untyped right? For example, we won't be able to differenciate a histogram metric from a gauge

Copy link
Member Author

Choose a reason for hiding this comment

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

Prometheus sends data with writerequest using this proto. Not sure if we can retrieve the type of the metric in an easy way 🤔

Copy link
Member

Choose a reason for hiding this comment

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

I guess this is going to be an issue with histograms? Should we do a best-effort guess if it includes metrics with some special labels and/or suffixes?

Comment on lines 62 to 95
if !math.IsNaN(val) && !math.IsInf(val, 0) {
events = append(events, PromEvent{
data: common.MapStr{
name: val,
},
labels: labels,
timestamp: metric.Timestamp.Time(),
})
}
}

// join metrics with same labels in a single event
eventList := map[string]mb.Event{}

for _, promEvent := range events {
labelsHash := promEvent.LabelsHash()
if _, ok := eventList[labelsHash]; !ok {
eventList[labelsHash] = mb.Event{
ModuleFields: common.MapStr{
"metrics": common.MapStr{},
},
}

// Add labels
if len(promEvent.labels) > 0 {
eventList[labelsHash].ModuleFields["labels"] = promEvent.labels
}
}

// Not checking anything here because we create these maps some lines before
e := eventList[labelsHash]
e.Timestamp = promEvent.timestamp
e.ModuleFields["metrics"].(common.MapStr).Update(promEvent.data)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

this can probably be optimized, by storing events already indexed by label hash, so only an insert is needed

// was an error creating the MetricSet then an error will be returned. The
// returned MetricSet must also implement either EventFetcher or EventsFetcher
// (but not both).
type HandlerFactory func(h *HttpServer) func(writer http.ResponseWriter, req *http.Request)
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you really need a factory? The metricset is already initializing the server, so you could pass a private metricset.handleFunc which implements func(writer http.ResponseWriter, req *http.Request)

Comment on lines 61 to 63
func (h *HttpEvent) SetEvent(event common.MapStr) {
h.event = event
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Having that you are now passing a custom handler when initializing (which I think is a good idea), do you need an HttpEvent construct anymore? This was used to pass data between a generic HTTP server and the metricset. But now the metricset is in control of the requests, and can directly build and send events to the output. That should make things simpler, WDYT?

Signed-off-by: chrismark <chrismarkou92@gmail.com>
@ChrsMark ChrsMark changed the title [WIP] Prometheus remote write endpoint Prometheus remote write endpoint Mar 6, 2020
Signed-off-by: chrismark <chrismarkou92@gmail.com>
@ChrsMark ChrsMark requested a review from exekias March 6, 2020 10:48
@ChrsMark
Copy link
Member Author

ChrsMark commented Mar 6, 2020

Note: In order to confirm that server metricset of http module is not broken I run INTEGRATION_TESTS=1 BEAT_STRICT_PERMS=false nosetests ./module/http/test_http.py successfully which shows that functionality is preserved.

metricbeat/module/prometheus/docker-compose.yml Outdated Show resolved Hide resolved
metricbeat/helper/server/http/http_test.go Outdated Show resolved Hide resolved
metricbeat/helper/server/http/http.go Outdated Show resolved Hide resolved
metricbeat/helper/server/http/http.go Outdated Show resolved Hide resolved
metricbeat/module/http/server/data.go Outdated Show resolved Hide resolved
metricbeat/module/http/server/server.go Outdated Show resolved Hide resolved
metricbeat/module/prometheus/remote_write/remote_write.go Outdated Show resolved Hide resolved
Signed-off-by: chrismark <chrismarkou92@gmail.com>
Signed-off-by: chrismark <chrismarkou92@gmail.com>
metricbeat/helper/server/http/http.go Show resolved Hide resolved
metricbeat/helper/server/http/http.go Outdated Show resolved Hide resolved
metricbeat/module/prometheus/remote_write/data.go Outdated Show resolved Hide resolved
return p.labels.String()
}

func samplesToEvents(metrics model.Samples) map[string]mb.Event {
Copy link
Member

Choose a reason for hiding this comment

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

I guess this is going to be an issue with histograms? Should we do a best-effort guess if it includes metrics with some special labels and/or suffixes?

@ChrsMark
Copy link
Member Author

Comments addressed. Let's see if CI is happy.

Signed-off-by: chrismark <chrismarkou92@gmail.com>
Signed-off-by: chrismark <chrismarkou92@gmail.com>
Signed-off-by: chrismark <chrismarkou92@gmail.com>
@ChrsMark
Copy link
Member Author

@jsoriano regarding #16609 (comment), this is something we discussed with @exekias already and guessing would be the way to go when we will decide to adopt histograms for this metricset.

I think that comments have been addressed 🙂 .

@ChrsMark ChrsMark added test-plan Add this PR to be manual test plan and removed test-plan Add this PR to be manual test plan labels Mar 11, 2020
else
# this works only on Mac envs
HOST_DOMAIN="host.docker.internal"
break
Copy link
Member

Choose a reason for hiding this comment

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

Why only breaking in this case?

Copy link
Member Author

Choose a reason for hiding this comment

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

We want to keep trying to reach host.docker.internal otherwise to fallback to 0.0.0.0.

@ChrsMark ChrsMark added needs_backport PR is waiting to be backported to other branches. v7.7.0 labels Mar 12, 2020
@ChrsMark ChrsMark merged commit a6f406b into elastic:master Mar 12, 2020
ChrsMark added a commit to ChrsMark/beats that referenced this pull request Mar 12, 2020
@ChrsMark ChrsMark removed the needs_backport PR is waiting to be backported to other branches. label Mar 12, 2020
@exekias exekias added the test-plan Add this PR to be manual test plan label Mar 27, 2020
@andresrc andresrc added the test-plan-added This PR has been added to the test plan label Mar 31, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in progress Pull request is currently in progress. Metricbeat Metricbeat Team:Integrations Label for the Integrations team Team:Platforms Label for the Integrations - Platforms team test-plan Add this PR to be manual test plan test-plan-added This PR has been added to the test plan v7.7.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Metricbeat] Prometheus Remote Endpoint
5 participants