ื ื™ื˜ื•ืจ ืžื™ืงืจื•-ืฉื™ืจื•ืชื™ Flask ืขื Prometheus

ื›ืžื” ืฉื•ืจื•ืช ืงื•ื“ ื•ื”ืืคืœื™ืงืฆื™ื” ืฉืœืš ืžื™ื™ืฆืจืช ืžื“ื“ื™ื, ื•ื•ืื•!

ืขืœ ืžื ืช ืœื”ื‘ื™ืŸ ืื™ืš ืคืจื•ืžืชืื•ืก_ ืขื•ื‘ื“ื‘ืงื‘ื•ืง_ืœื™ืฆื•ืืŸ ืžืกืคื™ืงื” ื“ื•ื’ืžื” ืžื™ื ื™ืžืœื™ืช:

from flask import Flask
from prometheus_flask_exporter import PrometheusMetrics

app = Flask(__name__)
metrics = PrometheusMetrics(app)

@app.route('/')
def main():
    return 'OK'

ื–ื” ื›ืœ ืžื” ืฉืืชื” ืฆืจื™ืš ื›ื“ื™ ืœื”ืชื—ื™ืœ! ืขืœ ื™ื“ื™ ื”ื•ืกืคืช ื™ื™ื‘ื•ื โ€‹โ€‹ื•ืฉื•ืจื” ืœืืชื—ื•ืœ PrometheusMetrics, ืชืงื‘ืœ ืžื“ื“ื™ื ืžืฉืš ื”ื‘ืงืฉื” ะธ ื“ืœืคืงื™ ื‘ืงืฉื”, ืžื•ืฆื’ ื‘ื ืงื•ื“ืช ื”ืงืฆื” /ืžื˜ืจื™ื ืืคืœื™ืงืฆื™ื™ืช Flask ืฉื‘ื” ื”ื™ื ืจืฉื•ืžื”, ื›ืžื• ื’ื ื›ืœ ืžื“ื“ื™ ื‘ืจื™ืจืช ื”ืžื—ื“ืœ ืฉืืชื” ืžืงื‘ืœ ืžื”ื‘ืกื™ืก ืกืคืจื™ื™ืช ืœืงื•ื—ื•ืช ืคืจื•ืžืชืื•ืก.

ืืชื” ื™ื›ื•ืœ ืœืžืฆื•ื ื“ื•ื’ืžื” ืงืœื” ืœืฉื™ืžื•ืฉ ื‘ืžืื’ืจ GitHub ืฉืžืคืขื™ืœ ืืช ื”ืžื•ืคืข ืคืจื•ืžืชืื•ืก ะธ ื’ืจืคื ื” ื™ื—ื“ ืขื ืืคืœื™ืงืฆื™ื™ืช ื”ื“ื’ืžื” ืœื™ืฆื™ืจืช ืžื“ื“ื™ื ืฉื™ืจืื• ื‘ืขืจืš ื›ืš:

ื ื™ื˜ื•ืจ ืžื™ืงืจื•-ืฉื™ืจื•ืชื™ Flask ืขื Prometheus

ืชืžืฆื ื’ื ืจืฉื™ืžื” ืฉืœ ืื™ื ื“ื™ืงื˜ื•ืจื™ื ื‘ README ื“ื•ื’ืžืื•ืช ื”ืžื•ืคื™ืขื•ืช ื‘ืœื•ื— ื”ืžื—ื•ื•ื ื™ื, ื™ื—ื“ ืขื ืฉืื™ืœืชื•ืช ืคืจื•ืžืชืื•ืก ื”ืžืื›ืœืกื•ืช ืืช ืœื•ื—ื•ืช ื”ืžื—ื•ื•ื ื™ื.

ื”ืชืืžื”

ื™ืฉื ืŸ ืืคืฉืจื•ื™ื•ืช ืชืฆื•ืจื” ืจื‘ื•ืช ื‘ืกืคืจื™ื”, ืชืจืื” README ืคืจื•ื™ืงื˜ ื“ื•ื’ืžืื•ืช ืฉืœื”ื ืขื ื”ืกื‘ืจ ืงืฆืจ.

