• 15 апреля стартует «Курс «SQL-injection Master» ©» от команды The Codeby

    За 3 месяца вы пройдете путь от начальных навыков работы с SQL-запросами к базам данных до продвинутых техник. Научитесь находить уязвимости связанные с базами данных, и внедрять произвольный SQL-код в уязвимые приложения.

    На последнюю неделю приходится экзамен, где нужно будет показать свои навыки, взломав ряд уязвимых учебных сайтов, и добыть флаги. Успешно сдавшие экзамен получат сертификат.

    Запись на курс до 25 апреля. Получить промодоступ ...

Статья Монитор ресурсов через WEB, или мониторим свою VPS с помощью Grafana с самописными метриками

Какую задачу решаю?

Есть свой vps.
Есть желание самому создавать метрики и мониторить их.
Текущие реализации монитора ресурсов на vps мне не нравятся.
Хочется сделать так, чтоб ты вбил адрес в браузере и увидел некий "дайджест" по состоянию твоей машины.

Стек

  • Grafana
  • Python 3.9
  • Фреймворк prometheus_client для того, чтоб реализовывать свои метрики
  • prometheus DB
  • VPS
  • Свой домен
Настройка Grafana

Не буду расписывать тут как установить графану, ибо мануалов в сети более, чем достаточно. Например
Укажу лишь нюансы, которые могут понадобиться. Мне захотелось вывести графану в отдельный поддомен типа https://domain.com/grafs
Для этого в конфиге nginx настраиваем обратный прокси:


Bash:
nano /etc/nginx/sites-available/domain.com

    # Настройки для графаны
    location /grafs {
        proxy_pass http://localhost:3000;
        rewrite ^/grafs/(.*) /$1 break;
        proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-IP $remote_addr;
    }

Так же поправим конфиг графаны:

Bash:
sudo nano /etc/grafana/grafana.ini

[server]
domain = mydomain.com
root_url = %(protocol)s://%(domain)s:%(http_port)s/grafs
serve_from_sub_path = true

Супер! Теперь графана будет крутиться на домене в subpath.

Делаем метрики

Для начала я решил вывести 3 основных показателя:
  • % использования CPU
  • % занятого места на диске
  • % занятой RAM
Начнем с функции do_alarm - подробней в моей предыдущей статье
Python:
def do_alarm(t_alarmtext):
    headers = {"Content-type": "application/json"}
    payload = {"text": f"{t_alarmtext}", "chat_id": f"{admin_id}"}
    requests.post(url=webhook_url, data=json.dumps(payload), headers=headers)

Данные из системы будем тянуть с помощью фреймворка

Python:
def get_cpu_procent():
    #print(f'psutil.cpu_percent(): {psutil.cpu_percent()}')
    return psutil.cpu_percent()


def get_virtual_memory_procent_usage():
    #print(f'psutil.virtual_memory().percent: {psutil.virtual_memory().percent}')
    return psutil.virtual_memory().percent


def get_disc_usage():
    #print(f'psutil.disk_usage.used.precent:{psutil.disk_usage("/").percent}')
    return psutil.disk_usage("/").percent

* за комментированные строчки тут для дебага.

Инициализируем вебсервер и добавляем в него три метрики:

Python:
class CustomCollector(object):
    def __init__(self):
        pass

    def collect(self):
        cpu_usage = GaugeMetricFamily('system_cpu_percent_usage', '% использования cpu')
        cpu_usage_metric = get_cpu_procent()
        cpu_usage.add_metric([], cpu_usage_metric)

        ram_usage = GaugeMetricFamily('system_ram_percent_usage', '% использования оперативы')
        ram_usage_metric = get_virtual_memory_procent_usage()
        ram_usage.add_metric([], ram_usage_metric)

        disk_usage = GaugeMetricFamily('system_disk_usage', '% занятого места на диске')
        disk_usage_metric = get_disc_usage()
        disk_usage.add_metric([], disk_usage_metric)

        yield cpu_usage
        yield ram_usage
        yield disk_usage

А теперь блок main

Python:
if __name__ == '__main__':
    try:
        start_http_server(8000)
        REGISTRY.register(CustomCollector())
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        logger.info('Program stopped')
    except Exception as e:
        t_alarmtext = f'prometheus-custom-collector (app.py):\n {str(e)}'
        do_alarm(t_alarmtext)
        logger.error(f'Other except error Exception', exc_info=True)

Исходный код скрипта можно взять тут - 4teraflops/system_metrics_4_vps

