-
Notifications
You must be signed in to change notification settings - Fork 162
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
1,626 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
# Example dashboard | ||
|
||
![Example dashboard](dashboard.png) | ||
|
||
With this example, you can bring up a simple Flask application that has 4 endpoints, plus one that always returns an error response. The 4 main endpoints simply return `ok` after some random delay. | ||
|
||
The example Compose project also includes a random load generator, a Prometheus server, and a Grafana instance with preconfigured dashboards, feeding data from Prometheus, which is also configured to scrape metrics from the example application automatically. All you need to do is: | ||
|
||
```shell | ||
$ docker-compose up -d | ||
``` | ||
|
||
Then open [http://localhost:3000/d/_eX4mpl3](http://localhost:3000/d/_eX4mpl3) in your browser to see the dashboard. You can edit each panel to check what metric it uses, but here is a quick rundown of what's going on in there. | ||
|
||
### Requests per second | ||
|
||
Number of successful Flask requests per second. Shown per path. | ||
|
||
``` | ||
rate( | ||
flask_http_request_duration_seconds_count{status="200"}[30s] | ||
) | ||
``` | ||
|
||
### Errors per second | ||
|
||
Number of failed (non HTTP 200) responses per second. | ||
|
||
``` | ||
sum( | ||
rate( | ||
flask_http_request_duration_seconds_count{status!="200"}[30s] | ||
) | ||
) | ||
``` | ||
|
||
### Total requests per minute | ||
|
||
The total number of requests measured over one minute intervals. Shown per HTTP response status code. | ||
|
||
``` | ||
increase( | ||
flask_http_request_total[1m] | ||
) | ||
``` | ||
|
||
### Average response time [30s] | ||
|
||
The average response time measured over 30 seconds intervals for successful requests. Shown per path. | ||
|
||
``` | ||
rate( | ||
flask_http_request_duration_seconds_sum{status="200"}[30s] | ||
) | ||
/ | ||
rate( | ||
flask_http_request_duration_seconds_count{status="200"}[30s] | ||
) | ||
``` | ||
|
||
### Requests under 250ms | ||
|
||
The percentage of successful requests finished within 1/4 second. Shown per path. | ||
|
||
``` | ||
increase( | ||
flask_http_request_duration_seconds_bucket{status="200",le="0.25"}[30s] | ||
) | ||
/ ignoring (le) | ||
increase( | ||
flask_http_request_duration_seconds_count{status="200"}[30s] | ||
) | ||
``` | ||
|
||
### Request duration [s] - p50 | ||
|
||
The 50th percentile of request durations over the last 30 seconds. In other words, half of the requests finish in (min/max/avg) these times. Shown per path. | ||
|
||
``` | ||
histogram_quantile( | ||
0.5, | ||
rate( | ||
flask_http_request_duration_seconds_bucket{status="200"}[30s] | ||
) | ||
) | ||
``` | ||
|
||
### Request duration [s] - p90 | ||
|
||
The 90th percentile of request durations over the last 30 seconds. In other words, 90 percent of the requests finish in (min/max/avg) these times. Shown per path. | ||
|
||
``` | ||
histogram_quantile( | ||
0.9, | ||
rate( | ||
flask_http_request_duration_seconds_bucket{status="200"}[30s] | ||
) | ||
) | ||
``` | ||
|
||
### Memory usage | ||
|
||
The memory usage of the Flask app. Based on data from the underlying Prometheus client library, not Flask specific. | ||
|
||
``` | ||
process_resident_memory_bytes{job="example"} | ||
``` | ||
|
||
### CPU usage | ||
|
||
The CPU usage of the Flask app as measured over 30 seconds intervals. Based on data from the underlying Prometheus client library, not Flask specific. | ||
|
||
``` | ||
rate( | ||
process_cpu_seconds_total{job="example"}[30s] | ||
) | ||
``` | ||
|
||
## Cleaning up | ||
|
||
Don't forget to shut the demo down, once finished: | ||
|
||
```shell | ||
$ docker-compose down -v | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
FROM python:3-alpine | ||
|
||
ADD requirements.txt /tmp/requirements.txt | ||
|
||
RUN pip install -r /tmp/requirements.txt | ||
|
||
ADD app.py /var/server/app.py | ||
|
||
CMD python /var/server/app.py |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import time | ||
import random | ||
|
||
from flask import Flask | ||
from prometheus_flask_exporter import PrometheusMetrics | ||
|
||
|
||
app = Flask(__name__) | ||
PrometheusMetrics(app) | ||
|
||
|
||
endpoints = ('one', 'two', 'three', 'four', 'five', 'error') | ||
|
||
|
||
@app.route('/one') | ||
def first_route(): | ||
time.sleep(random.random() * 0.2) | ||
return 'ok' | ||
|
||
|
||
@app.route('/two') | ||
def the_second(): | ||
time.sleep(random.random() * 0.4) | ||
return 'ok' | ||
|
||
|
||
@app.route('/three') | ||
def test_3rd(): | ||
time.sleep(random.random() * 0.6) | ||
return 'ok' | ||
|
||
|
||
@app.route('/four') | ||
def fourth_one(): | ||
time.sleep(random.random() * 0.8) | ||
return 'ok' | ||
|
||
|
||
@app.route('/error') | ||
def oops(): | ||
return ':(', 500 | ||
|
||
|
||
if __name__ == '__main__': | ||
app.run('0.0.0.0', 5000, threaded=True) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
flask | ||
prometheus_flask_exporter |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
version: '2' | ||
services: | ||
|
||
# a sample app with metrics enabled | ||
app: | ||
build: | ||
context: app | ||
stop_signal: SIGKILL | ||
ports: | ||
- 5000:5000 | ||
|
||
# dumb, random load generator | ||
generator: | ||
build: | ||
context: generator | ||
stop_signal: SIGKILL | ||
|
||
# the Prometheus server | ||
prometheus: | ||
image: prom/prometheus:v2.2.1 | ||
volumes: | ||
- ./prometheus/config.yml:/etc/prometheus/prometheus.yml | ||
ports: | ||
- 9090:9090 | ||
|
||
# Grafana for visualization | ||
grafana: | ||
image: grafana/grafana:5.1.0 | ||
volumes: | ||
- ./grafana/config.ini:/etc/grafana/grafana.ini | ||
- ./grafana/datasource.yaml:/etc/grafana/provisioning/datasources/default.yaml | ||
- ./grafana/dashboard.yaml:/etc/grafana/provisioning/dashboards/default.yaml | ||
- ./grafana/dashboards:/var/lib/grafana/dashboards | ||
ports: | ||
- 3000:3000 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
FROM python:3-alpine | ||
|
||
ADD requirements.txt /tmp/requirements.txt | ||
|
||
RUN pip install -r /tmp/requirements.txt | ||
|
||
ADD generate_events.py /var/app/generator.py | ||
|
||
CMD python /var/app/generator.py |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import time | ||
import random | ||
import threading | ||
|
||
import requests | ||
|
||
endpoints = ('one', 'two', 'three', 'four', 'error') | ||
|
||
|
||
def run(): | ||
while True: | ||
try: | ||
target = random.choice(endpoints) | ||
requests.get("http://app:5000/%s" % target, timeout=1) | ||
|
||
except: | ||
pass | ||
|
||
|
||
if __name__ == '__main__': | ||
for _ in range(4): | ||
thread = threading.Thread(target=run) | ||
thread.setDaemon(True) | ||
thread.start() | ||
|
||
while True: | ||
time.sleep(1) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
requests |
Oops, something went wrong.