ื”ืชืฆื•ืจื” ื”ื‘ืกื™ืกื™ืช ืžื•ืฆื’ืช ืœืžืขืœื”. ืคืฉื•ื˜ ืฆื•ืจ ืžื•ืคืข PrometheusMetrics, ื‘ื•ืื• ื ืงืจื ืœื–ื” ืžื“ื“ื™ื, ื•ืœืื—ืจ ืžื›ืŸ ื”ืฉืชืžืฉ ื‘ื• ื›ื“ื™ ืœื”ื’ื“ื™ืจ ืืช ื”ืžื“ื“ื™ื ื”ื ื•ืกืคื™ื ืฉื‘ืจืฆื•ื ืš ืœืืกื•ืฃ ืขืœ ื™ื“ื™ ืขื™ื˜ื•ืจ ื”ืคื•ื ืงืฆื™ื•ืช:

  • @metrics.counter(..)

  • @metrics.gauge(..)

  • @metrics.summary(..)

  • @metrics.histogram(..)

ืžื•ื ื™ื ืกื•ืคืจื™ื ืฉื™ื—ื•ืช, ื•ืื—ืจื™ื ืื•ืกืคื™ื ืžื“ื“ื™ื ืขืœ ืกืžืš ืžืฉืš ื”ืฉื™ื—ื•ืช ื”ืœืœื•. ืืชื” ื™ื›ื•ืœ ืœื”ื’ื“ื™ืจ ืชื•ื•ื™ื•ืช ืขื‘ื•ืจ ื›ืœ ืื—ื“ ืžืืœื”, ืื•ืœื™ ื‘ืืžืฆืขื•ืช ืžืืคื™ื™ื ื™ ื‘ืงืฉื” ืื• ืชื’ื•ื‘ื”. ืœื“ื•ื’ืžื”:

from flask import Flask, request
from prometheus_flask_exporter import PrometheusMetrics

app = Flask(__name__)

# group by endpoint rather than path
metrics = PrometheusMetrics(app, group_by='endpoint')

@app.route('/collection/:collection_id/item/:item_id')
@metrics.counter(
    'cnt_collection', 'Number of invocations per collection', labels={
        'collection': lambda: request.view_args['collection_id'],
        'status': lambda resp: resp.status_code
    })
def get_item_from_collection(collection_id, item_id):
    pass

ื‘ื“ื•ื’ืžื” ืœืขื™ืœ, ืœื—ื™ืฆื” ืขืœ ื ืงื•ื“ืช ื”ืงืฆื” /collection/10002/item/76 ื™ื’ืจื•ื ืœืžื•ื ื” ืœื”ื’ื“ื™ืœ, ืœืžืฉืœ cnt_collection{collection = "10002", status = "200"}, ื‘ื ื•ืกืฃ ืชืงื‘ืœ ืืช ืžื“ื“ื™ ื‘ืจื™ืจืช ื”ืžื—ื“ืœ (ืขื‘ื•ืจ ื›ืœ ื ืงื•ื“ืช ืงืฆื” ื‘ื“ื•ื’ืžื” ื–ื•) ืžืกืคืจื™ื™ืช ื‘ืจื™ืจืช ื”ืžื—ื“ืœ:

  • flask_http_request_duration_seconds - ืžืฉืš ื‘ืงืฉืช HTTP ื‘ืฉื ื™ื•ืช ืขื‘ื•ืจ ื›ืœ ื‘ืงืฉื•ืช Flask ืœืคื™ ืฉื™ื˜ื”, ื ืชื™ื‘ ื•ืžืฆื‘

  • flask_http_request_total - ื”ืžืกืคืจ ื”ื›ื•ืœืœ ืฉืœ ื‘ืงืฉื•ืช HTTP ืœืคื™ ืฉื™ื˜ื•ืช ื•ืกื˜ื˜ื•ืกื™ื

