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

First commit prometheus middleware. #1022

Merged
merged 1 commit into from
Jan 16, 2017
Merged

Conversation

enxebre
Copy link
Contributor

@enxebre enxebre commented Jan 6, 2017

I started to look at this so I wanted to get familiar with the source code.
This is a trivial prometheus middleware based on https://github.com/zbindenren/negroni-prometheus

It could be an starting point for #839
I'm happy to continue working on this if there's interest on this feature.

As we've agreed on some points and this is taking shape I'll add a progress tracker here:

  • Discuss design
    First commit prometheus middleware. #1022 (comment)
  • Define Metrics middleware and Interface
  • Create implementation for Prometheus
  • Agree on basic labels
  • Ability to choose exporter at run time (flag)
    --web.metrics.prometheus
  • Support configurable params for exporters at runtime
    --web.metrics.prometheus.Buckets="100,200"
  • Update Docs

@emilevauge
Copy link
Member

ping @SantoDE :)

@SantoDE
Copy link
Collaborator

SantoDE commented Jan 7, 2017

Hey @enxebre ,

thank's a lot for your contribution :-)

You're idea is in the right direction and a good starting point, but I'm looking for a slightly different approach to not "vendor lock in" on just prometheus. If you're interested in going on further here, let us have a short talk on Slack and / or drop me an email [email protected]. I guess, together we can move that further fast :)

/cc: @containous/traefik

Regards,
Santo

@ralphtheninja
Copy link

It would be awesome to have some kind of support for Prometheus. Looking forward to what you can come up with! :)

@bamarni
Copy link
Contributor

bamarni commented Jan 8, 2017

There seems to be a nice generic package for that purpose : https://github.com/rcrowley/go-metrics

Looking forward to this too 👍

@enxebre
Copy link
Contributor Author

enxebre commented Jan 9, 2017

cool, lets discuss on slack then. @SantoDE

@enxebre
Copy link
Contributor Author

enxebre commented Jan 9, 2017

Potential solutions for a more generic approach could be:

It'd be nice to have:

  • Basic Grafana Traefik dashboard
  • Kubernetes Helm chart example for Traefik + Prometheus + Grafana dashboard

@enxebre
Copy link
Contributor Author

enxebre commented Jan 10, 2017

Added a second commit with a more vendor neutral approach using github.com/go-kit/kit/metrics b9bc081

@@ -101,7 +101,7 @@ import:
- package: k8s.io/client-go
version: ^v1.5.0
- package: github.com/gogo/protobuf
version: 0.3
version: "0.3"
Copy link
Member

Choose a reason for hiding this comment

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

why any " here ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think it was just glide adding that

)

var (
dflBuckets = []float64{300, 1200, 5000}
Copy link
Contributor

Choose a reason for hiding this comment

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

300ms seems high already, as those are cumulative how about adding some lower ones too?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe 100?

start := time.Now()
prw := &responseRecorder{rw, http.StatusOK}
next(prw , r)
labels := []string{ "code", strconv.Itoa(prw.StatusCode()), "method", r.Method, "path", r.URL.Path }
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure the request path is suitable as a label for a generic metrics handler, because it has an unbounded cardinality.

Copy link
Collaborator

Choose a reason for hiding this comment

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

We should probably use something else then the path. Sadly, I can't think of something yet. Will update once I have an idea :-)

[]string{"code", "method", "path"},
)

if len(buckets) == 0 {
Copy link
Contributor

Choose a reason for hiding this comment

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

At the moment this check seems unneeded as the buckets are not configurable.

@@ -680,6 +682,8 @@ func (server *Server) loadConfig(configurations configs, globalConfiguration Glo
}

var negroni = negroni.New()
metricsMiddlewareBackend := middlewares.NewMetricsWrapper(middlewares.NewPrometheus(frontend.Backend))
Copy link
Collaborator

Choose a reason for hiding this comment

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

We should make it configurable (for the feature), which specific "exporter" to use. WDYT @enxebre?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yup, I agree

)

var (
dflBuckets = []float64{300, 1200, 5000}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe 100?

- package: github.com/mvdan/xurls
- package: github.com/prometheus/client_golang
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we need the client, if we're going with go-kit? Shouldn't go-kit bring it's dependencies?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right

start := time.Now()
prw := &responseRecorder{rw, http.StatusOK}
next(prw , r)
labels := []string{ "code", strconv.Itoa(prw.StatusCode()), "method", r.Method, "path", r.URL.Path }
Copy link
Collaborator

Choose a reason for hiding this comment

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

We should probably use something else then the path. Sadly, I can't think of something yet. Will update once I have an idea :-)

@SantoDE
Copy link
Collaborator

SantoDE commented Jan 11, 2017

Hey @enxebre,

thank's for your contribution 👍

I'm okay with using go-kit as it's supporting labels (over e.g. go-metrics, which doesn't). I also like the approach of having a metrics middleware which then uses the specific "exporter". I added some feedback to the Review.

@enxebre enxebre force-pushed the prometheus branch 3 times, most recently from aaa7f46 to 2fe8c82 Compare January 11, 2017 14:05
@enxebre
Copy link
Contributor Author

enxebre commented Jan 11, 2017

Added a new commit with updated tests, docs and runtime config for monitoring.
Some explicit logic for the promhttp.handler on web.go and for instantiating the monitoring implementation on server.go is intentionally left there. This can be hidden in the metricsWrapper in the future once we have a least two implementations so we can see the right abstractions. 2fe8c82

@SantoDE
Copy link
Collaborator

SantoDE commented Jan 12, 2017

Hey @enxebre,

that looks good 👍 When do you will have time to fix the conficts?

@enxebre enxebre force-pushed the prometheus branch 2 times, most recently from e49be88 to 34402bb Compare January 12, 2017 11:33
@enxebre
Copy link
Contributor Author

enxebre commented Jan 12, 2017

Conflicts fixed and green build now

@SantoDE
Copy link
Collaborator

SantoDE commented Jan 12, 2017

Hey @enxebre ,

Thanks again! Really :)

LGTM 👼

/cc: @containous/traefik

Could you please squash and rebase your branch? Thanks! :-)

