В своей практике я достаточно много времени посвящаю проектированию и администрированию облачных инфраструктур различного назначения. В основном это Apache CloudStack. Данная система обладает отличными возможностями, но в части мониторинга, функциональности явно недостаточно (читайте — отсутствует), особенно, если на мониторинг смотреть шире чем мониторинг индивидуального объекта наблюдения (сервер, виртуальная машина).


В целом, в связи с более широкими требованиями к систем визуального анализа информации и потребностями в части интеграции с источниками данных стали распространяться специализированные решения для ad-hoc анализа данных, такие как Kibana, Grafana и иные. Данные системы могут интегрироваться со специализированными хранилищами временных рядов данных, одним из которых является InfluxDB. Статья расскажет о готовом решении, распространяемом в виде образа Docker, использующем LibVirt API, Grafana и InfluxDB, предназначенном для сбора и анализа параметров исполняющихся VM для гипервизора KVM.


Обзор решения


Решение представлено в форме Docker-контейнера, распространяемого по лицензии Apache License v2, поэтому оно может без ограничений применяться в любых организациях и изменяться, отражая потребности конкретной задачи. Контейнер размещается на выделенном сервере, python-утилита сбора данных удаленно подключается по протоколу TCP к LibVirt и отправляет данные в InfluxDB, откуда они могут быть запрошены с помощью Grafana для визуализации и анализа.


Контейнер доступен в виде исходных кодов на GitHub и в виде доступного для установки образа на DockerHub. Язык реализации — python.


Почему Docker-контейнер


Данное решение является конечным и удобным для внедрения, а так же не требует каких-либо дополнительных настроек и установки дополнительного ПО на серверах виртуализации, кроме разрешения доступа к API LibVirt по сети. Если доступ к API LibVirt снаружи не представляется возможным, то возможно установить Docker на хосте виртуализации и запускать контейнер локально.


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

Какие данные собираются


Сенсор собирает следующие данные о виртуальных машинах, доступные через LibVirt:


CPU:


    {
        "fields": {
            "cpuTime": 1070.75,
            "cpus": 4
        },
        "measurement": "cpuTime",
        "tags": {
            "vmHost": "qemu+tcp://root@10.252.1.33:16509/system",
            "vmId": "i-376-1733-VM",
            "vmUuid": "12805898-0fda-4fa6-9f18-fac64f673679"
        }
    }

RAM:


{
        "fields": {
            "maxmem": 4194304,
            "mem": 4194304,
            "rss": 1443428
        },
        "measurement": "rss",
        "tags": {
            "vmHost": "qemu+tcp://root@10.252.1.33:16509/system",
            "vmId": "i-376-1733-VM",
            "vmUuid": "12805898-0fda-4fa6-9f18-fac64f673679"
        }
    }

Статистика по каждому сетевому адаптеру с привязкой к MAC-адресу:


{
        "fields": {
            "readBytes": 111991494,
            "readDrops": 0,
            "readErrors": 0,
            "readPackets": 1453303,
            "writeBytes": 3067403974,
            "writeDrops": 0,
            "writeErrors": 0,
            "writePackets": 588124
        },
        "measurement": "networkInterface",
        "tags": {
            "mac": "06:f2:64:00:01:54",
            "vmHost": "qemu+tcp://root@10.252.1.33:16509/system",
            "vmId": "i-376-1733-VM",
            "vmUuid": "12805898-0fda-4fa6-9f18-fac64f673679"
        }
    }

Статистика по каждому диску:


{
        "fields": {
            "allocatedSpace": 890,
            "ioErrors": -1,
            "onDiskSpace": 890,
            "readBytes": 264512607744,
            "readIOPS": 16538654,
            "totalSpace": 1000,
            "writeBytes": 930057794560,
            "writeIOPS": 30476842
        },
        "measurement": "disk",
        "tags": {
            "image": "cc8121ef-2029-4f4f-826e-7c4f2c8a5563",
            "pool": "b13cb3c0-c84d-334c-9fc3-4826ae58d984",
            "vmHost": "qemu+tcp://root@10.252.1.33:16509/system",
            "vmId": "i-376-1733-VM",
            "vmUuid": "12805898-0fda-4fa6-9f18-fac64f673679"
        }
    }