Запуск скрипта: python3 app.py. Для удобства можно скрипт сделать демоном, либо положить в докер.

Добавляем метрики в Grafana

Опять же, ка кдобавить datasource в графану мануалов в сети великое множество, например = повторяться не буду.
Но как говорил Ваилий Иванович петьке: "Но есть один нюанс!"
Нюанс в том, что подключеная Datasource к Grafana должна смотреть еще и наши самописные метрики.

Для этго идем в конфиг прометеуса и добавляем ему джобу

Bash:
nano /etc/prometheus/prometheus.yml

  - job_name: 'prometheus-custom'
    scrape_interval: 5s
    static_configs:
      - targets: ['localhost:8000']


Визуализация

Итак, наши метрики готовы. После всех установок и нстроек у вас будет крутиться 3 сервиса. Порты не должны смотреть наружу:
  • - grafana server
  • - prometheus DB
  • - самописные метрики
Чтоб убедиться в том, что все работает корректно, посмотрим что выдают наши самописные метрики: curl localhost:8000
Если метрики работают корректно, то будет что-то такое:
# HELP python_gc_objects_collected_total Objects collected during gc
# TYPE python_gc_objects_collected_total counter
python_gc_objects_collected_total{generation="0"} 57.0
python_gc_objects_collected_total{generation="1"} 292.0
python_gc_objects_collected_total{generation="2"} 0.0
# HELP python_gc_objects_uncollectable_total Uncollectable object found during GC
# TYPE python_gc_objects_uncollectable_total counter
python_gc_objects_uncollectable_total{generation="0"} 0.0
python_gc_objects_uncollectable_total{generation="1"} 0.0
python_gc_objects_uncollectable_total{generation="2"} 0.0
# HELP python_gc_collections_total Number of times this generation was collected
# TYPE python_gc_collections_total counter
python_gc_collections_total{generation="0"} 62.0
python_gc_collections_total{generation="1"} 5.0
python_gc_collections_total{generation="2"} 0.0
# HELP python_info Python platform information
# TYPE python_info gauge
python_info{implementation="CPython",major="3",minor="8",patchlevel="0",version="3.8.0"} 1.0
# HELP process_virtual_memory_bytes Virtual memory size in bytes.
# TYPE process_virtual_memory_bytes gauge
process_virtual_memory_bytes 2.51322368e+08
# HELP process_resident_memory_bytes Resident memory size in bytes.
# TYPE process_resident_memory_bytes gauge
process_resident_memory_bytes 2.2777856e+07
# HELP process_start_time_seconds Start time of the process since unix epoch in seconds.
# TYPE process_start_time_seconds gauge
process_start_time_seconds 1.61753591402e+09
# HELP process_cpu_seconds_total Total user and system CPU time spent in seconds.
# TYPE process_cpu_seconds_total counter
process_cpu_seconds_total 81.83
# HELP process_open_fds Number of open file descriptors.
# TYPE process_open_fds gauge
process_open_fds 7.0
# HELP process_max_fds Maximum number of open file descriptors.
# TYPE process_max_fds gauge
process_max_fds 1024.0
# HELP system_cpu_percent_usage % использования cpu
# TYPE system_cpu_percent_usage gauge
system_cpu_percent_usage 2.9
# HELP system_ram_percent_usage % использования оперативы
# TYPE system_ram_percent_usage gauge
system_ram_percent_usage 50.7
# HELP system_disk_usage % занятого места на диске
# TYPE system_disk_usage gauge
system_disk_usage 68.0

Все самописные метрики не просто так начинаются с одного слова "system" - благодаря этому метрики лягут в один раздел.
Если залогиниться в графане на своем домене, затем создать дашборд, создать график, то вы увидите там:

Снимок экрана от 2021-04-06 15-24-10.png


Наши метрики работают. Осталось настроить визуализацию. Много ума для этого не надо. Просто тыкаешься и что-то в итоге получится, т.к. интерфейс интуитивный.

Снимок экрана от 2021-04-06 15-26-07.png
 

Aleks Pu

New member
11.02.2022
1
0
BIT
0
Я до сих пор не увидел в тексте содержимое файла config.py.

Поэтому:
#from config import admin_id, webhook_url
webhook_url='127.0.0.1'
...
#payload = {"text": f"{t_alarmtext}", "chat_id": f"{admin_id}"}
payload = {"text": f"{t_alarmtext}"}


И смотрим наши метрики по адресу: 127.0.0.1:8000
 
Последнее редактирование:
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!