Чтобы отслеживать состояние работающих приложений, необходимо проводить их постоянный мониторинг. А если приложения выполняются в таком хорошо масштабируемом окружении, как Docker Swarm, то потребуется также и хорошо масштабируемый инструмент мониторинга. В этой статье говорится о настройке именно такого инструмента.


В процессе работы мы установим агенты cAdvisor на каждой ноде для сбора метрик хоста и контейнеров. Метрики будут сохраняться в InfluxDB. Для построения графиков на основе этих метрик воспользуемся Grafana. Эти инструменты распространяются с открытым исходным кодом и могут быть развернуты в виде контейнеров.


Для построения кластера мы будем использовать Docker Swarm Mode и развернем необходимые сервисы в виде стека. Это позволит организовать динамическую систему мониторинга, которая способна автоматически начинать мониторинг новых нод по мере их добавления в рой (swarm). Файлы проекта можно найти здесь.


Обзор инструментов


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


cAdvisor


cAdvisor будет собирать метрики хостов и контейнеров. Он устанавливается в виде docker-образа c подключенным в виде общего тома сокетом docker и корневой файловой системой на хосте. cAdvisor может записывать собираемые метрики в несколько видов баз данных временных рядов (time-series database), включая InfluxDB, Prometheus и т. д. У него даже есть веб-интерфейс, в котором по собранным данным строятся графики.


InfluxDB


Масштабируемое хранилище для метрик, событий и аналитики реального времени.

InfluxDB — это база данных временных рядов с открытым исходным кодом, которая позволяет хранить числовые метрики и назначать им теги. В этой системе реализован SQL-подобный язык запросов, который можно использовать для работы с хранимыми данными. События мы будем фильтровать с помощью тегов по хосту или даже контейнеру.


Grafana


Grafana — многофункциональная система с открытым исходным кодом, которая позволяет создавать панели инструментов и графики на основе метрик, получаемых из Graphite, Elasticsearch, OpenTSDB, Prometheus и InfluxDB.

Grafana — это популярный инструмент визуализации, который позволяет создавать панели инструментов, получая данные из Graphite, Elasticsearch, OpenTSDB, Prometheus и, конечно, InfluxDB. Начиная с четвертой версии появилась возможность настройки оповещений на основе результатов запросов. Мы создадим панель инструментов, с помощью которой можно будет вывести данные по конкретному хосту и сервису.


Docker Swarm Mode


Swarm Mode появился в Docker начиная с версии 1.12.0. Он позволяет с легкостью создать из множества хостов рой (swarm) и без труда управлять им. Чтобы обеспечить работу встроенных механизмов обнаружения сервисов и оркестровки, в Swarm mode реализовано хранилище типа ключ-значение. Хосты могут выполнять роль менеджера (manager) или рабочего узла (worker). В общем случае менеджер отвечает за функцию оркестровки, а на рабочих узлах выполняются контейнеры. Поскольку это демонстрационная установка, мы разместим InfluxDB и Grafana на менеджере.


В Swarm Mode есть интересная функция маршрутная сетка (routing mesh), которая выполняет роль виртуального балансировщика нагрузки. Предположим, у нас есть 10 слушающих 80-й порт контейнеров, которые выполняются на 5 узлах. При попытке доступа к 80-у порту одного из этих контейнеров запрос может быть направлен любому из них, даже запущенному на другом хосте. Таким образом, опубликовав IP-адрес любой ноды, вы автоматически включаете балансировку запросов между десятью контейнерами.


Если вы планируете самостоятельно выполнить приведенные в этой демонстрации команды в своей системе, вам понадобятся следующие программы:


  • Docker: версия >= 1.13 (для Docker Compose File версии 3 и Swarm Mode);
  • Docker Machine: версия >= 0.8;
  • Docker Compose: версия >= 1.10 (для Docker Compose File версии 3).

