рдкреНрд░реЛрдореЗрдерд┐рдпрд╕рдХреЛ рд╕рд╛рде рдлреНрд▓рд╛рд╕реНрдХ рдорд╛рдЗрдХреНрд░реЛрд╕рд░реНрднрд┐рд╕реЗрд╕рд╣рд░реВ рдирд┐рдЧрд░рд╛рдиреА рдЧрд░реНрджреИ

рдХреЛрдбрдХреЛ рдХреЗрд╣реА рд▓рд╛рдЗрдирд╣рд░реВ рд░ рддрдкрд╛рдИрдВрдХреЛ рдЕрдиреБрдкреНрд░рдпреЛрдЧрд▓реЗ рдореЗрдЯреНрд░рд┐рдХреНрд╕ рдЙрддреНрдкрдиреНрди рдЧрд░реНрджрдЫ, рд╡рд╛рд╣!

prometheus_ рдХрд╕рд░реА рдХрд╛рдо рдЧрд░реНрджрдЫ рднрдиреЗрд░ рдмреБрдЭреНрдирдХреЛ рд▓рд╛рдЧрд┐рдлреНрд▓рд╛рд╕реНрдХ_рдирд┐рд░реНрдпрд╛рддрдХ рдПрдХ рдиреНрдпреВрдирддрдо рдЙрджрд╛рд╣рд░рдг рдкрд░реНрдпрд╛рдкреНрдд рдЫ:

from flask import Flask
from prometheus_flask_exporter import PrometheusMetrics

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

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

рддрдкрд╛рдИрдВрд▓реЗ рд╕реБрд░реБ рдЧрд░реНрдирдХреЛ рд▓рд╛рдЧрд┐ рдпрддрд┐ рдорд╛рддреНрд░ рдЖрд╡рд╢реНрдпрдХ рдЫ! рдкреНрд░рд╛рд░рдореНрднрд┐рдХрддрд╛рдХреЛ рд▓рд╛рдЧрд┐ рдЖрдпрд╛рдд рд░ рд░реЗрдЦрд╛ рдердкреЗрд░ PrometheusMetrics, рддрдкрд╛рдИрдВрд▓реЗ рдореЗрдЯреНрд░рд┐рдХреНрд╕ рдкреНрд░рд╛рдкреНрдд рдЧрд░реНрдиреБрд╣реБрдиреЗрдЫ рдЕрдиреБрд░реЛрдз рдЕрд╡рдзрд┐ ╨╕ рдЕрдиреБрд░реЛрдз рдХрд╛рдЙрдиреНрдЯрд░рд╣рд░реВ, рдЕрдиреНрддрд┐рдо рдмрд┐рдиреНрджреБрдорд╛ рдкреНрд░рджрд░реНрд╢рд┐рдд /рдореЗрдЯреНрд░рд┐рдХреНрд╕ рдлреНрд▓рд╛рд╕реНрдХ рдЕрдиреБрдкреНрд░рдпреЛрдЧ рдпреЛ рджрд░реНрддрд╛ рдЧрд░рд┐рдПрдХреЛ рдЫ, рд╕рд╛рдереИ рддрдкрд╛рдИрдВрд▓реЗ рдЖрдзрд╛рд░рдмрд╛рдЯ рдкреНрд░рд╛рдкреНрдд рдЧрд░реНрдиреБрд╣реБрдиреЗ рд╕рдмреИ рдкреВрд░реНрд╡рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдореЗрдЯреНрд░рд┐рдХрд╣рд░реВ рдкреНрд░реЛрдореЗрдерд┐рдпрд╕ рдЧреНрд░рд╛рд╣рдХ рдкреБрд╕реНрддрдХрд╛рд▓рдп.

рддрдкрд╛рдИрдВ рдкрд╛рдЙрди рд╕рдХреНрдиреБрд╣реБрдиреНрдЫ рдЙрджрд╛рд╣рд░рдг рдкреНрд░рдпреЛрдЧ рдЧрд░реНрди рд╕рдЬрд┐рд▓реЛ GitHub рднрдгреНрдбрд╛рд░рдорд╛ рдЬреБрди рдЙрджрд╛рд╣рд░рдг рдЪрд▓рд╛рдЙрдБрдЫ Prometheus ╨╕ рдЧреНрд░рд╛рдлрд╛рдирд╛ рдПрдХ рдбреЗрдореЛ рдЕрдиреБрдкреНрд░рдпреЛрдЧрдХреЛ рд╕рд╛рде рдореЗрдЯреНрд░рд┐рдХреНрд╕ рдЙрддреНрдкрдиреНрди рдЧрд░реНрди рдЬреБрди рдпреЛ рдЬрд╕реНрддреЛ рджреЗрдЦрд┐рдиреНрдЫ:

рдкреНрд░реЛрдореЗрдерд┐рдпрд╕рдХреЛ рд╕рд╛рде рдлреНрд▓рд╛рд╕реНрдХ рдорд╛рдЗрдХреНрд░реЛрд╕рд░реНрднрд┐рд╕реЗрд╕рд╣рд░реВ рдирд┐рдЧрд░рд╛рдиреА рдЧрд░реНрджреИ

рддрдкрд╛рдИрд▓реЗ рд╕реВрдЪрдХрд╣рд░реВрдХреЛ рд╕реВрдЪреА рдкрдирд┐ рдкрд╛рдЙрдиреБрд╣реБрдиреЗрдЫ рдкрдвреНрдиреБрд╣реЛрд╕реН рдбреНрдпрд╛рд╕рдмреЛрд░реНрдбрдорд╛ рджреЗрдЦрд╛ рдкрд░реНрдиреЗ рдЙрджрд╛рд╣рд░рдгрд╣рд░реВ, рдбреНрдпрд╛рд╕рдмреЛрд░реНрдбрд╣рд░реВ рднрд░реНрдиреЗ рдкреНрд░реЛрдореЗрдерд┐рдпрд╕ рдХреНрд╡реЗрд░реАрд╣рд░реВ рд╕рд╣рд┐рддред

рд╕рдорд╛рдпреЛрдЬрди

рдкреБрд╕реНрддрдХрд╛рд▓рдпрдорд╛ рдзреЗрд░реИ рдХрдиреНрдлрд┐рдЧрд░реЗрд╕рди рд╡рд┐рдХрд▓реНрдкрд╣рд░реВ рдЫрдиреН, рд╣реЗрд░реНрдиреБрд╣реЛрд╕реН рдкрдвреНрдиреБрд╣реЛрд╕реН рд╕рдВрдХреНрд╖рд┐рдкреНрдд рд╡реНрдпрд╛рдЦреНрдпрд╛ рд╕рдВрдЧ рддрд┐рдиреАрд╣рд░реВрд▓рд╛рдИ рдкрд░рд┐рдпреЛрдЬрдирд╛ рдЙрджрд╛рд╣рд░рдгрд╣рд░реВред

рдЖрдзрд╛рд░рднреВрдд рдХрдиреНрдлрд┐рдЧрд░реЗрд╕рди рдорд╛рдерд┐ рджреЗрдЦрд╛рдЗрдПрдХреЛ рдЫред рдХреЗрд╡рд▓ рдПрдЙрдЯрд╛ рдЙрджрд╛рд╣рд░рдг рд╕рд┐рд░реНрдЬрдирд╛ рдЧрд░реНрдиреБрд╣реЛрд╕реН 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{рд╕рдВрдХрд▓рди = "10002", рд╕реНрдерд┐рддрд┐ = "200"}, рд╕рд╛рдереИ рддрдкрд╛рдИрдВрд▓реЗ рдкреВрд░реНрд╡рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдкреБрд╕реНрддрдХрд╛рд▓рдпрдмрд╛рдЯ рдкреВрд░реНрд╡рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдореЗрдЯреНрд░рд┐рдХреНрд╕ (рдпрд╕ рдЙрджрд╛рд╣рд░рдгрдорд╛ рдкреНрд░рддреНрдпреЗрдХ рдЕрдиреНрддрд┐рдо рдмрд┐рдиреНрджреБрдХреЛ рд▓рд╛рдЧрд┐) рдкреНрд░рд╛рдкреНрдд рдЧрд░реНрдиреБрд╣реБрдиреЗрдЫ:

  • flask_http_request_duration_seconds - рд╡рд┐рдзрд┐, рдорд╛рд░реНрдЧ рд░ рд╕реНрдерд┐рддрд┐ рджреНрд╡рд╛рд░рд╛ рд╕рдмреИ рдлреНрд▓рд╛рд╕реНрдХ рдЕрдиреБрд░реЛрдзрд╣рд░реВрдХреЛ рд▓рд╛рдЧрд┐ рд╕реЗрдХреЗрдиреНрдбрдорд╛ HTTP рдЕрдиреБрд░реЛрдз рдЕрд╡рдзрд┐

  • flask_http_request_total - рд╡рд┐рдзрд┐рд╣рд░реВ рд░ рд╕реНрдерд┐рддрд┐рд╣рд░реВрджреНрд╡рд╛рд░рд╛ HTTP рдЕрдиреБрд░реЛрдзрд╣рд░реВрдХреЛ рдХреБрд▓ рд╕рдВрдЦреНрдпрд╛