Общая статистика по хосту виртуализации, как ее "видит" LibVirt:


    {
        "fields": {
            "freeMem": 80558,
            "idle": 120492574,
            "iowait": 39380,
            "kernel": 1198652,
            "totalMem": 128850,
            "user": 6416940
        },
        "measurement": "nodeInfo",
        "tags": {
            "vmHost": "qemu+tcp://root@10.252.1.33:16509/system"
        }
    }

Настройка LibVirt


В конфигурационном файле /etc/libvirt/libvirtd.conf необходимо установить:


listen_tls = 0
listen_tcp = 1
tcp_port = "16509"
auth_tcp = "none"
mdns_adv = 0

Внимание! Вышеуказанные настройки позволят соединяться с API LibVirt по TCP, настройте корректно файрвол для ограничения доступа.

После выполнения данных настроек LibVirt необходимо перезапустить.


sudo service libvirt-bin restart

InfluxDB


Установка (для Ubuntu)


Установка InfluxDB осуществляется по документации, например, для Ubuntu:


curl -sL https://repos.influxdata.com/influxdb.key | sudo apt-key add -
source /etc/lsb-release
echo "deb https://repos.influxdata.com/${DISTRIB_ID,,} ${DISTRIB_CODENAME} stable" | sudo tee /etc/apt/sources.list.d/influxdb.list

sudo apt-get update && sudo apt-get install influxdb
sudo service influxdb start

Настройка аутентификации


Выполним команду influx для открытия сессии к СУБД:


$ influx

Создадим администратора (он нам понадобится когда мы активируем аутентификацию):


CREATE USER admin WITH PASSWORD '<password>' WITH ALL PRIVILEGES

Создадим базу данных pulsedb и обычного пользователя pulse с доступом к этой базе данных:


CREATE DATABASE pulsedb
CREATE USER pulse WITH PASSWORD '<password>'
GRANT ALL ON pulsedb TO pulse

Активируем аутентификацию в конфигурационном файле /etc/influxdb/influxdb.conf:


auth-enabled = true

Перезапустим InfluxDB:


service influxdb restart

Если все сделано правильно, теперь при открытии сессии необходимо указывать имя пользователя и пароль:


influx -username pulse -password secret

Запуск контейнера для начала сбора данных


docker pull bwsw/cs-pulse-sensor

docker run --restart=always -d --name 10.252.1.11              -e PAUSE=10              -e INFLUX_HOST=influx              -e INFLUX_PORT=8086              -e INFLUX_DB=pulsedb              -e INFLUX_USER=pulse              -e INFLUX_PASSWORD=secret              -e GATHER_HOST_STATS=true
             -e DEBUG=true              -e KVM_HOST=qemu+tcp://root@10.252.1.11:16509/system              bwsw/cs-pulse-sensor

Большинство параметров самоочевидны, поясню лишь два:


  1. PAUSE — интервал между запросом значений в секундах;
  2. GATHER_HOST_STATS — определяет собирать или нет дополнительно статистику по хосту;

После этого в журнале контейнера с помощью команды docker logs должна отражаться активность и не должны отражаться ошибки.


Если открыть сессию к InfluxDB, то в консоли можно выполнить команду и убедиться в наличии данных измерений:


influx -database pulsedb -username admin -password secret

> select * from cpuTime limit 1
name: cpuTime
time                cpuTime cpus vmHost                                   vmId          vmUuid
----                ------- ---- ------                                   ----          ------
1498262401173035067 1614.06 4    qemu+tcp://root@10.252.1.30:16509/system i-332-2954-VM 9c002f94-8d24-437e-8af3-a041523b916a

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


Установка и настройка Grafana (Ubuntu)


Устанавливаем, как описано в документации


wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_4.4.1_amd64.deb
sudo apt-get install -y adduser libfontconfig
sudo dpkg -i grafana_4.4.1_amd64.deb
sudo service grafana-server start
sudo update-rc.d grafana-server defaults

Запускаем web-браузер и открываем административный интерфейс Grafana http://influx.host.com:3000/.


Добавление источника данных в Grafana


Детальная инструкция по добавлению источника данных на сайте проекта. В нашем же случае добавляемый источник данных может выглядеть следующим образом:



После сохранения источника данных, можно создать "дэшборды" и попробовать создавать запросы для графиков (поскольку данная инструкция не о том, как пользоваться Grafana, то привожу лишь выражения для запросов):


Загрузка CPU (минутки):


select NON_NEGATIVE_DERIVATIVE(MAX("cpuTime"), 1m) / LAST("cpus") / 60 * 100 from "cpuTime" where "vmUuid" = '6da0cdc9-d8ff-4b43-802c-0be01c6e0099' and $timeFilter group by time(1m)

Загрузка CPU (пятиминутки):


select NON_NEGATIVE_DERIVATIVE(MAX("cpuTime"), 5m) / LAST("cpus") / 300 * 100 from "cpuTime" where "vmUuid" = '6da0cdc9-d8ff-4b43-802c-0be01c6e0099' and $timeFilter group by time(5m)

Загрузка CPU (все VM):


select  NON_NEGATIVE_DERIVATIVE(MAX("cpuTime"),1m) / LAST("cpus") / 60 * 100 as CPU from "cpuTime" WHERE $timeFilter group by time(1m), vmUuid


Память VM (пятиминутная агрегация):


SELECT MAX("rss") FROM "rss" WHERE "vmUuid" = '6da0cdc9-d8ff-4b43-802c-0be01c6e0099' and $timeFilter GROUP BY time(5m) fill(null)

Статистика ReadBytes, WriteBytes для диска (пятиминутная агрегация):


select NON_NEGATIVE_DERIVATIVE(MAX("readBytes"),5m)  / 300 from "disk" where "image" = '999a1942-3e14-4d04-8123-391494a28198' and $timeFilter group by time(5m)

select NON_NEGATIVE_DERIVATIVE(MAX("writeBytes"),5m)  / 300 from "disk" where "image" = '999a1942-3e14-4d04-8123-391494a28198' and $timeFilter group by time(5m)

Статистика ReadBits, WriteBits для NIC (пятиминутная агрегация):


select NON_NEGATIVE_DERIVATIVE(MAX("readBytes"), 5m)  / 300 * 8 from "networkInterface" where "mac" = '06:07:70:00:01:10' and $timeFilter group by time(5m)

select NON_NEGATIVE_DERIVATIVE(MAX("writeBytes"), 5m)  / 300 * 8 from "networkInterface" where "mac" ='06:07:70:00:01:10' and $timeFilter group by time(5m)

Вся мощь языка запросов InfluxDB к вашим услугам, и Вы можете строить такие дэшборды, которые отвечают Вашим потребностям и позволяют производить наглядный анализ данных. Например, один из самых полезных для меня кейсов — это разбор инцидентов, бывает, что клиент жалуется на то, что "ваш код ****о" © и говорит, что его VM чудесно работала, а потом раз и все. Строим выражение, смотрим на картинку, видим как CPU его VM в течение часа уходил в пике и таки ушел. Скриншот — отличный аргумент при решении конфликта.


Еще можно анализировать самых интенсивно использующих различные ресурсы VM, чтобы мигрировать их на отдельные хосты. Да все, что угодно. В этом смысле Grafana, Kibana и подобные системы выгодно отличаются от традиционных систем мониторинга (например, Zabbix) тем, что позволяют делать анализ по требованию и строить комплексные аналитические наборы, а InfluxDB помогает обеспечить высокую производительность анализа даже на большом наборе данных.


Заключение


Код, получающий данные из LibVirt тестировался с VM, которые используют тома в QCOW2 формате. Я постарался учесть варианты LVM2 и RBD, но не тестировал. Если у кого-то получится протестировать код на других вариантах томов VM и прислать исправления для кода, буду признателен.