Рой будет состоять из трех локальных виртуальных машин, которые мы развернем с помощью docker-machine-плагина Virtualbox. Для этого у вас должна быть установлена система виртуализации Virtualbox. Используя другие плагины, можно развернуть виртуальные машины в облачных сервисах. Шаги после создания машин будут одинаковыми для всех плагинов. Более подробную информацию о docker-machine вы можете найти здесь.


При создании виртуальных машин мы оставим опции по умолчанию. Здесь размещена более подробная информация о доступных опциях. Хост, выполняющий функцию менеджера роя, назовем manager, а рабочие узлы — agent1 и agent2. Вы можете создать столько нод, сколько хотите. Просто повторите приведенные команды с другим именем хоста. Для создания виртуальной машины выполните следующие команды:


docker-machine create manager
docker-machine create agent1
docker-machine create agent2

На выполнение этих команд может потребоваться некоторое время. После создания машин вывод docker-machine ls должен выглядеть примерно так:


NAME      ACTIVE   DRIVER       STATE     URL                         SWARM   DOCKER        ERRORS
agent1    -        virtualbox   Running   tcp://192.168.99.101:2376           v17.03.1-ce
agent2    -        virtualbox   Running   tcp://192.168.99.102:2376           v17.03.1-ce
manager   -        virtualbox   Running   tcp://192.168.99.100:2376           v17.03.1-ce          

Для использования движка docker на хосте manager необходимо переключить контекст. Далее мы будем выполнять команды в docker, установленном на хосте manager, а НЕ в локальной системе. Для этого выполните команду:


eval `docker-machine env manager`

Теперь, когда мы переключились в docker на manager, инициализируем этот хост в качестве менеджера роя. Нам потребуется его IP, который будет опубликован на других подключаемых нодах. Команда docker-machine ip manager позволяет получить необходимую информацию. Итак, для создания роя выполните следующую команду:


docker swarm init --advertise-addr `docker-machine ip manager`

Теперь нам нужно два рабочих узла. Для этого необходимо передать Join Token и IP, опубликованный при создании роя. Чтобы получить токен, выполните команду docker swarm join-token -q worker. Команда docker-machine ip manager, как и прежде, позволит получить IP менеджера и его стандартный порт 2377. Мы могли бы добавить новые машины в рой, по очереди переключаясь в контекст каждого рабочего узла, но гораздо проще выполнить эти команды по SSH. Для присоединения рабочих узлов к рою выполните следующие команды:


docker-machine ssh agent1 docker swarm join --token `docker swarm join-token -q worker` `docker-machine ip manager`:2377
docker-machine ssh agent2 docker swarm join --token `docker swarm join-token -q worker` `docker-machine ip manager`:2377

Список входящих в рой нод можно вывести командой docker node ls. После добавления рабочих узлов вывод должен выглядеть так:


ID                           HOSTNAME  STATUS  AVAILABILITY  MANAGER STATUS
3j231njh03spl0j8h67z069cy *  manager   Ready   Active        Leader
muxpteij6aldkixnl31f0asar    agent1    Ready   Active
y2gstaqpqix1exz09nyjn8z41    agent2    Ready   Active

Docker Stack


С файлом docker-compose третьей версии в одном файле можно определить стек сервисов целиком, включая стратегию развертывания, и выполнить развертывание одной командой deploy. Основным отличием третьей версии файла docker-compose от второй стало появление параметра deploy в описании каждого сервиса. Этот параметр определяет способ развертывания контейнеров. Файл docker-compose для тестовой системы мониторинга приведен ниже:


version: '3'

services:
  influx:
    image: influxdb
    volumes:
      - influx:/var/lib/influxdb
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.role == manager

  grafana:
    image: grafana/grafana
    ports:
      - 0.0.0.0:80:3000
    volumes:
      - grafana:/var/lib/grafana
    depends_on:
      - influx
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.role == manager

  cadvisor:
    image: google/cadvisor
    hostname: '{{.Node.ID}}'
    command: -logtostderr -docker_only -storage_driver=influxdb -storage_driver_db=cadvisor -storage_driver_host=influx:8086
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:rw
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro
    depends_on:
      - influx
    deploy:
      mode: global

volumes:
  influx:
    driver: local
  grafana:
    driver: local

В нашем стеке 3 сервиса, которые описаны ниже.


influx


Здесь мы будем использовать образ influxdb. Для постоянного хранилища создадим том influx, который будет монтироваться в папку контейнера /var/lib/influxdb. Нам нужна только одна копия InfluxDB, которая будет размещена на хосте manager. Сервер docker запущен на этом же хосте, поэтому команды для контейнера могут выполняться здесь же. Поскольку обоим оставшимся сервисам нужна influxDB, мы добавим в описание этих сервисов ключ depends_on со значением influx.


grafana


Будем использовать образ grafana/grafana и пробросим 3000-й порт контейнера на 80-й порт хоста. Маршрутная сетка позволяет подключаться к grafana через 80-й порт любого хоста, входящего в рой. Для постоянного хранения данных создадим еще один том под названием grafana. Он будет монтироваться в папке контейнера /var/lib/grafana. Grafana также развернем на хосте manager.


cadvisor


Чтобы настроить cAdvisor, придется поработать чуть больше, чем с предыдущими сервисами. Более подробная информация доступна по этой ссылке. Выбор значения hostname в этом случае — задача непростая. Мы собираемся установить агенты на каждой ноде, и этот контейнер будет собирать метрики ноды и работающих на ней контейнеров. Когда cAdvisor посылает метрики в InfluxDB, он устанавливает тег machine, который содержит имя контейнера с cAdvisor. Его значение должно соответствовать ID ноды, на которой он запущен. В стеках Docker можно использовать шаблоны в именах. Более подробную информацию можно найти здесь. Мы дали контейнерам имена, содержащие ID ноды, на которой они запущены, и таким образом сможем определить, откуда пришла метрика. Это достигается использованием следующего выражения '{{.Node.ID}}'.


Мы также добавим в cadvisor несколько параметров командной строки. Параметр logtostderr перенаправляет сгенерированные cadvsior логи в stderr, что упрощает отладку. Флаг docker_only говорит, что нас интересуют лишь контейнеры docker. Следующие три параметра определяют место в хранилище, где необходимо разместить собранные метрики. Мы попросим cAdvisor положить их в базу данных cadvisor на сервере InfluxDB, слушающем на influx:8086. Это позволит настроить отправку метрик на influx-сервис нашего стека. Внутри стека все порты открыты (exposed), поэтому их не нужно указывать отдельно.


Прописанные в файле тома нужны cAdvisor для сбора метрик с хоста и докера. Для развертывания cadvisor мы будем использовать режим global. Это даст уверенность, что на каждой ноде роя выполняется лишь один экземпляр сервиса cadvisor.


В конце файла у нас есть ключ volumes, в котором указаны тома influx и grafana. Поскольку оба тома будут размещены на хосте manager, для них мы пропишем драйвер local.


Для развертывания стека сохраните размещенный выше файл под именем docker-stack.yml и выполните следующую команду:


docker stack deploy -c docker-stack.yml monitor

Она запустит сервисы стека monitor. Первый запуск команды может занять некоторое время, поскольку ноды должны загрузить образы контейнеров. Вам также потребуется создать в InfluxDB базу данных для хранения метрик под названием cadvisor.


docker exec `docker ps | grep -i influx | awk '{print $1}'` influx -execute 'CREATE DATABASE cadvisor'

Выполнение команды может завершиться неудачей с сообщением, что контейнер influx не существует. Причина ошибки в том, что контейнер еще не готов. Подождите немного и выполните команду еще раз. Мы можем выполнять команды в сервисе influx, поскольку он запущен на хосте manager и мы используем установленный здесь docker. Чтобы выяснить ID контейнера с InfluxDB, можно воспользоваться командой docker ps | grep -i influx | awk '{print $1}', а для создания базы данных с именем cadvisor надо выполнить команду influx -execute 'CREATE DATABASE cadvisor'.