@bamarni
Copy link
Contributor

bamarni commented Jan 12, 2017

How about the path label, isn't the backend label enough? My concern with it is that it might create a lot of time series, if for instance backends contain dynamic placeholders such as order_id or user_id, which is very common.

IMO a label based on the request path is more suited for backends which have a well-known and limited set of paths, thus should be registered explicitly at application level as we cannot assume it at the proxy level.

@SantoDE
Copy link
Collaborator

SantoDE commented Jan 12, 2017

@enxebre Yeah, I would probably keep out the path labels (as mentioned in the review). I don't see any real advantage just yet. A label per backend should be fine :)

@enxebre
Copy link
Contributor Author

enxebre commented Jan 13, 2017

Never mind, just found the way :) should be ok now.
This is supported now --web.metrics.prometheus.Buckets="100,200"
Thoughts?

@enxebre enxebre force-pushed the prometheus branch 6 times, most recently from d8444da to 0d2d3b6 Compare January 16, 2017 09:38
Copy link
Member

@emilevauge emilevauge left a comment

Choose a reason for hiding this comment

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

Thanks a lot @enxebre , that's awesome :)
LGTM (once tests pass, you need to go fmt)
/cc @containous/traefik

@enxebre
Copy link
Contributor Author

enxebre commented Jan 16, 2017

Cool, green build now 🙌

@SantoDE
Copy link
Collaborator

SantoDE commented Jan 16, 2017

Merged 👍 👼

Thanks a lot @enxebre

@SantoDE SantoDE merged commit 483ef48 into traefik:master Jan 16, 2017

```bash
$ traefik --web.metrics.prometheus --web.metrics.prometheus.buckets="100,300"
```

Choose a reason for hiding this comment

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

Is this all it takes? No steps needed to configure prometheus?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ralphtheninja this is how Traefik exposes metrics to Prometheus. As a separate thing you must run Prometheus and configure it to scrape the Traefik endpoint . It be nice to have a Kubernetes Helm chart example for Traefik + Prometheus + Grafana dashboard showing the whole thing

Choose a reason for hiding this comment

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

Ok. Thanks for info!

@brian-brazil
Copy link

You're idea is in the right direction and a good starting point, but I'm looking for a slightly different approach to not "vendor lock in" on just prometheus.

The way we handle this in Prometheus is that the client libraries aren't tied to Prometheus, so you can for example instrument with Prometheus and output to Graphite without ever running a Prometheus server. Thus we recommend to use the standard Prometheus client libraries where possible.


const (
reqsName = "requests_total"
latencyName = "request_duration_milliseconds"

Choose a reason for hiding this comment

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

The Prometheus convention is to use seconds.

// NewPrometheus returns a new prometheus Metrics implementation.
func NewPrometheus(name string, config *types.Prometheus) *Prometheus {
var m Prometheus
m.reqsCounter = prometheus.NewCounterFrom(

Choose a reason for hiding this comment

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

This is already covered by the latency histogram's _count timeseries.

It's recommend not to split out latency results by success/failure/status code as that's very commonly misinterpreted. What I'd suggest is removing the code label from the latency metric.

It's also unwise on cardinality grounds to have too many labels on a histogram, as it already has one itself.

@SantoDE
Copy link
Collaborator

SantoDE commented Jan 16, 2017

Hey @brian-brazil,

thank's a lot for your valuable feedback :-) Do you have any more? Shall we have a talk on Slack? Or are you even interested in putting up a PR on your?

This one is already merged so we need to put up another one anyways.

Cheers

@brian-brazil
Copy link

I saw it mentioned it on twitter, so I put in a few comments on common issues around instrumentation.

From another look, the only other comment I'd have would be to prefix your metrics with traefik_ or whatever else makes sense for you. In theory (and it's very much a theory) metric names should be globally unique, and you want a user to have an idea of what sort of request it is and where it's being measured from.

@enxebre
Copy link
Contributor Author

enxebre commented Jan 16, 2017

Hey @brian-brazil thanks for the feedback!
I'll create a follow up PR to address the comments about instrumentation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants