Prometheus: мониторинг Azure VMSS с node_exporter и Docker контейнеров в Docker Swarm с cAdvisor

By | 03/17/2018
 

В продолжение постов о развёртывании Prometheus для мониторинга проекта в Azure (привет, Azure, давно не виделись! см. Azure: почему никогда).

Спустя три месяца – проект решил, что мониторинг им всё-таки нужен, и меня “вернули”.

Посты по теме:

Остановился я на добавлении к Prometheus серверу виртуальных машин из VMSS с помощью Prometheus exporter_proxy – пост Prometheus: exporter_proxy – мониторинг сервисов в приватной сети.

Ниже описывается продолжение настройки мониторинга сервисов – теперь к Prometheus серверу добавим мониторинг виртуальных машин из Azure VMSS и Docker Swarm managers и Docker Swarm workers, которые работают в этих VMSS.

Описание проекта

Очень кратко – про сам проект, который будет мониторится.

Рабочие окружения состоят из двух Azure VMSS (Virtual Machine Scale Set), на которых работает Docker Swarm: один VMSS для Swarm Manager нод, второй для Swarm Workers нод.

Перед каждым VMSS имеется свой Load Balancer, который разруливает трафик к интансам в этих VMSS.

Собственно само приложение включает в себя 6 контейнеров, которые запущены на Swarm Workers нодах:

admin@hzwzatr7dzxp4000000:~$ docker service ls
ID                  NAME                           MODE                REPLICAS            IMAGE                                      PORTS
rusilcjixgtw        jm_website_api_layer           replicated          1/1                 jm/jm-api-layer:v2.0.3             *:4004->4004/tcp
q3ua0haf2vdx        jm_website_proxy               replicated          1/1                 jm/jm-website-proxy:latest         *:80->80/tcp,*:443->443/tcp
qou735xchcf6        jm_website_transform           replicated          1/1                 jm/jm-cms-transform-layer:v2.1.1   *:3003->3003/tcp
r30zxn2az7xv        jm_website_transform_preview   replicated          1/1                 jm/jm-cms-transform-layer:v2.1.1   *:3004->3004/tcp
niy35cv64ftp        jm_website_web                 replicated          1/1                 jm/jm-website:v2.3.14              *:8008->8008/tcp
r2zhb82wasae        jm_website_web_preview         replicated          1/1                 jm/jm-website:v2.3.14              *:8080->8080/tcp

Описание Prometheus сервера

Теперь кратко о самом Prometheus сервере.

Prometheus и Grafana запущены на виртуальной машине, в отдельной Azure Resource Group.

К виртуальной машине во время развёртывания Resource Group (с помощью Azure Resource Manager шаблонов, см. пост Azure: provisioning с Resource Manager, Jenkins и Groovy) подключается внешний диск, который монтируется в /data из Ansible-задачи:

root@jm-monitoring-production-vm:~# findmnt /data
TARGET SOURCE    FSTYPE OPTIONS
/data  /dev/sdc1 ext4   rw,relatime,data=ordered

Задачи в Ansible – создание каталога /data и монтирование диска:

...
- name: Create  "{{ data_mount_path }}" directory
  file:
    path: "{{ data_mount_path }}"
    owner: root
    group: root
    mode: 0755
    state: directory

- name: Mount volume "{{ data_volume }}"
  mount:
    path: "{{ data_mount_path }}"
    src: "{{ data_volume }}"
    state: mounted
    fstype: ext4
...

В /data хранятся данные самого Prometheus, Grafana и сертификаты Let’s Ecnrypt:

root@jm-monitoring-production-vm:~# tree -L 2 /data/
/data/
├── grafana
│   ├── grafana.db
│   ├── plugins
│   └── sessions
├── letsencrypt
│   ├── accounts
│   ├── archive
│   ├── csr
│   ├── keys
│   ├── live
│   └── renewal
├── lost+found
└── prometheus
├── 00
├── 01
├── 02
...

Для доступа к Prometheus и Grafana, а так же для завершения SSL-сессии – перед ними запущен NGINX со следующим конфигом:

upstream prometheus_server {
    server 127.0.0.1:9090;
}

upstream grafana_ui {
    server 127.0.0.1:3000;
}

server {

    server_name    www.monitor.domain.tld;
    listen         80;
    return         301 https://monitor.domain.tld$request_uri;
}

server {

    server_name    monitor.domain.tld;
    listen         80;

    root /var/www/monitor.domain.tld;

    location ~ /.well-known {
        allow all;
    }

    location / {

        allow 194.***.***.45;
        allow 37.***.***.130;
        deny  all;

        return 301 https://monitor.domain.tld$request_uri;
    }
}

server {

    server_name monitor.domain.tld;
    listen 443 ssl;

    access_log /var/log/nginx/monitor.domain.tld-access.log proxy;
    error_log /var/log/nginx/monitor.domain.tld-error.log notice;

    ssl on;
    ssl_certificate /data/letsencrypt/live/monitor.domain.tld/fullchain.pem;
    ssl_certificate_key /data/letsencrypt/live/monitor.domain.tld/privkey.pem;

    root /var/www/monitor.domain.tld;

    location / {

        auth_basic_user_file /var/www/monitor.domain.tld/.htaccess;
        auth_basic "Password-protected Area";

        allow 194.***.***.45;
        allow 37.***.***.130;
        deny  all;

        proxy_redirect          off;
        proxy_set_header        Host            $host;
        proxy_set_header        X-Real-IP       $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass http://grafana_ui$request_uri;
    }

    location /prometheus {

        auth_basic_user_file /var/www/monitor.domain.tld/.htaccess;
        auth_basic "Password-protected Area";

        allow 194.***.***.45;
        allow 37.***.***.130;
        deny  all;

        proxy_redirect          off;
        proxy_set_header        Host            $host;
        proxy_set_header        X-Real-IP       $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass http://prometheus_server$request_uri;
    }

}

Добавление мониторинга VMSS

Задача на сегодня – добавить мониторинг виртуальных машин Docker Swarm менеджеров и воркеров, мониторинг Docker engine на них и контейнеров на воркерах, в которых работает само приложение.

Большая часть уже настроена (см. Prometheus: exporter_proxy – мониторинг сервисов в приватной сети), поэтому этот пост больше обзорный и как документация для самого себя – на случай, если проект опять “пропадёт” на три месяца.

Запуск exporter_proxy

Начнём с запуска exporter_proxy сервисов на менеджер-ноде.

Сейчас установка выполняется на QA, у которого 1 Manager нода и 3 workers.

Со Staging и Production будет немного сложнее, т.к. там три менеджера, и придётся обновлять правила Azure Load Balancer, но пока можно обойтись простым Compose файлом.

На manager ноде потребуется запустить exporter_proxy и подключить ему файл настроек.

Файл настроек сейчас выглядит так:

listen: "0.0.0.0:9099"

access_log:
  path: "/dev/stdout"
  format: "ltsv"
  fields: ['time', 'time_nsec', 'status', 'size', 'reqtime_nsec', 'backend', 'path', 'query', 'method']

error_log:
  path: "/dev/stderr"

exporters:
  master_exporter:
    url: "http://10.0.0.4:9100/metrics"
    path: "/master_exporter/metrics"
  worker_1_exporter:
    url: "http://192.168.0.4:9100/metrics"
    path: "/worker_1_node_exporter/metrics"
  worker_2_exporter:
    url: "http://192.168.0.5:9100/metrics"
    path: "/worker_2_node_exporter/metrics"
  worker_3_exporter:
    url: "http://192.168.0.6:9100/metrics"
    path: "/worker_3_node_exporter/metrics"

node_exporter-ы пока не запущены нигде, к ним перейдём чуть позже.

Создаём Compose файл для exporter_proxy:

version: '3'

networks:
  prometheus:

services:

  prometheus-proxy:
    image: rrreeeyyy/exporter_proxy
    volumes:
      - /home/admin/prometheus-proxy-config.yml:/etc/config.yml
    ports:
      - "9099:9099"
    command:
      - '-config=/etc/config.yml'
    networks:
      - prometheus
    deploy:
      placement:
        constraints:
          - node.role == manager

Тут в блоке:

...
      placement:
        constraints:
          - node.role == manager

указываем на запуск exporter_proxy только на менеджер-нодах. (см. constraint)

Тут ещё один нюанс, который связан чисто с текущим сетапом – менеджер ноды находятся в статусе drain, что бы не запускать на них сервисы вообще:

admin@hzwzatr7dzxp4000000:~$ docker node ls
ID                            HOSTNAME              STATUS              AVAILABILITY        MANAGER STATUS
ai3040vw51jch7zikcx0mjy5c *   hzwzatr7dzxp4000000   Ready               Drain               Leader
b4y0ldkfzyhztmapaw48uqre4     hzwzatr7dzxp4000000   Ready               Active
ughyd9bhoexkcvcipbg9ewfws     hzwzatr7dzxp4000001   Ready               Active
bd6v85fk6mr0tcam8xu3pmn78     hzwzatr7dzxp4000002   Ready               Active

Что бы Docker Swarm запустил наш прокси – обновляем стаус менеджер-ноды на active:

admin@hzwzatr7dzxp4000000:~$ docker node update --availability active ai3040vw51jch7zikcx0mjy5c
ai3040vw51jch7zikcx0mjy5c

Проверяем стеки сейчас:

admin@hzwzatr7dzxp4000000:~$ docker stack ls
NAME                SERVICES
jm_website          6

Создаём новый:

admin@hzwzatr7dzxp4000000:~$ docker stack deploy -c prometheus-proxy.yml jm-monitoring-proxy
Creating network jm-monitoring-proxy_prometheus
Creating service jm-monitoring-proxy_prometheus-proxy

Проверяем:

admin@hzwzatr7dzxp4000000:~$ docker stack ls
NAME                  SERVICES
jm-monitoring-proxy   1
jm_website            6

Проверяем сам сервис:

admin@hzwzatr7dzxp4000000:~$ docker service ps jm-monitoring-proxy_prometheus-proxy
ID                  NAME                                     IMAGE                             NODE                  DESIRED STATE       CURRENT STATE                ERROR               PORTS
ud26ch5ekthn        jm-monitoring-proxy_prometheus-proxy.1   rrreeeyyy/exporter_proxy:latest   hzwzatr7dzxp4000000   Running             Running about a minute ago

Пробуем получить метрики от proxy:

admin@hzwzatr7dzxp4000000:~$ curl -s localhost:9099/master_exporter/metrics
404 page not found

ОК – т.к. сам node_exporter ещё не запущен.

node_exporter и cAdvisor

Далее создадим сервис, который будет запускать контейнеры с node_exporter и cAdvisor на каждой ноде кластера – и менеджерах, и воркерах.

Созадём Compose файл:

version: '3'

networks:
  prometheus:

services:

  node-exporter:
    image: prom/node-exporter
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    command: 
      - '--path.procfs=/host/proc' 
      - '--path.sysfs=/host/sys'
      - --collector.filesystem.ignored-mount-points
      - "^/(sys|proc|dev|host|etc|rootfs/var/lib/docker/containers|rootfs/var/lib/docker/overlay2|rootfs/run/docker/netns|rootfs/var/lib/docker/aufs)($$|/)"
    ports:
      - 9100:9100
    networks:
      - prometheus
    deploy:
      mode: global

  cadvisor:
    image: google/cadvisor
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:rw
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro
    ports:
      - 8081:8080
    networks:
      - prometheus
    deploy:
      mode: global

В блоке:

...
    deploy:
      mode: global

указываем на запуск на всех нодах кластера (см. mode).

Создаём новый сервис:

admin@hzwzatr7dzxp4000000:~$ docker stack deploy -c prometheus-monitoring.yml jm-monitoring
Creating network jm-monitoring_prometheus
Creating service jm-monitoring_node-exporter
Creating service jm-monitoring_cadvisor

Проверяем стеки:

admin@hzwzatr7dzxp4000000:~$ docker stack ls
NAME                  SERVICES
jm-monitoring         2
jm-monitoring-proxy   1
jm_website            6

Сервисы этого стека:

admin@hzwzatr7dzxp4000000:~$ docker stack ps jm-monitoring
ID                  NAME                                                    IMAGE                       NODE                  DESIRED STATE       CURRENT STATE         ERROR               PORTS
t8kuno4xtygz        jm-monitoring_cadvisor.ughyd9bhoexkcvcipbg9ewfws        google/cadvisor:latest      hzwzatr7dzxp4000001   Running             Running 3 hours ago
kf0eei7ah5qs        jm-monitoring_cadvisor.bd6v85fk6mr0tcam8xu3pmn78        google/cadvisor:latest      hzwzatr7dzxp4000002   Running             Running 3 hours ago
85ajf4v2k61o        jm-monitoring_cadvisor.b4y0ldkfzyhztmapaw48uqre4        google/cadvisor:latest      hzwzatr7dzxp4000000   Running             Running 3 hours ago
vxo1nadcrlrc        jm-monitoring_cadvisor.ai3040vw51jch7zikcx0mjy5c        google/cadvisor:latest      hzwzatr7dzxp4000000   Running             Running 3 hours ago
jek7t3qtchvm        jm-monitoring_node-exporter.ai3040vw51jch7zikcx0mjy5c   prom/node-exporter:latest   hzwzatr7dzxp4000000   Running             Running 3 hours ago
bbvwf2zzakw5        jm-monitoring_node-exporter.ughyd9bhoexkcvcipbg9ewfws   prom/node-exporter:latest   hzwzatr7dzxp4000001   Running             Running 3 hours ago
pr25igrd8yua        jm-monitoring_node-exporter.bd6v85fk6mr0tcam8xu3pmn78   prom/node-exporter:latest   hzwzatr7dzxp4000002   Running             Running 3 hours ago
nqavzuuh5rhb        jm-monitoring_node-exporter.b4y0ldkfzyhztmapaw48uqre4   prom/node-exporter:latest   hzwzatr7dzxp4000000   Running             Running 3 hours ago

Всё поднялось, на всех нодах – 1 менеджер + 3 воркера == по 4 контейнера с cAdvisor и node_exporter.

Проверим метрики:

admin@hzwzatr7dzxp4000000:~$ curl -s localhost:9100/metrics | head
HELP go_gc_duration_seconds A summary of the GC invocation durations.
TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0"} 3.1599e-05
go_gc_duration_seconds{quantile="0.25"} 3.61e-05
go_gc_duration_seconds{quantile="0.5"} 3.8698e-05
go_gc_duration_seconds{quantile="0.75"} 4.6399e-05
go_gc_duration_seconds{quantile="1"} 0.000280095
go_gc_duration_seconds_sum 0.001616076
go_gc_duration_seconds_count 30
HELP go_goroutines Number of goroutines that currently exist.

ОК – node_exporter метрики отдаёт, проверим через exporter_proxy:

admin@hzwzatr7dzxp4000000:~$ curl -s localhost:9099/master_exporter/metrics | head
HELP go_gc_duration_seconds A summary of the GC invocation durations.
TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0"} 3.25e-05
go_gc_duration_seconds{quantile="0.25"} 3.67e-05
go_gc_duration_seconds{quantile="0.5"} 4.07e-05
go_gc_duration_seconds{quantile="0.75"} 5.23e-05
go_gc_duration_seconds{quantile="1"} 0.000656404
go_gc_duration_seconds_sum 0.002062908
go_gc_duration_seconds_count 30
HELP go_goroutines Number of goroutines that currently exist.

Вернёмся к prometheus-proxy-config.yml и добавим сбор метрик от cAdvisor.

Сначала проверим метрики от него напрямую:

admin@hzwzatr7dzxp4000000:~$ curl -s localhost:8081/metrics | head
HELP cadvisor_version_info A metric with a constant '1' value labeled by kernel version, OS version, docker version, cadvisor version & cadvisor revision.
TYPE cadvisor_version_info gauge
cadvisor_version_info{cadvisorRevision="1e567c2",cadvisorVersion="v0.28.3",dockerVersion="17.09.0-ce",kernelVersion="4.4.0-116-generic",osVersion="Alpine Linux v3.4"} 1
HELP container_cpu_load_average_10s Value of container cpu load average over the last 10 seconds.
TYPE container_cpu_load_average_10s gauge
container_cpu_load_average_10s{container_label_com_docker_stack_namespace="",container_label_com_docker_swarm_node_id="",container_label_com_docker_swarm_service_id="",container_label_com_docker_swarm_service_name="",container_label_com_docker_swarm_task="",container_label_com_docker_swarm_task_id="",container_label_com_docker_swarm_task_name="",id="/",image="",name=""} 0
...

Обновляем конфиг для прокси:

listen: "0.0.0.0:9099"

access_log:
  path: "/dev/stdout"
  format: "ltsv"
  fields: ['time', 'time_nsec', 'status', 'size', 'reqtime_nsec', 'backend', 'path', 'query', 'method']

error_log:
  path: "/dev/stderr"

exporters:
  manager_exporter:
    url: "http://10.0.0.4:9100/metrics"
    path: "/manager_exporter/metrics"
  worker_1_node_exporter:
    url: "http://192.168.0.4:9100/metrics"
    path: "/worker_1_node_exporter/metrics"
  worker_2_node_exporter:
    url: "http://192.168.0.5:9100/metrics"
    path: "/worker_2_node_exporter/metrics"
  worker_3_node_exporter:
    url: "http://192.168.0.6:9100/metrics"
    path: "/worker_3_node_exporter/metrics"
  worker_1_cadvisor_exporter:
    url: "http://192.168.0.4:8081/metrics"
    path: "/worker_1_cadvisor_exporter/metrics"
  worker_2_cadvisor_exporter:
    url: "http://192.168.0.5:8081/metrics"
    path: "/worker_2_cadvisor_exporter/metrics"
  worker_3_cadvisor_exporter:
    url: "http://192.168.0.6:8081/metrics"
    path: "/worker_3_cadvisor_exporter/metrics"

Пересоздаём стек прокси:

admin@hzwzatr7dzxp4000000:~$ docker stack rm jm-monitoring-proxy
Removing service jm-monitoring-proxy_prometheus-proxy
Removing network jm-monitoring-proxy_prometheus
admin@hzwzatr7dzxp4000000:~$ docker stack deploy -c prometheus-proxy.yml jm-monitoring-proxy
Creating network jm-monitoring-proxy_prometheus
Creating service jm-monitoring-proxy_prometheus-proxy

Проверяем метрики cAdvisor через прокси:

admin@hzwzatr7dzxp4000000:~$ curl -s localhost:9099/worker_1_cadvisor_exporter/metrics | head
...
container_cpu_load_average_10s{container_label_com_docker_stack_namespace="",container_label_com_docker_swarm_node_id="",container_label_com_docker_swarm_service_id="",container_label_com_docker_swarm_service_name="",container_label_com_docker_swarm_task="",container_label_com_docker_swarm_task_id="",container_label_com_docker_swarm_task_name="",id="/",image="",name=""} 0
container_cpu_load_average_10s{container_label_com_docker_stack_namespace="",container_label_com_docker_swarm_node_id="",container_label_com_docker_swarm_service_id="",container_label_com_docker_swarm_service_name="",container_label_com_docker_swarm_task="",container_label_com_docker_swarm_task_id="",container_label_com_docker_swarm_task_name="",id="/docker",image="",name=""} 0
container_cpu_load_average_10s{container_label_com_docker_stack_namespace="",container_label_com_docker_swarm_node_id="",container_label_com_docker_swarm_service_id="",container_label_com_docker_swarm_service_name="",container_label_com_docker_swarm_task="",container_label_com_docker_swarm_task_id="",container_label_com_docker_swarm_task_name="",id="/init.scope",image="",name=""} 0
container_cpu_load_average_10s{container_label_com_docker_stack_namespace="",container_label_com_docker_swarm_node_id="",container_label_com_docker_swarm_service_id="",container_label_com_docker_swarm_service_name="",container_label_com_docker_swarm_task="",container_label_com_docker_swarm_task_id="",container_label_com_docker_swarm_task_name="",id="/system.slice",image="",name=""} 0
container_cpu_load_average_10s{container_label_com_docker_stack_namespace="",container_label_com_docker_swarm_node_id="",container_label_com_docker_swarm_service_id="",container_label_com_docker_swarm_service_name="",container_label_com_docker_swarm_task="",container_label_com_docker_swarm_task_id="",container_label_com_docker_swarm_task_name="",id="/system.slice/accounts-daemon.service",image="",name=""} 0