Чтобы вывести список сервисов стека, выполните docker stack services monitor. Вывод команды будет выглядеть примерно так:


ID            NAME              MODE        REPLICAS  IMAGE
0fru8w12pqdx  monitor_influx    replicated  1/1       influxdb:latest
m4r34h5ho984  monitor_grafana   replicated  1/1       grafana/grafana:latest
s1yeap330m7e  monitor_cadvisor  global      3/3       google/cadvisor:latest

Список запущенных контейнеров можно получить командной docker stack ps monitor, вывод которой будет примерно таким:


ID            NAME                                        IMAGE                   NODE     DESIRED STATE  CURRENT STATE               ERROR  PORTS
n7kobaozqzj6  monitor_cadvisor.y78ac29r904m8uy6hxffb7uvn  google/cadvisor:latest  agent2   Running        Running about a minute ago
1nsispop3hsu  monitor_cadvisor.z52c9vloiutl5dbuj5lnykzvl  google/cadvisor:latest  agent1   Running        Running about a minute ago
9n6djc80mamd  monitor_cadvisor.qn82bfj5cpin2cpmx9qv1j56s  google/cadvisor:latest  manager  Running        Running about a minute ago
hyr8piriwa0x  monitor_grafana.1                           grafana/grafana:latest  manager  Running        Running about a minute ago
zk7u8g73ko5w  monitor_influx.1                            influxdb:latest         manager  Running        Running about a minute ago

Настройка Grafana


После того как все сервисы развернуты, можно открыть grafana. Для этого подойдет IP любой ноды роя. Мы укажем IP менеджера, выполнив следующую команду:


open http://`docker-machine ip manager`

По умолчанию для входа в grafana используются имя пользователя admin и пароль admin. В grafana в качестве источника данных надо добавить InfluxDB. На домашней страничке должна быть ссылка Create your first data source, кликните по ней. Если ссылки нет, выберите из меню Data Sources пункт Add data source, который откроет форму добавления нового Data Source.



Добавление Data Source в Grafana


Источнику данных можно дать любое имя. Поставьте галку в чекбоксе default, чтобы в дальнейшем не приходилось указывать его в других формах. Далее установим Type равным InfluxDB, URL — http://influx:8086 и Access — proxy. Таким образом мы указали на наш InfluxDb-контейнер. В поле Database введите cadvisor и нажмите Save and Test — должно появиться сообщение Data source is working.


В github-репозитории проекта есть файл dashboard.json, созданный для импорта в Grafana. В нем описана панель инструментов мониторинга систем и контейнеров, которые выполняются в рое. Сейчас мы только импортируем эту панель инструментов, а поговорим о ней в следующем разделе. Наведите курсор на пункт меню Dashboards и выберите Import Option. Нажмите кнопку Upload .json file и выберите dashboard.json. Далее выберите источник данных и нажмите кнопку Import.


Grafana Dashboard



Grafana Dashboard


Импортированная в Grafana панель инструментов предназначена для мониторинга хостов и контейнеров роя. Вы можете детализировать отчет до уровня хоста и выполняющихся на нем контейнеров. Нам понадобятся две переменные, для добавление которых в панель инструментов Grafana необходима функциональность работы с шаблонами. Более подробная информация о работе с шаблонами в связке с InfluxDB — на этой странице. У нас есть две переменные: host для выбора ноды и container для выбора контейнера. Чтобы увидеть эти переменные, на странице панели инструментов выберите Settings и нажмите Templating.


Первая переменная — host — позволяет выбрать ноду и ее метрики. Когда cAdvisor отправляет метрики в InfluxDB, он присоединяет к ним несколько тегов, которые можно использовать для фильтрации. У нас есть тег под названием machine, который содержит имя хоста (hostname) экземпляра cAdvisor. В данном случае он будет соответствовать ID хоста в рое. Для получения значений тега используется запрос show tag values with key = "machine".