ื™ืฉื ืŸ ืืคืฉืจื•ื™ื•ืช ืœื“ืœื’ ืขืœ ืžืขืงื‘ ืื—ืจ ื ืงื•ื“ื•ืช ืงืฆื” ืกืคืฆื™ืคื™ื•ืช, ืœืจืฉื•ื ืžื“ื“ื™ ื‘ืจื™ืจืช ืžื—ื“ืœ ื ื•ืกืคื™ื ืื• ืœื“ืœื’ ืขืœ ืืœื” ื”ืžืคื•ืจื˜ื™ื ืœืžืขืœื”, ืื• ืœื”ื—ื™ืœ ืืช ืื•ืชื• ืžื“ื“ ืžื•ืชืื ืื™ืฉื™ืช ืขืœ ืžืกืคืจ ื ืงื•ื“ื•ืช ืงืฆื”. ืœื‘ื“ื•ืง README ืคืจื•ื™ืงื˜ ื›ื“ื™ ืœืจืื•ืช ืžื” ื–ืžื™ืŸ.

app = Flask(__name__)
metrics = PrometheusMetrics(app)

@app.route('/')
def main():
    pass  # requests tracked by default

@app.route('/skip')
@metrics.do_not_track()
def skip():
    pass  # default metrics are not collected

# custom metric to be applied to multiple endpoints
common_counter = metrics.counter(
    'by_endpoint_counter', 'Request count by endpoints',
    labels={'endpoint': lambda: request.endpoint}
)

@app.route('/common/one')
@common_counter
def endpoint_one():
    pass  # tracked by the custom and the default metrics

@app.route('/common/two')
@common_counter
def endpoint_two():
    pass  # also tracked by the custom and the default metrics

# register additional default metrics
metrics.register_default(
    metrics.counter(
        'by_path_counter', 'Request count by request paths',
        labels={'path': lambda: request.path}
    )
)

ืœืกืคืจื™ื™ื” ื™ืฉ ื”ืจื—ื‘ื•ืช ื ื•ื—ื•ืช ืœืกืคืจื™ื•ืช ืžืจื•ื‘ื•ืช ืขื™ื‘ื•ื“ ืคื•ืคื•ืœืจื™ื•ืช ื›ื’ื•ืŸ uWSGI ื•-Gunicorn. ืืชื” ื™ื›ื•ืœ ื’ื ืœืžืฆื•ื ื“ื•ื’ืžืื•ืช ืงื˜ื ื•ืช ืœืžืงืจื™ ืฉื™ืžื•ืฉ ืžืžื•ืงื“ื™ื, ื›ื•ืœืœ ืจื™ื‘ื•ื™ ืขื™ื‘ื•ื“ื™ื.

ืื•ืกืฃ ืžื“ื“ื™ื

ื›ืคื™ ืฉื”ื•ื–ื›ืจ ืœืขื™ืœ, ื”ืกืคืจื™ื™ื” ืžืกืคืงืช ื ืงื•ื“ืช ืงืฆื” ื›ื‘ืจื™ืจืช ืžื—ื“ืœ /ืžื˜ืจื™ื ื‘ื™ื™ืฉื•ื Flask, ืฉื™ื›ื•ืœ ืœืฉืžืฉ ืžื˜ืจื” ืขื‘ื•ืจ ื‘ื•ื ื” ืคืจื•ืžืชืื•ืก.

ื‘ื“ื•ื’ืžื” ืฉืœ ืœื•ื— ื”ืžื—ื•ื•ื ื™ื ืœืขื™ืœ, ืชื•ื›ืœ ืœืžืงื“ ืืช ื”-Prometheus ืฉืœืš ืœืืคืœื™ืงืฆื™ื™ืช Flask ืขื ื”ื’ื“ืจื•ืช ื‘ืจื™ืจืช ืžื—ื“ืœ ืขื ื”ืชืฆื•ืจื” ื”ื–ื•:

scrape_configs:
  - job_name: 'example'

    dns_sd_configs:
      - names: ['app']
        port: 5000
        type: A
        refresh_interval: 5s

