В продолжение постов о развёртывании 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 нодах:
[simterm]
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
[/simterm]
Описание Prometheus сервера
Теперь кратко о самом Prometheus сервере.
Prometheus и Grafana запущены на виртуальной машине, в отдельной Azure Resource Group.
К виртуальной машине во время развёртывания Resource Group (с помощью Azure Resource Manager шаблонов, см. пост Azure: provisioning с Resource Manager, Jenkins и Groovy) подключается внешний диск, который монтируется в /data из Ansible-задачи:
[simterm]
root@jm-monitoring-production-vm:~# findmnt /data TARGET SOURCE FSTYPE OPTIONS /data /dev/sdc1 ext4 rw,relatime,data=ordered
[/simterm]
Задачи в 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:
[simterm]
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
...
[/simterm]
Для доступа к 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, что бы не запускать на них сервисы вообще:
[simterm]
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
[/simterm]
Что бы Docker Swarm запустил наш прокси — обновляем стаус менеджер-ноды на active:
[simterm]
admin@hzwzatr7dzxp4000000:~$ docker node update --availability active ai3040vw51jch7zikcx0mjy5c ai3040vw51jch7zikcx0mjy5c
[/simterm]
Проверяем стеки сейчас:
[simterm]
admin@hzwzatr7dzxp4000000:~$ docker stack ls NAME SERVICES jm_website 6
[/simterm]
Создаём новый:
[simterm]
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
[/simterm]
Проверяем:
[simterm]
admin@hzwzatr7dzxp4000000:~$ docker stack ls NAME SERVICES jm-monitoring-proxy 1 jm_website 6
[/simterm]
Проверяем сам сервис:
[simterm]
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
[/simterm]
Пробуем получить метрики от proxy:
[simterm]
admin@hzwzatr7dzxp4000000:~$ curl -s localhost:9099/master_exporter/metrics 404 page not found
[/simterm]
ОК — т.к. сам 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).
Создаём новый сервис:
[simterm]
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
[/simterm]
Проверяем стеки:
[simterm]
admin@hzwzatr7dzxp4000000:~$ docker stack ls NAME SERVICES jm-monitoring 2 jm-monitoring-proxy 1 jm_website 6
[/simterm]
Сервисы этого стека:
[simterm]
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
[/simterm]
Всё поднялось, на всех нодах — 1 менеджер + 3 воркера == по 4 контейнера с cAdvisor и node_exporter.
Проверим метрики:
[simterm]
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.
[/simterm]
ОК — node_exporter метрики отдаёт, проверим через exporter_proxy:
[simterm]
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.
[/simterm]
Вернёмся к prometheus-proxy-config.yml и добавим сбор метрик от cAdvisor.
Сначала проверим метрики от него напрямую:
[simterm]
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
...
[/simterm]
Обновляем конфиг для прокси:
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"
Пересоздаём стек прокси:
[simterm]
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
[/simterm]
Проверяем метрики cAdvisor через прокси:
[simterm]
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
[/simterm]
ОК — пошли метрики от cAdvisor.
Добавление targets в Prometeus сервер
Последний шаг — это добавить таргеты в Prometheus сервер.
Находим конфиг сервера:
[simterm]
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
[/simterm]
Обновляем его блок 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 сервера:
[simterm]
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
[/simterm]
Перезапускаем Prometheus сервер:
[simterm]
root@jm-monitoring-production-vm:/opt/prometheus# docker-compose restart prometheus-server Restarting prometheus_prometheus-server_1 ... done
[/simterm]
И проверяем 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.
Ну и ещё кучу всего поделать.