ОК – пошли метрики от cAdvisor.

Добавление targets в Prometeus сервер

Последний шаг – это добавить таргеты в Prometheus сервер.

Находим конфиг сервера:

root@jm-monitoring-production-vm:~# cd /etc/prometheus/
root@jm-monitoring-production-vm:/etc/prometheus# ls -l
total 8
-rwxrwxr-x 1 prometheus prometheus  583 Dec 26 13:39 alert.rules
-rwxrwxr-x 1 prometheus prometheus 1525 Dec 26 13:39 prometheus.yml

Обновляем его блок scrape_configs, пока используем static_configs:

...
  - job_name: 'jm-website-qa'

    static_configs:
      - targets:
        - jm-website-qa-master-ip.westeurope.cloudapp.azure.com:9099
        labels:
          __metrics_path__: /manager_exporter/metrics
          name: jm-website-qa-manager-vm
      - targets:
        - jm-website-qa-master-ip.westeurope.cloudapp.azure.com:9099
        labels:
          __metrics_path__: /worker_1_node_exporter/metrics
          name: jm-website-qa-worker-1-vm
      - targets:
        - jm-website-qa-master-ip.westeurope.cloudapp.azure.com:9099
        labels:
          __metrics_path__: /worker_2_node_exporter/metrics
          name: jm-website-qa-worker-2-vm
      - targets:
        - jm-website-qa-master-ip.westeurope.cloudapp.azure.com:9099
        labels:
          __metrics_path__: /worker_3_node_exporter/metrics
          name: jm-website-qa-worker-3-vm
      - targets:
        - jm-website-qa-master-ip.westeurope.cloudapp.azure.com:9099
        labels:
          __metrics_path__: /worker_1_cadvisor_exporter/metrics
          name: jm-website-qa-worker-1-docker
      - targets:
        - jm-website-qa-master-ip.westeurope.cloudapp.azure.com:9099
        labels:
          __metrics_path__: /worker_2_cadvisor_exporter/metrics
          name: jm-website-qa-worker-2-docker
      - targets:
        - jm-website-qa-master-ip.westeurope.cloudapp.azure.com:9099
        labels:
          __metrics_path__: /worker_3_cadvisor_exporter/metrics
          name: jm-website-qa-worker-3-docker
...

[/simterm]

Проверяем сервисы Prometheus сервера:

root@jm-monitoring-production-vm:/etc/prometheus# cd /opt/prometheus/
root@jm-monitoring-production-vm:/opt/prometheus# docker-compose ps
Name                           Command               State           Ports
------------------------------------------------------------------------------------------------
prometheus_alertmanager_1        /bin/alertmanager -config. ...   Up      0.0.0.0:9093->9093/tcp
prometheus_cadvisor_1            /usr/bin/cadvisor -logtostderr   Up      0.0.0.0:8080->8080/tcp
prometheus_grafana_1             /run.sh                          Up      0.0.0.0:3000->3000/tcp
prometheus_node-exporter_1       /bin/node_exporter --path. ...   Up      0.0.0.0:9100->9100/tcp
prometheus_prometheus-server_1   /bin/prometheus -config.fi ...   Up      0.0.0.0:9090->9090/tcp

Перезапускаем Prometheus сервер:

root@jm-monitoring-production-vm:/opt/prometheus# docker-compose restart prometheus-server
Restarting prometheus_prometheus-server_1 ... done

И проверяем targets:

 

Проверим метрики от cAdvisor, в Prometheus graphs вызываем, например, container_cpu_user_seconds_total, добавляем фильтры:

container_cpu_user_seconds_total{container_label_com_docker_stack_namespace="jm_website",container_label_com_docker_swarm_service_name="jm_website_proxy"}

Grafana dashboard

И по-быстрому – добавим дашборд в Grafana, через импорт, например Docker monitoring:

Осталось навести порядок в labels.

Ну и ещё кучу всего поделать.