рддреНрдпрд╣рд╛рдБ рд╡рд┐рд╢рд┐рд╖реНрдЯ рдЕрдиреНрддрд┐рдо рдмрд┐рдиреНрджреБрд╣рд░реВрдХреЛ рдЯреНрд░реНрдпрд╛рдХрд┐рдЩ рдЫреЛрдбреНрди, рдЕрддрд┐рд░рд┐рдХреНрдд рдкреВрд░реНрд╡рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдореЗрдЯреНрд░рд┐рдХреНрд╕ рд▓рдЧ рдЧрд░реНрди рд╡рд╛ рдорд╛рдерд┐ рд╕реВрдЪреАрдмрджреНрдз рддреА рдЫреЛрдбреНрдиреЗ рд╡рд┐рдХрд▓реНрдкрд╣рд░реВ рдЫрдиреН, рд╡рд╛ рдзреЗрд░реИ рдЕрдиреНрддрдмрд┐рдиреНрджреБрд╣рд░реВрдорд╛ рд╕рдорд╛рди рдЕрдиреБрдХреВрд▓рди рдореЗрдЯреНрд░рд┐рдХ рд▓рд╛рдЧреВ рдЧрд░реНрдиреБрд╣реЛрд╕реНред рдЬрд╛рдБрдЪ рдЧрд░реНрдиреБрд╣реЛрд╕реН рдкрдвреНрдиреБрд╣реЛрд╕реН рдХреЗ рдЙрдкрд▓рдмреНрдз рдЫ рд╣реЗрд░реНрдирдХреЛ рд▓рд╛рдЧрд┐ рдкрд░рд┐рдпреЛрдЬрдирд╛ред

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 рдЬрд╕реНрддрд╛ рд▓реЛрдХрдкреНрд░рд┐рдп рдмрд╣реБрдкреНрд░рдХреНрд░рд┐рдпрд╛ рдкреБрд╕реНрддрдХрд╛рд▓рдпрд╣рд░реВрдХреЛ рд▓рд╛рдЧрд┐ рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рд╡рд┐рд╕реНрддрд╛рд░рд╣рд░реВ рдЫрдиреНред рддрдкрд╛рдИрд▓реЗ рдмрд╣реБрдкреНрд░рдХреНрд░рд┐рдпрд╛ рд╕рд╣рд┐рдд рд▓рдХреНрд╖рд┐рдд рдкреНрд░рдпреЛрдЧрдХрд╛ рд╕рд╛рдирд╛ рдЙрджрд╛рд╣рд░рдгрд╣рд░реВ рдкрдирд┐ рдлреЗрд▓рд╛ рдкрд╛рд░реНрди рд╕рдХреНрдиреБрд╣реБрдиреНрдЫред

рдореЗрдЯреНрд░рд┐рдХреНрд╕ рд╕рдВрдЧреНрд░рд╣

рдорд╛рдерд┐ рдЙрд▓реНрд▓реЗрдЦ рдЧрд░рд┐рдП рдЕрдиреБрд╕рд╛рд░, рдкреБрд╕реНрддрдХрд╛рд▓рдпрд▓реЗ рдкреВрд░реНрд╡рдирд┐рд░реНрдзрд╛рд░рд┐рдд рд░реВрдкрдорд╛ рдЕрдиреНрддрд┐рдо рдмрд┐рдиреНрджреБ рдкреНрд░рджрд╛рди рдЧрд░реНрджрдЫ /рдореЗрдЯреНрд░рд┐рдХреНрд╕ рдлреНрд▓рд╛рд╕реНрдХ рдЕрдиреБрдкреНрд░рдпреЛрдЧрдорд╛, рдЬрд╕рд▓реЗ рд▓рдХреНрд╖реНрдпрдХреЛ рд░реВрдкрдорд╛ рд╕реЗрд╡рд╛ рдЧрд░реНрди рд╕рдХреНрдЫ рдкреНрд░реЛрдореЗрдерд┐рдпрд╕ рдирд┐рд░реНрдорд╛рдгрдХрд░реНрддрд╛.

рдорд╛рдерд┐рдХреЛ рдбреНрдпрд╛рд╕рдмреЛрд░реНрдб рдЙрджрд╛рд╣рд░рдгрдорд╛, рддрдкрд╛рдИрдВрд▓реЗ рдЖрдлреНрдиреЛ рдкреНрд░реЛрдореЗрдерд┐рдпрд╕рд▓рд╛рдИ рдпрд╕ рдХрдиреНрдлрд┐рдЧрд░реЗрд╕рдирдХреЛ рд╕рд╛рде рдкреВрд░реНрд╡рдирд┐рд░реНрдзрд╛рд░рд┐рдд рд╕реЗрдЯрд┐рдЩрд╣рд░реВрдХреЛ рд╕рд╛рде рдлреНрд▓рд╛рд╕реНрдХ рдЕрдиреБрдкреНрд░рдпреЛрдЧрдорд╛ рд▓рдХреНрд╖рд┐рдд рдЧрд░реНрди рд╕рдХреНрдиреБрд╣реБрдиреНрдЫ:

scrape_configs:
  - job_name: 'example'

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

рдорд╛ рдкреВрд░рд╛ рдЙрджрд╛рд╣рд░рдг рд╣реЗрд░реНрдиреБрд╣реЛрд╕реН GitHub рднрдгреНрдбрд╛рд░рд╣рд░реВред рдпрд╕рд▓реЗ рдорд╛рдиреНрджрдЫ рдХрд┐ рдкреНрд░реЛрдореЗрдерд┐рдпрд╕рд▓реЗ рддрдкрд╛рдЗрдБрдХреЛ рдлреНрд▓рд╛рд╕реНрдХ рдЕрдиреБрдкреНрд░рдпреЛрдЧ рдЙрджрд╛рд╣рд░рдгрд╣рд░реВ рдлреЗрд▓рд╛ рдкрд╛рд░реНрди рд╕рдХреНрдЫ 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(рдкреЛрд░реНрдЯ)рдлрд░рдХ HTTP рдкреЛрд░реНрдЯрдорд╛ рдпреЛ рдЕрдиреНрддреНрдп рдмрд┐рдиреНрджреБ рдЦреЛрд▓реНрдирдХреЛ рд▓рд╛рдЧрд┐, 5099 рдорд╛рдерд┐рдХреЛ рдЙрджрд╛рд╣рд░рдгрдорд╛ред рд╡реИрдХрд▓реНрдкрд┐рдХ рд░реВрдкрдорд╛, рдпрджрд┐ рддрдкрд╛рдИрдВ рдПрдЙрдЯреИ рдлреНрд▓рд╛рд╕реНрдХ рдЕрдиреБрдкреНрд░рдпреЛрдЧрдорд╛ рдЕрдиреНрддрд┐рдо рдмрд┐рдиреНрджреБ рднрдПрдХреЛрдорд╛ рдЦреБрд╕реА рд╣реБрдиреБрд╣реБрдиреНрдЫ, рддрд░ рддрдкрд╛рдИрдВрд▓реЗ рдпрд╕рдХреЛ рдорд╛рд░реНрдЧ рдкрд░рд┐рд╡рд░реНрддрди рдЧрд░реНрди рдЖрд╡рд╢реНрдпрдХ рдЫред /рдореЗрдЯреНрд░рд┐рдХреНрд╕, рддрдкрд╛рдИрд▓реЗ рдЕрд░реНрдХреЛ URI рд▓рд╛рдИ рдкрде рдкреНрдпрд╛рд░рд╛рдорд┐рдЯрд░рдХреЛ рд░реВрдкрдорд╛ рдкрд╛рд╕ рдЧрд░реНрди рд╕рдХреНрдиреБрд╣реБрдиреНрдЫ, рд╡рд╛ рдкреНрд░рдпреЛрдЧ рдЧрд░реНрди рд╕рдХреНрдиреБрд╣реБрдиреНрдЫ register_endpoint(..)рдпрд╕рд▓рд╛рдИ рдкрдЫрд┐ рд╕реНрдерд╛рдкрдирд╛ рдЧрд░реНрдиред

рд╕рдиреНрджрд░реНрдн

рдпрджрд┐ рддрдкрд╛рдЗрдБ рдпрд╕рд▓рд╛рдИ рдкреНрд░рдпрд╛рд╕ рдЧрд░реНрдиреЗ рдирд┐рд░реНрдгрдп рдЧрд░реНрдиреБрд╣реБрдиреНрдЫ рднрдиреЗ, GitHub рдорд╛ рдореБрджреНрджрд╛ рдЦреЛрд▓реНрди рд╕реНрд╡рддрдиреНрддреНрд░ рдорд╣рд╕реБрд╕ рдЧрд░реНрдиреБрд╣реЛрд╕реН рд╡рд╛ рддрдкрд╛рдЗрдБрдХреЛ рдЯрд┐рдкреНрдкрдгреА, рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛, рд░ рд╕реБрдЭрд╛рд╡рд╣рд░реВ рдЫреЛрдбреНрдиреБрд╣реЛрд╕реН!

рдзрдиреНрдпрд╡рд╛рдж!

рд╕реНрд░реЛрдд: www.habr.com

рдПрдХ рдЯрд┐рдкреНрдкрдгреА рдердкреНрди