Вторая переменная — container — позволяет детализировать отчет до уровня контейнера. У нас есть тег с именем container_name, в котором вполне предсказуемо содержится имя контейнера. Нам также надо фильтровать метрики по значению тега host. Запрос будет выглядеть следующим образом: show tag values with key = "container_name" WHERE machine =~ /^$host$/. Он вернет нам список контейнеров, у которых переменная host содержит имя интересующего нас хоста.


Имя контейнера будет выглядеть примерно так:


monitor_cadvisor.y78ac29r904m8uy6hxffb7uvn.3j231njh03spl0j8h67z069cy. Мы, однако, заинтересованы только в его monitor_cadvisor-части (до первой точки). Если запущено несколько экземпляров одного сервиса, их данные нужно будет выводить в отдельных строках. Чтобы получить подстроку до первой точки, применим регулярное выражение /([^.]+)/.


Переменные мы настроили, теперь можем использовать их в графиках. Далее разговор пойдет о графике Memory, а с остальными можно работать по такому же принципу. Данные, относящиеся к памяти, находятся в InfluxDB в ряде memory_usage, поэтому запрос будет начинаться с SELECT "value" FROM "memory_usage".


Теперь надо добавить фильтры в выражение WHERE. Первым условием будет равенство machine значению переменной host: "machine" =~ /^$host$/. В следующем условии container_name должен начинаться со значения переменной container. Здесь мы воспользуемся оператором «начинается с», поскольку отфильтровали переменную container до первой точки: "container_name" =~ /^$container$*/. Последние условие накладывает ограничение на время событий в соответствии с временным интервалом $timeFilter, выбранным в панели инструментов grafana. Запрос теперь выглядит так:


SELECT "value" FROM "memory_usage" WHERE "container_name" =~ /^$container$*/ AND "machine" =~ /^$host$/ AND $timeFilter

Поскольку нам нужны отдельные строки для разных хостов и контейнеров, необходимо сгруппировать данные на основе значений тегов machine и container_name:


SELECT "value" FROM "memory_usage" WHERE "container_name" =~ /^$container$*/ AND "machine" =~ /^$host$/ AND $timeFilter GROUP BY "machine", "container_name"

Мы также создали alias для этого запроса: Memory {host: $tag_machine, container: $tag_container_name}. Здесь $tag_machine будет замещен значением тега machine, а tag_container_name ­— значением тега container_name. Остальные графики настраиваются похожим образом, меняются только имена рядов (series). Для этих метрик в Grafana можно создать предупреждения. Более подробную информацию о системе предупреждений (Alerting) см. здесь.


Заключение


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


После завершения работы с демонстрацией стек может быть удален командной:


docker stack rm monitor

Ненужные виртуальные машины останавливаются и удаляются командами:


docker-machine stop manager agent1 agent2
docker-machine rm -f manager agent1 agent2

Ссылки:


  1. Оригинал: Monitoring Docker Swarm with cAdvisor, InfluxDB and Grafana.
Поделиться с друзьями
-->

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


  1. sequence
    01.05.2017 21:25
    +2

    Связка cadvisor + docker + aufs бомба замедленного действия для вашего окружения. Причина критичный баг двухлетней выдержки. Будьте осторожны, если вдруг ваш интерес не ограничится песочницей.


    1. ZloyRabadaber
      02.05.2017 14:28

      не подскажите в какую сторону смотреть, чтобы обойти этот баг?


      1. sequence
        02.05.2017 19:23

        storage-driver: overlay2, с ним проблемы вроде нет.
        В любом случае применять cadvisor далее тестовых сред я бы не стал.


  1. SicYar
    03.05.2017 10:31

    Не холивара ради, но почему cAdvisor, а не Consul? Так же метрики, вэб-мордашка, запуск в контейнере ну и т.д.