Чтобы отслеживать состояние работающих приложений, необходимо проводить их постоянный мониторинг. А если приложения выполняются в таком хорошо масштабируемом окружении, как Docker Swarm, то потребуется также и хорошо масштабируемый инструмент мониторинга. В этой статье говорится о настройке именно такого инструмента. В процессе работы мы установим агенты cAdvisor на каждой ноде для сбора метрик хоста и контейнеров. Метрики будут сохраняться в InfluxDB. Для построения графиков на основе этих метрик воспользуемся Grafana. Эти инструменты распространяются с открытым исходным кодом и могут быть развернуты в виде контейнеров. Для построения кластера мы будем использовать Docker Swarm Mode и развернем необходимые сервисы в виде стека. Это позволит организовать динамическую систему мониторинга, которая способна автоматически начинать мониторинг новых нод по мере их добавления в рой (swarm). Файлы проекта можно найти здесь. Выбор систем мониторинга достаточно велик. Для построения нашего стека мы будем использовать хорошо работающие в контейнерах сервисы с открытым исходным кодом. Далее я опишу состав стека. cAdvisor будет собирать метрики хостов и контейнеров. Он устанавливается в виде docker-образа c подключенным в виде общего тома сокетом docker и корневой файловой системой на хосте. cAdvisor может записывать собираемые метрики в несколько видов баз данных временных рядов (time-series database), включая InfluxDB, Prometheus и т. д. У него даже есть веб-интерфейс, в котором по собранным данным строятся графики. InfluxDB — это база данных временных рядов с открытым исходным кодом, которая позволяет хранить числовые метрики и назначать им теги. В этой системе реализован SQL-подобный язык запросов, который можно использовать для работы с хранимыми данными. События мы будем фильтровать с помощью тегов по хосту или даже контейнеру. Grafana — это популярный инструмент визуализации, который позволяет создавать панели инструментов, получая данные из Graphite, Elasticsearch, OpenTSDB, Prometheus и, конечно, InfluxDB. Начиная с четвертой версии появилась возможность настройки оповещений на основе результатов запросов. Мы создадим панель инструментов, с помощью которой можно будет вывести данные по конкретному хосту и сервису. Swarm Mode появился в Docker начиная с версии 1.12.0. Он позволяет с легкостью создать из множества хостов рой (swarm) и без труда управлять им. Чтобы обеспечить работу встроенных механизмов обнаружения сервисов и оркестровки, в Swarm mode реализовано хранилище типа ключ-значение. Хосты могут выполнять роль менеджера (manager) или рабочего узла (worker). В общем случае менеджер отвечает за функцию оркестровки, а на рабочих узлах выполняются контейнеры. Поскольку это демонстрационная установка, мы разместим InfluxDB и Grafana на менеджере. В Swarm Mode есть интересная функция маршрутная сетка (routing mesh), которая выполняет роль виртуального балансировщика нагрузки. Предположим, у нас есть 10 слушающих 80-й порт контейнеров, которые выполняются на 5 узлах. При попытке доступа к 80-у порту одного из этих контейнеров запрос может быть направлен любому из них, даже запущенному на другом хосте. Таким образом, опубликовав IP-адрес любой ноды, вы автоматически включаете балансировку запросов между десятью контейнерами. Если вы планируете самостоятельно выполнить приведенные в этой демонстрации команды в своей системе, вам понадобятся следующие программы: Рой будет состоять из трех локальных виртуальных машин, которые мы развернем с помощью docker-machine-плагина Virtualbox. Для этого у вас должна быть установлена система виртуализации Virtualbox. Используя другие плагины, можно развернуть виртуальные машины в облачных сервисах. Шаги после создания машин будут одинаковыми для всех плагинов. Более подробную информацию о docker-machine вы можете найти здесь. При создании виртуальных машин мы оставим опции по умолчанию. Здесь размещена более подробная информация о доступных опциях. Хост, выполняющий функцию менеджера роя, назовем manager, а рабочие узлы — agent1 и agent2. Вы можете создать столько нод, сколько хотите. Просто повторите приведенные команды с другим именем хоста. Для создания виртуальной машины выполните следующие команды: На выполнение этих команд может потребоваться некоторое время. После создания машин вывод docker-machine ls должен выглядеть примерно так: Для использования движка docker на хосте manager необходимо переключить контекст. Далее мы будем выполнять команды в docker, установленном на хосте manager, а НЕ в локальной системе. Для этого выполните команду: Теперь, когда мы переключились в docker на manager, инициализируем этот хост в качестве менеджера роя. Нам потребуется его IP, который будет опубликован на других подключаемых нодах. Команда docker-machine ip manager позволяет получить необходимую информацию. Итак, для создания роя выполните следующую команду: Теперь нам нужно два рабочих узла. Для этого необходимо передать Join Token и IP, опубликованный при создании роя. Чтобы получить токен, выполните команду docker swarm join-token -q worker. Команда docker-machine ip manager, как и прежде, позволит получить IP менеджера и его стандартный порт 2377. Мы могли бы добавить новые машины в рой, по очереди переключаясь в контекст каждого рабочего узла, но гораздо проще выполнить эти команды по SSH. Для присоединения рабочих узлов к рою выполните следующие команды: Список входящих в рой нод можно вывести командой docker node ls. После добавления рабочих узлов вывод должен выглядеть так: С файлом docker-compose третьей версии в одном файле можно определить стек сервисов целиком, включая стратегию развертывания, и выполнить развертывание одной командой deploy. Основным отличием третьей версии файла docker-compose от второй стало появление параметра deploy в описании каждого сервиса. Этот параметр определяет способ развертывания контейнеров. Файл docker-compose для тестовой системы мониторинга приведен ниже: В нашем стеке 3 сервиса, которые описаны ниже. Здесь мы будем использовать образ influxdb. Для постоянного хранилища создадим том influx, который будет монтироваться в папку контейнера /var/lib/influxdb. Нам нужна только одна копия InfluxDB, которая будет размещена на хосте manager. Сервер docker запущен на этом же хосте, поэтому команды для контейнера могут выполняться здесь же. Поскольку обоим оставшимся сервисам нужна influxDB, мы добавим в описание этих сервисов ключ depends_on со значением influx. Будем использовать образ grafana/grafana и пробросим 3000-й порт контейнера на 80-й порт хоста. Маршрутная сетка позволяет подключаться к grafana через 80-й порт любого хоста, входящего в рой. Для постоянного хранения данных создадим еще один том под названием grafana. Он будет монтироваться в папке контейнера /var/lib/grafana. Grafana также развернем на хосте manager. Чтобы настроить 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 и выполните следующую команду: Она запустит сервисы стека monitor. Первый запуск команды может занять некоторое время, поскольку ноды должны загрузить образы контейнеров. Вам также потребуется создать в InfluxDB базу данных для хранения метрик под названием 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. Вывод команды будет выглядеть примерно так: Список запущенных контейнеров можно получить командной docker stack ps monitor, вывод которой будет примерно таким: После того как все сервисы развернуты, можно открыть grafana. Для этого подойдет IP любой ноды роя. Мы укажем IP менеджера, выполнив следующую команду: По умолчанию для входа в 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 панель инструментов предназначена для мониторинга хостов и контейнеров роя. Вы можете детализировать отчет до уровня хоста и выполняющихся на нем контейнеров. Нам понадобятся две переменные, для добавление которых в панель инструментов 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. Запрос теперь выглядит так:  Поскольку нам нужны отдельные строки для разных хостов и контейнеров, необходимо сгруппировать данные на основе значений тегов 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. После завершения работы с демонстрацией стек может быть удален командной: Ненужные виртуальные машины останавливаются и удаляются командами: Ссылки: CEO в Southbridge