PS: При мониторинге сетевого трафика VM Вы можете обнаружить, что данные по PPS значительно меньше тех, которые Вы получаете посредством Sflow/Netflow на маршрутизаторе или tcpdump в VM. Это известное свойство KVM, сетевая подсистема которого не придерживается стандартного MTU в 1500 байт.


PPS: Документация по API LibVirt для python ужасна и мне пришлось продираться через разные версии, чтобы все же выяснить в каком виде возвращаются данные и что они означают.


PPS2: Если что, как говорят на Газорпазорпе, "Я рядом, если надо поговорить" ©

Поделиться с друзьями
-->

Комментарии (9)


  1. denissa
    08.07.2017 21:23

    Доброго времени суток! Понравились ваши статьи. После прочтения их у меня появилось к Вам несколько вопросов. Не оставите мне свой контактный e-mail для связи?


    1. ivankudryavtsev
      08.07.2017 21:39
      +1

      Добрый вечер. Ответил в личные сообщения.


  1. SirEdvin
    08.07.2017 22:27
    +1

    Зачем использовать influxdb, у которого есть определенные проблемы со стабильность и у которого платная кластеризация?
    Почему не prometheus + libvirt_exporter?


    1. ivankudryavtsev
      09.07.2017 05:09
      +2

      Трудно сказать, зачем использовать продукт A, а не продукт B.

      Наверное, моя мысль не так развивалась, когда я решил реализовать данный модуль. Я посмотрел что есть актуального среди TSDB, посмотрел бенчмарки и выбрал InfluxDB. Разработка заняла меньше двух дней, что вполне допустимо даже для неудачного PoC.

      Если взять даже грубую оценку для сбора данных 1 раз в 30 секунд для 1000 хостов с 100 VM на каждом, то получается 100000 VM (~ 500000 точек). Скорее всего, данный масштаб еще нормально [обработается](https://www.influxdata.com/announcing-influxdb-v0-10-100000s-writes-per-second-better-compression/) одним сервером InfluxDB, а возможно, что и нет… Но, если вдуматься, то это уже задача иного масштаба, потому что 1000 серверов — это 30 стоек и это вполне приличные деньги, достаточные для применения подхода к решению задачи, который займет не два дня на реализацию. Возможно, что на данном этапе это уже будет какая-то другая технология, может быть платная кластеризация InfluxDB.

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

      В общем, хочу лишь сказать, что описанное мной решение работает и работает хорошо, а если будет хорошо работать и для других людей — волшебно.

      Это, конечно, тред про администрирование, но для супермасштабных задач я бы попробовал Apache Kafka + Spark + RocksDB


  1. timofei-durakov
    09.07.2017 15:59

    Спасибо за статью.Только посмотрите еще в сторону аутентификации на libvirt.


  1. raskal
    09.07.2017 15:59

    В статистике по дискам есть такие поля:
    «allocatedSpace»: 890,
    «onDiskSpace»: 890,
    «totalSpace»: 1000

    Подскажите, что они значат в контексте libvirt?


    1. ivankudryavtsev
      09.07.2017 16:05

      В зависимости от типа Storage, например файлы в формате qcow2, диск может иметь фактический размер не совпадающий с задекларированным в связи с «ленивым» выделением. TotalSpace — размер диска как его видит vm, а onDiskSpace — сколько на диске фактически занято, allocatedSpace не могу сказать значение, надо смотреть документацию libvirt.

      Для raw файлов и блочных устройств все три должны совпадать.


      1. raskal
        09.07.2017 16:41

        Вот интересно как раз понять, как onDiskSpace считается. Хотелось бы знать не только, сколько выделено диска (это можно и из бэкенда узнать, если там, например, OpenStack или просто Ceph), но и сколько его реально используется вмкой (потому что ни один бэкенд такой информации не даст, конечно же).


        1. ivankudryavtsev
          10.07.2017 05:49

          Для Qcow2 — сколько файл на диске занимает, столько и выдается. А вот сколько используется VM-кой (типа df -h) — на такой вопрос ответить не получится. Насколько я понимаю, qcow2 online trim не поддерживает.