ืจืื” ื“ื•ื’ืžื” ืžืœืื” ื‘ ืžืื’ืจื™ GitHub. ื–ื” ืžื ื™ื— ืฉ-Prometheus ื™ื›ื•ืœ ืœืžืฆื•ื ืืช ืžื•ืคืขื™ ืืคืœื™ืงืฆื™ื™ืช Flask ืฉืœืš http://app:5000/metrics, ืฉื‘ื• ืฉื ื”ื“ื•ืžื™ื™ืŸ ืฉืœ ื”ืืคืœื™ืงืฆื™ื” ืขืฉื•ื™ ืœื”ื•ืคื™ืข ื‘ื›ืชื•ื‘ื•ืช IP ืžืจื•ื‘ื•ืช, ืœืžืฉืœ ื‘ืขืช ืจื™ืฆื” ืงื•ื‘ืจื ื˜ ืื• ื ื—ื™ืœ ื“ื•ืงืจ.

ืื ื—ืฉื™ืคืช ื ืงื•ื“ืช ื”ืงืฆื” ืฉืœ ื”ืžื“ื“ื™ื ื‘ืฆื•ืจื” ื–ื• ืœื ืžืชืื™ืžื” ืœืš, ืื•ืœื™ ื‘ื’ืœืœ ืฉืื™ื ืš ืจื•ืฆื” ืœืืคืฉืจ ื’ื™ืฉื” ื—ื™ืฆื•ื ื™ืช ืืœื™ื”, ืชื•ื›ืœ ืœื‘ื˜ืœ ืื•ืชื” ื‘ืงืœื•ืช ืขืœ ื™ื“ื™ ืžืขื‘ืจ ื ืชื™ื‘=ืื™ืŸ ื‘ืขืช ื™ืฆื™ืจืช ืžื•ืคืข PrometheusMetrics.

from flask import Flask, request
from prometheus_flask_exporter import PrometheusMetrics

app = Flask(__name__)
metrics = PrometheusMetrics(app, path=None)

...

metrics.start_http_server(5099)

ืื– ืืชื” ื™ื›ื•ืœ ืœื”ืฉืชืžืฉ start_http_server(port)ื›ื“ื™ ืœืคืชื•ื— ืืช ื ืงื•ื“ืช ื”ืงืฆื” ื”ื–ื• ื‘ื™ืฆื™ืืช HTTP ืื—ืจืช, 5099 ื‘ื“ื•ื’ืžื” ืœืขื™ืœ. ืœื—ืœื•ืคื™ืŸ, ืื ืืชื” ืžืจื•ืฆื” ืžื›ืš ืฉื ืงื•ื“ืช ื”ืงืฆื” ื ืžืฆืืช ื‘ืื•ืชื• ื™ื™ืฉื•ื Flask, ืืš ืขืœื™ืš ืœืฉื ื•ืช ืืช ื”ื ืชื™ื‘ ืฉืœื” ืž /ืžื˜ืจื™ื, ืืชื” ื™ื›ื•ืœ ืœื”ืขื‘ื™ืจ URI ืื—ืจ ื›ืคืจืžื˜ืจ ื”ื ืชื™ื‘, ืื• ืœื”ืฉืชืžืฉ register_endpoint(..)ืœื”ืชืงื™ืŸ ืื•ืชื• ืžืื•ื—ืจ ื™ื•ืชืจ.

ืชื–ื›ื•ืจ

ืื ืชื—ืœื™ื˜ ืœื ืกื•ืช ืืช ื–ื”, ืืœ ืชื”ืกืก ืœืคืชื•ื— ื‘ืขื™ื” ื‘-GitHub ืื• ืœื”ืฉืื™ืจ ืืช ื”ื”ืขืจื•ืช, ื”ืžืฉื•ื‘ ื•ื”ื”ืฆืขื•ืช ืฉืœืš!

ืชื•ื“ื” ืœืš!

ืžืงื•ืจ: www.habr.com

ื”ื•ืกืคืช ืชื’ื•ื‘ื”