Задача — настроить Proof of Concept мониторинга, используя Prometheus, что бы показать основные его возможности.
Используется Prometheus 2.2.1 (между 1.* и 2.* существенные различия в синтаксисе).
Чего не добавлено в этот пост — это работа с Prometheus API, и хотелось подробнее остановиться на Grafana и её шаблонах, но уже отдельным постом. Кроме того, в самом тексте и в конце поста пачка ссылок, которые использовались во время написания.
Поднимать будем локально, на Virtualbox машинах.
Схема будет следующая:
- 1 машина, jm-prometheus-server — для самого Prometheus сервера, там же Alertmanager
- 2 машины под Docker Swarm, на каждом хосте которого будут запущены:
- cadvisor: для получения метрик Docker-контейнеров
- node-exporter: для получения метрик с виртуальных машин
- плюс отдельные контейнеры на хостах:
- хост jm-swarm-manager, тут будут:
- blackbox-exporter: для проверки сайтов
- prometheus: будет собирать метрики из Docker Swarm сервисов, и передавать их на Prometheus сервер на хосте jm-prometheus-server
- хост jm-swarm-worker, тут будет:
- nginx: для тестов blackbox
- хост jm-swarm-manager, тут будут:
Содержание
Подготовка
Запуск Virutalbox машины
Используем VirtualBox с Debian.
Запустим машину, установим на неё всё необходимое, потом склонируем.
Для упрощения запуска машины из консоли — можно использовать такой bash-скрипт:
#!/usr/bin/env bash
BOXNAME=$1
[[ $BOXNAME ]] || { echo -e "\nERROR: set VM name as first argument. Exit.\n"; exit 1; }
# register VM
VBoxManage createvm --name "$BOXNAME" --register
# set network interfaces
VBoxManage modifyvm "$BOXNAME" --nic1 bridged --bridgeadapter1 enp0s25 --nictype1 82540EM --cableconnected1 on
# set OS type
VBoxManage modifyvm "$BOXNAME" --ostype Debian_64
# create HDD
cd /home/setevoy/VirtualBox\ VMs/"$BOXNAME"/ && VBoxManage createhd --filename "$BOXNAME".vdi --size 10000
# add IDE
VBoxManage storagectl "$BOXNAME" --name "IDE Controller" --add ide
# attach disk
VBoxManage storageattach "$BOXNAME" --storagectl "IDE Controller" --port 0 --device 0 --type hdd --medium "$BOXNAME".vdi
# attach ISO
VBoxManage storageattach "$BOXNAME" --storagectl IDE Controller" --port 1 --device 0 --type dvddrive --medium /home/setevoy/OS/debian-9.4.0-amd64-netinst.iso
# set memory
VBoxManage modifyvm "$BOXNAME" --memory 2048
# start VM
VBoxManage startvm "$BOXNAME"
Запускаем машину:
[simterm]
$ ./mk_vboxes.sh debian_docker_base_vm Virtual machine 'debian_docker_base_vm' is created and registered. UUID: 1b09be7a-7dd3-49b8-afcc-27daccd0fb78 Settings file: '/home/setevoy/VirtualBox VMs/debian_docker_base_vm/debian_docker_base_vm.vbox' 0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100% Medium created. UUID: 9dbd7c1b-4546-4821-94e9-4e82e1c45836 Waiting for VM "debian_docker_base_vm" to power on... VM "debian_docker_base_vm" has been successfully started.
[/simterm]
Устанавливаем ОС как обычно:
Переходим к установке Docker.
Установка Docker
Обновляем систему:
[simterm]
root@debian:/home/setevoy# apt update && apt -y upgrade && reboot
[/simterm]
Устанавливаем зависимости:
[simterm]
root@debian:/home/setevoy# apt -y install apt-transport-https ca-certificates curl gnupg2 software-properties-common sudo
[/simterm]
Добавляем GPG ключ Docker:
[simterm]
root@debian:/home/setevoy# curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add -
[/simterm]
Добавляем репозиторий Docker:
[simterm]
root@debian:/home/setevoy# add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable"
[/simterm]
Обновляем списки пакетов:
[simterm]
root@debian:/home/setevoy# apt update
[/simterm]
Устанавливаем Docker:
[simterm]
root@debian:/home/setevoy# apt -y install docker-ce
[/simterm]
Проверяем:
[simterm]
root@debian:/home/setevoy# docker run hello-world Unable to find image 'hello-world:latest' locally latest: Pulling from library/hello-world ca4f61b1923c: Pull complete Digest: sha256:97ce6fa4b6cdc0790cda65fe7290b74cfebd9fa0c9b8c38e979330d547d22ce1 Status: Downloaded newer image for hello-world:latest Hello from Docker! This message shows that your installation appears to be working correctly. ...
[/simterm]
Добавляем пользователя setevoy в группу docker:
[simterm]
root@debian:/home/setevoy# usermod -aG docker setevoy
[/simterm]
Установка Docker Compose
Устанавливаем Compose:
[simterm]
root@debian:/home/setevoy# curl -L https://github.com/docker/compose/releases/download/1.20.1/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose root@debian:/home/setevoy# chmod +x /usr/local/bin/docker-compose
[/simterm]
Проверяем:
[simterm]
root@debian:/home/setevoy# docker-compose --version docker-compose version 1.20.1, build 5d8c71b
[/simterm]
Клонирование Virtualbox машин
Находим запущенную машину:
[simterm]
$ vboxmanage list runningvms
"debian_docker_base_vm" {1b09be7a-7dd3-49b8-afcc-27daccd0fb78}
[/simterm]
Выключаем её:
[simterm]
$ vboxmanage controlvm debian_docker_base_vm acpipowerbutton
[/simterm]
Создаём три клона — машину для Prometheus сервера:
[simterm]
$ vboxmanage clonevm debian_docker_base_vm --register --name jm_prometheus_server 0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100% Machine has been successfully cloned as "jm_prometheus_server"
[/simterm]
Для Docker Swarm менеджера:
[simterm]
$ vboxmanage clonevm debian_docker_base_vm --register --name jm_swarm_manager 0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100% Machine has been successfully cloned as "jm_swarm_manager"
[/simterm]
И для Docker Swarm worker:
[simterm]
$ vboxmanage clonevm debian_docker_base_vm --register --name jm_swarm_worker 0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100% Machine has been successfully cloned as "jm_swarm_worker"
[/simterm]
Проверяем:
[simterm]
$ vboxmanage list vms | grep jm
"jm_prometheus_server" {d47b0810-ce8a-43e1-b316-6659a5f91914}
"jm_swarm_manager" {ddefa9c7-88b9-40d8-9d4a-ace5f0194a50}
"jm_swarm_worker" {74e5559a-117c-4df8-b401-f5a030d85d5e}
[/simterm]
И запускаем их:
[simterm]
$ vboxmanage startvm jm_prometheus_server Waiting for VM "jm_prometheus_server" to power on... VM "jm_prometheus_server" has been successfully started. $ vboxmanage startvm jm_swarm_manager Waiting for VM "jm_swarm_manager" to power on... VM "jm_swarm_manager" has been successfully started. $ vboxmanage startvm jm_swarm_worker Waiting for VM "jm_swarm_worker" to power on... VM "jm_swarm_worker" has been successfully started.
[/simterm]
Можно добавить --type headless, что бы не создавать окна.
Логинимся, задаём имена хостов:
[simterm]
root@debian:/home/setevoy# hostnamectl set-hostname jm-prometheus-server root@debian:/home/setevoy# hostname jm-prometheus-server
[/simterm]
Повторяем на менеджере и воркере.
Создание Docker Swarm
Теперь переходим к Docker Swarm.
Логинимся на manager-хост, создаём Swarm:
[simterm]
root@jm-swarm-manager:/home/setevoy# docker swarm init
Swarm initialized: current node (mtdra23bnqjacjehpe584nqw7) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-602pa1w2n4wtwk9r99q55un03y3wix2digvcden0y5kzsobbru-2yru5erpvih0qd6txr86emb6s 10.11.100.194:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
[/simterm]
Логинимся на worker, подключаем его к swarm:
[simterm]
root@jm-swarm-worker:/home/setevoy# docker swarm join --token SWMTKN-1-602pa1w2n4wtwk9r99q55un03y3wix2digvcden0y5kzsobbru-2yru5erpvih0qd6txr86emb6s 10.11.100.194:2377 This node joined a swarm as a worker.
[/simterm]
На менеджере проверяем ноды:
[simterm]
root@jm-swarm-manager:/home/setevoy# docker node ls ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION mtdra23bnqjacjehpe584nqw7 * jm-swarm-manager Ready Active Leader 18.03.0-ce emcxnaqamthemtrljg1i941ea jm-swarm-worker Ready Active 18.03.0-ce
[/simterm]
Prometheus
Основы Prometheus
Prometheus — pull-based система мониторинга, т.е. сервер обращается к своим клиентам для получения данных (см. Prometheus and the Debate Over ‘Push’ Versus ‘Pull’ Monitoring).
В Prometheus такие клиенты, т.е. цели, которые надо мониторить, называются targets (собственно — цели), или instances, а группа инстансов, выполняющая одну роль в терминологии Prometheus называется job.
В определённые в конфигурации промежутки времени Prometheus будет выполнять запросы (scrape) к этим таргетам (инстансам) для получения от них метрик, которые, как правило, доступны через URI /metric.
Например — у Prometheus-сервера доступны его собственные метрики.
Запускаем контейнер с Prometheus, и проверяем:
[simterm]
root@jm-prometheus-server:/home/setevoy# docker run -tid -p 9090:9090 prom/prometheus
root@jm-prometheus-server:/home/setevoy# curl -s localhost:9090/metrics | head -5
# HELP go_gc_duration_seconds A summary of the GC invocation durations.
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0"} 1.4375e-05
go_gc_duration_seconds{quantile="0.25"} 2.2796e-05
go_gc_duration_seconds{quantile="0.5"} 2.7988e-05
[/simterm]
А node_exporter — предоставляет свои метрики:
[simterm]
root@jm-prometheus-server:/home/setevoy# docker run -tid -p 9100:9100 prom/node-exporter
root@jm-prometheus-server:/home/setevoy# curl -s localhost:9100/metrics | grep node | grep -v \# | head -5
node_arp_entries{device="eth0"} 1
node_boot_time 1.522487906e+09
node_context_switches 212022
node_cpu{cpu="cpu0",mode="guest"} 0
node_cpu{cpu="cpu0",mode="guest_nice"} 0
[/simterm]
Метрики
Каждая метрика возвращает некий ключ и значение, которые будут сохранены в timeseries-базу данных Prometheus-а (TSDB).
Формат метрик Prometehus:
<metric name>{<label name>=<label value>, ...}
Например:
http_requests_total{service="service", server="pod50", env="production"}
Storage
По умолчанию Prometheus использует локальное хранилище для данных, в виде time-series базы данных собственной разработки, но поддерживает интеграцию и с другими базами — см. Remote Endpoints and Storage.
Собранные данные группируются в блоки по два часа: каждый такой блок состоит из каталога, который содержит один или несколько chunk-файлов с time-series данными за этот период времени, файл метаданных и файл индекса, который содержит имена метрик и метки (labels), например:
[simterm]
root@jm-prometheus-server:/home/setevoy# docker exec -ti adoring_pasteur ls -l /prometheus/ total 12 drwxr-xr-x 3 nobody nogroup 4096 Mar 31 11:00 01C9XVE1AAJ287MTJ0FR9EPP1M -rw------- 1 nobody nogroup 2 Mar 31 09:30 lock drwxr-xr-x 2 nobody nogroup 4096 Mar 31 09:30 wal
[/simterm]
И содержимое каталога 01C9XVE1AAJ287MTJ0FR9EPP1M:
[simterm]
root@jm-prometheus-server:/home/setevoy# docker exec -ti adoring_pasteur ls -l /prometheus/01C9XVE1AAJ287MTJ0FR9EPP1M total 80 drwxr-xr-x 2 nobody nogroup 4096 Mar 31 11:00 chunks -rw-r--r-- 1 nobody nogroup 66320 Mar 31 11:00 index -rw-r--r-- 1 nobody nogroup 275 Mar 31 11:00 meta.json -rw-r--r-- 1 nobody nogroup 9 Mar 31 11:00 tombstones
[/simterm]
См. Storage и Prometheus 2.0: New storage layer dramatically increases monitoring scalability for Kubernetes and other distributed systems.
Exporters
Если приложение или сервер сам по себе не возвращают метрики — их можно получить с помощью экспортёров, например:
blackbox-exporter: используется для проверки URL, DNS, TCPnode-exporter: для получения метрик системы, таких как потребление CPU, памяти, диска и т.д.cAdvisor: для получения метрик о работе Docker engine и контейнеров, которые на нём запущены
Список различных экспортёров можно найти тут>>>.
Prometheus конфиг
В файле настроек Prometheus описываются его собственные настройки, такие как интервал для выполнения запросов к таргетам, и сами таргеты, объединённые в задачи (jobs).
Таргеты могут определяться через статический конфиг, например:
...
scrape_configs:
- job_name: 'prometheus'
scrape_interval: 15s
scrape_timeout: 10s
metrics_path: /metrics
scheme: http
static_configs:
- targets:
- 10.11.100.194:9100
Тут мы указываем опрашивать URI /metrics по HTTP каждые 15 секунд, используя статический IP swarm-воркера 10.11.100.194 и порт 9100:
Либо динамический:
...
scrape_configs:
- job_name: 'prometheus'
scrape_interval: 15s
scrape_timeout: 10s
metrics_path: /metrics
scheme: http
# static_configs:
# - targets:
# - 10.11.100.194:9100
dns_sd_configs:
- names:
- node-exporter
refresh_interval: 30s
type: A
port: 9100
В случае динамической конфигурации — Prometheus выполнит DNS запрос к Docker (sd в dns_sd_configs — Service Discovery), и начнёт собирать метрики с каждого найденного контейнера с node-exporter:
В этом примере ещё нет Swarm-а и запущен только один контейнер в Compose, но сейчас мы это исправим.
Запуск Prometheus стека
Для того, что бы наш Prometheus сервер мог собирать метрики с сервисов в Docker Swarm — можно использовать либо prometheus_proxy, либо exporter_proxy, либо воспользоваться federation — когда два Prometheus сервера работают вместе, и один из них — «основной» — собирает метрики со второго. Второй, в свою очередь, работает в Docker Swarm, и имеет доступ к его service discovery, что позволяет использовать динамические конфигурации.
Основной сервер Prometheus будет на хосте jm-prometheus-server, а запускать его будем из Docker Compose.
Обновляем prometheus.yml:
global:
scrape_interval: 15s
evaluation_interval: 15s
alerting:
alertmanagers:
- static_configs:
- targets:
rule_files:
scrape_configs:
- job_name: 'prometheus'
scheme: http
static_configs:
- targets:
- node-exporter:9100
- prometheus-server:9090
Создаём базовый Compose файл, например prometheus-stack.yml, позже к нему добавим остальные сервисы (Grafana, Alertmanager, blackbox-expoter):
version: '3.3'
networks:
prometheus:
services:
prometheus-server:
image: prom/prometheus
networks:
- prometheus
ports:
- 9090:9090
volumes:
- /home/setevoy/prometheus.yml:/etc/prometheus/prometheus.yml
node-exporter:
image: prom/node-exporter
networks:
- prometheus
ports:
- 9100:9100
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)($$|/)"
Запускаем:
[simterm]
root@jm-prometheus-server:/home/setevoy# docker-compose -f prometheus-stack.yml up -d Starting setevoy_node-exporter_1 ... done Starting setevoy_prometheus-server_1 ... done
[/simterm]
Проверяем:
Prometheus cross-service federation
Теперь в Docker Swarm на менеджер-ноде запустим второй Prometheus сервер, который будет собирать метрики из сервисов в Swarm, и отдавать их основному серверу — это и есть Cross-service federation.
На Swarm Manager создаём Compose файл, например foo-services.yml:
networks:
foo-services:
driver: overlay
services:
prometheus-server:
image: prom/prometheus
networks:
- foo-services
ports:
- 9090:9090
volumes:
- /home/setevoy/prometheus.yml:/etc/prometheus/prometheus.yml
deploy:
placement:
constraints:
- node.role == manager
node-exporter:
image: prom/node-exporter
networks:
- foo-services
ports:
- 9100:9100
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)($$|/)"
deploy:
mode: global
Тут же создаём /home/setevoy/prometheus.yml, в котором используем dns_sd_configs для поиска сервисов с именем node-exporterв сети foo-services:
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'foo-services'
scrape_interval: 15s
scrape_timeout: 10s
metrics_path: /metrics
scheme: http
dns_sd_configs:
- names:
- tasks.node-exporter
refresh_interval: 30s
type: A
port: 9100
Создаём стек:
[simterm]
root@jm-swarm-manager:/home/setevoy# docker stack deploy -c foo-services.yml foo-services Creating network foo-services_foo-services Creating service foo-services_node-exporter Creating service foo-services_prometheus-server
[/simterm]
Проверяем:
[simterm]
root@jm-swarm-manager:/home/setevoy# docker service ls ID NAME MODE REPLICAS IMAGE PORTS 1gm920gcb5if foo-services_node-exporter global 2/2 prom/node-exporter:latest *:9100->9100/tcp yqqoxacvgak3 foo-services_prometheus-server replicated 1/1 prom/prometheus:latest *:9090->9090/tcp
[/simterm]
Проверяем — где у нас запущены node-exporter-ы:
[simterm]
root@jm-swarm-manager:/home/setevoy# docker service ps foo-services_node-exporter ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS mnsr87po5wft foo-services_node-exporter.rjz4b1n4w62hb4m0ghih8s33l prom/node-exporter:latest jm-swarm-worker Running Running 6 minutes ago o6ophn50froy foo-services_node-exporter.hi7bswznodxgc489g3o5gs9uy prom/node-exporter:latest jm-swarm-manager Running Running 6 minutes ago
[/simterm]
Проверяем DNS — выполняем nslookup из контейнера:
[simterm]
root@jm-swarm-manager:/home/setevoy# docker exec -ti 2afdc8a632bc nslookup tasks.node-exporter Server: 127.0.0.11 Address 1: 127.0.0.11 Name: tasks.node-exporter Address 1: 10.0.0.7 2afdc8a632bc Address 2: 10.0.0.8 foo-services_node-exporter.rjz4b1n4w62hb4m0ghih8s33l.mnsr87po5wftbfc4jz62jatuw.foo-services_foo-services
[/simterm]
И проверяем targets в Prometheus на swarm-manager-е:
Настройка federation
Возвращаемся к хосту jm-prometheus-server, и обновляем основной Prometeus-сервер — в prometheus.yml указываем federation, полностью сейчас он выглядит так:
global:
scrape_interval: 15s
evaluation_interval: 15s
alerting:
alertmanagers:
- static_configs:
- targets:
rule_files:
scrape_configs:
- job_name: 'prometheus'
scheme: http
static_configs:
- targets:
- node-exporter:9100
- prometheus-server:9090
- job_name: 'federate-foo-services'
honor_labels: true
metrics_path: '/federate'
params:
'match[]':
- '{job="foo-services"}'
static_configs:
- targets:
- 10.11.100.197:9090
Запускаем:
[simterm]
root@jm-prometheus-server:/home/setevoy# docker-compose -f prometheus-stack.yml up -d Starting setevoy_node-exporter_1 ... done Starting setevoy_prometheus-server_1 ... done
[/simterm]
Проверяем на нём targets:
И метрики из Swarm:
ОК, продолжаем.
Запуск cAdvisor и NGINX
(тут я рестартовал систему на ноутбуке, так что далее в посте IP на скришотах и в конфигах поменялись)
Добавим ещё два сервиса в Swarm — cAdvisor и NGINX.
На менеджер-ноде обновляем foo-services.yml, добавляем сервис cAdvisor, деплоим его globally, и сервис nginx, который деплоим только на worker и который будем «мониторить» и выполнять над ним relabeling:
...
cadvisor:
image: google/cadvisor
networks:
- foo-services
ports:
- 8080:8080
volumes:
- /:/rootfs:ro
- /var/run:/var/run:rw
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
deploy:
mode: global
resources:
limits:
memory: 100M
nginx-service:
image: nginx
networks:
- foo-services
ports:
- 80:80
deploy:
placement:
constraints:
- node.role == worker
Обновляем prometheus.yml — добавляем сбор метрик из cAdvisor:
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'foo-services'
scrape_interval: 15s
scrape_timeout: 10s
metrics_path: /metrics
scheme: http
dns_sd_configs:
- names:
- tasks.node-exporter
refresh_interval: 30s
type: A
port: 9100
- names:
- tasks.cadvisor
refresh_interval: 30s
type: A
port: 8080
Обновляем стек:
[simterm]
root@jm-swarm-manager:/home/setevoy# docker stack deploy -c foo-services.yml foo-services Updating service foo-services_prometheus-server (id: yqqoxacvgak3gvo1pqi0v4igr) Updating service foo-services_node-exporter (id: 1gm920gcb5ifk1pbzjtm2pv2x) Creating service foo-services_cadvisor Creating service foo-services_nginx-service
[/simterm]
Проверяем:
[simterm]
root@jm-swarm-manager:/home/setevoy# docker service ls ID NAME MODE REPLICAS IMAGE PORTS 7q5rc0pc0jah foo-services_cadvisor global 2/2 google/cadvisor:latest isr4n51ranmv foo-services_nginx-service replicated 1/1 nginx:latest *:80->80/tcp ylqsbcxa9s4x foo-services_node-exporter global 2/2 prom/node-exporter:latest *:9100->9100/tcp xbeolaxggjvo foo-services_prometheus-server replicated 1/1 prom/prometheus:latest *:9090->9090/tcp
Relabeling
Relabeling позволяет переопределять labels для таргетов и метрик, которые собираются Prometheus.
Замена значений меток
На скриншоте выше в Labels («метки») мы видим instance=»10.0.0.*:9100«:
С помощью relabaling мы можем тут изментиь имя на что-то более понятное, чем далее можно пользоваться при составлении графиков и настроек алертов, т.к. сам Prometheus для подключения использует данные из лейбла __address__. Если метка instance не содержит значения — Prometheus подставит в неё значение из метки __address__.
Итак — заменим label «instance» со значения __address__ на значение из мета-метки __meta_dns_name, которая доступна для dns_sd_configs.
Редактируем prometheus.yml на jm-swarm-manager, где работает наш «вторичный» Prometheus сервер, и добавляем relabel_configs:
...
scrape_configs:
- job_name: 'foo-services'
scrape_interval: 15s
scrape_timeout: 10s
metrics_path: /metrics
scheme: http
relabel_configs:
- source_labels: [__meta_dns_name]
target_label: instance
...
Перезапускаем сервис, проверяем:
Редактирование меток
Но теперь в Graphs мы не сможем определить на каком именно интансе большая нагрузка на CPU:
Тут можно добавить ещё одну метку, по которой мы будем выполнять идентификацию сервисов.
Для начала — вырежем tasks из имени интанса — используем replacement:
...
relabel_configs:
- source_labels: [__meta_dns_name]
regex: '^tasks\.(.*).*'
target_label: instance
replacement: ${1}
...
И, к примеру — добавим к метке instance приватный IP и порт контейнера:
...
relabel_configs:
- source_labels: [__meta_dns_name, __address__]
separator: ';'
regex: '^tasks\.(.*).*;(.*)'
target_label: instance
replacement: ${1}:${2}
...
Добавление меток
Но так выглядит не слишком приятно, да и сортировка не слишком удобная — просто добавим новую метку «nodeip»:
...
relabel_configs:
- source_labels: [__meta_dns_name]
separator: ';'
regex: '^tasks\.(.*).*'
target_label: instance
replacement: ${1}
- source_labels: [__address__]
target_label: "nodeip"
...
Удаление метрик по имени
Кроме relabel_configs у Prometheus так же есть возможность работы с собираемыми метриками, используя metric_relabel_configs.
Используя его можно «на входе» удалять ненужны метрики, что бы не забивать базу Prometheus ненужными данными.
Например — удалять все метрики с метками container_network_receive_errors_total и container_network_transmit_errors_total.
Проверяем список метрик сейчас:
Обновляем конфиг, добавляем metric_relabel_configs и ction: drop:
...
relabel_configs:
- source_labels: [__meta_dns_name]
separator: ';'
regex: '^tasks\.(.*).*'
target_label: instance
replacement: ${1}
- source_labels: [__address__]
target_label: "nodeip"
metric_relabel_configs:
- source_labels: [__name__]
regex: '(container_network_receive_errors_total|container_network_transmit_errors_total)$'
action: drop
...
Перезапускаем, проверяем:
Всё — метрик container_network_receive_errors_total и container_network_transmit_errors_total нет.
Удаление метрик по метке
Кроме удаления метрики полностью — можно удалять только часть результатов.
Например — удалить все результаты метрики container_network_tcp_usage_total, у которых есть метка id со значением /system.slice*:
Обновляем конфиг:
...
metric_relabel_configs:
- source_labels: [__name__]
regex: '(container_network_receive_errors_total|container_network_transmit_errors_total)$'
action: drop
- source_labels: [id]
regex: '^/system.slice.*$'
action: drop
...
Проверяем:
Удаление метки
Кроме всего прочего — можно удалить и метку, например — если в ней содержится секретная информация, которую вы не хотите делать доступной.
Вырежем метку container_label_com_docker_swarm_node_id:
Обновляем конфиг, и используем labeldrop:
...
- regex: 'container_label_com_docker_swarm_node_id'
action: labeldrop
...
Проверяем:
См. больше действий в документации>>>.
blackbox-exporter
blackbox-exporter может использоваться для проверки удалённых сервисов по HTTP(S), DNS, ICMP и TCP.
Добавим его для проверки кода ответа запущенного ранее NGINX, плюс будем выполнять ping удалёного хоста.
Для настройки проверок у blackbox-exporter имеется свой файл конфигурации, в котором описываются «модули», которые далее могут использоваться файле настроек Prometheus сервера.
На хосте jm-swarm-manager создаём файл настроек для blackbox-exporter, например blackbox-exporter.yml, в котором добавляем модули:
modules:
icmp:
prober: icmp
timeout: 5s
http_200_module:
prober: http
timeout: 5s
http:
valid_status_codes: []
method: GET
no_follow_redirects: false
fail_if_ssl: false
fail_if_not_ssl: false
fail_if_not_matches_regexp:
- "DevOps"
Модуль icmp — всё понятно, а в модуле http_200_module мы проверяем ответ сервера (valid_status_codes: [] — по умолчанию 200), а в fail_if_not_matches_regexp — ищем слово «DevOps» в ответе (оно есть в заголовке и meta теге RTFM блога).
Добавляем запуск контейнера в Compose файл — foo-services.yml:
...
blackbox-exporter:
image: prom/blackbox-exporter
command: '--config.file=/config/blackbox.yml'
# for debug
#command: '--config.file=/config/blackbox.yml --log.level=debug'
networks:
- foo-services
volumes:
- /home/setevoy/blackbox-exporter.yml:/config/blackbox.yml
...
Теперь для Prometheus надо создать новую job, в которой используем модули blackbox:
...
- job_name: 'blackbox'
metrics_path: /probe
params:
module:
- http_200_module
- icmp
static_configs:
- targets:
- nginx-service
- http://rtfm.co.ua
- https://rtfm.co.ua
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: blackbox-exporter:9115
Перезапускаем cтек, и проверяем:
blackbox-exporter отдаёт набор метрик probe-*:
И результат проверки fail_if_not_matches_regexp: "DevOps":
В теле ответа от NGINX в Swarm слова «DevOps» нет, потому статус «Down«, а rtfm.co.ua — «Up«.
Запросы от Prometheus в логах RTFM:
194.***.***.45 — — [05/Apr/2018:15:35:07 +0300] «GET / HTTP/1.1» 200 125349 «-» «Go-http-client/1.1» «-» «rtfm.co.ua» sn=»rtfm.co.ua» rt=0.619 ua=»unix:/var/run/rtfm-php-fpm.sock» us=»200″ ut=»0.609″ ul=»125358″ cs=-
Больше примеров конфигурации blackbox-exporter — в его example-config.
Обновление корневого Prometheus
Все эти настройки мы выполнили на «вторичном» Prometheus — теперь надо всё это добавить на основной сервер, на котором далее уже будем настраивать Alertmanager и Grafana.
Возвращаемся к хосту jm-prometheus-server, обновляем его prometheus.yml, добавляем ещё одно условие в match:
...
- job_name: 'federate-foo-services'
honor_labels: true
metrics_path: '/federate'
params:
'match[]':
- '{job="foo-services"}'
- '{job="blackbox"}'
...
Перезапускаем, проверяем:
ОК — все метрики с удалённого Prometheus есть на нашем основном сервере.
Теперь, используя их, тут можно настроить отправку сообщений об ошибках и графики.
Alertmanager
Prometheus сервер так же отвечает за алерты. Алерты указываются в файле настроек, который он считывает, обрабатывает правила, и при наступлении определённого события — отправляет уведомление Alertmanager, который будет запущен в отдельном контейнере.
Сам Alertmanager уже занимается обработкой алертов — он может переслать алерт, отправить уведомление по почте или в Slack и т.д.
Интеграция Alertmanager и Slack
Получение Slack API URL
Переходим в WebHooks Slack:
Кликаем Add configuration, выбираем канал или человека:
Жмём Add integration, и получаем URL Webhook-a:
В Slack должно придти уведомление:
Настройка alerts
Для начала создадим базовый файл правил с примером из документации, назовём его alert.rules:
groups:
- name: example
rules:
- alert: InstanceDown
expr: up == 0
for: 1s
labels:
severity: page
annotations:
summary: "Instance {{ $labels.instance }} down"
description: "{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 1 second."
Настройки уведомлений
Далее создаём файл настроек alertmanager_config.yml, который будет использоваться самим Alertmanager для определения настроек уведомлений, документация тут>>>:
global: smtp_smarthost: 'mail.domain.tld:25' smtp_from: '[email protected]' smtp_auth_username: 'user' smtp_auth_password: 'pass' route: repeat_interval: 1h receiver: default receivers: - name: 'default' email_configs: - to: '[email protected]' slack_configs: - api_url: https://hooks.slack.com/services/T16***ZAj
Запуск Alertmanager
Обновляем Compose файл prometheus-stack.yml на хосте jm-prometheus-server с основным Prometheus сервером — добавляем маппинг файла alert.rules в контейнер с Prometheus, и запуск Alertmanager:
...
prometheus-server:
image: prom/prometheus
networks:
- prometheus
ports:
- 9090:9090
volumes:
- /home/setevoy/prometheus.yml:/etc/prometheus/prometheus.yml
- /home/setevoy/alert.rules:/etc/prometheus/alert.rules
...
alertmanager:
image: prom/alertmanager
ports:
- 9093:9093
volumes:
- /home/setevoy/alertmanager_config.yml:/etc/alertmanager/config.yml
networks:
- prometheus
command:
- '--config.file=/etc/alertmanager/config.yml'
Обновляем файл настроек Prometheus сервера — указываем URL Alertmanager-а, и файл с правилами алертов:
...
alerting:
alertmanagers:
- static_configs:
- targets:
- alertmanager:9093
rule_files:
- "alert.rules"
...
Перезапускаем стек, и проверяем:
Теперь — «уроним» сервис foo-services_prometheus-server на хосте jm-swarm-manager:
[simterm]
root@jm-swarm-manager:/home/setevoy# docker service rm foo-services_prometheus-server foo-services_prometheus-server
[/simterm]
Проверяем статус в targets на основном сервере мониторинга:
И проверяем Alerts — http://10.11.100.199:9090/alerts:
Сообщение в Slack:
Alert-ы и PromQL
Теперь, когда мы настроили базовые увеломления — можно рассмотреть другие примеры создания алертов.
В условиях уведомлений мы видели выражение вида expr: up == 0, которое выполняется к базе данных Prometheus, делается выборка значений up для каждого инстанса и выполняется сравнение полученного Value и заданого значения, тут это 0. Если инстанс вернул up == 0 — значит инстанс down, и триггер на алерт срабатывает.
Для составления запросов — используется язык PromQL (Prometheus Query Language).
count()
Пример использования count — берём метрику container_last_seen:
В ответе получаем список меток и их Value:
container_last_seen{container_label_com_docker_stack_namespace=»foo-services»,container_label_com_docker_swarm_service_id=»0ivi6banbzeeufe2b2lokd6ng»,container_label_com_docker_swarm_service_name=»foo-services_cadvisor»,container_label_com_docker_swarm_task_id=»gvlbtdljc37yj8sgsq36aj1t9″,container_label_com_docker_swarm_task_name=»foo-services_cadvisor.y22iw8hjn37vn4hg3im0217z8.gvlbtdljc37yj8sgsq36aj1t9″,id=»/docker/adc9958b46eb79f7e3dd9385e1499bfacd331e43cd4df2c73d134c57c4440a5f»,image=»google/cadvisor:latest@sha256:9e347affc725efd3bfe95aa69362cf833aa810f84e6cb9eed1cb65c35216632a»,instance=»cadvisor»,job=»foo-services»,name=»foo-services_cadvisor.y22iw8hjn37vn4hg3im0217z8.gvlbtdljc37yj8sgsq36aj1t9″,nodeip=»10.0.0.13:8080″}
В Value передаётся время в виде unix-time (кол-во секунд с January 1, 1970 UTC) с момента, когда Prometheus последний раз видел контейнер.
Выбираем метку, по которой хотим сделать выборку, например — container_label_com_docker_swarm_service_name, и выполняем count по следущему условию:
count(container_last_seen{container_label_com_docker_swarm_service_name=~"^foo-services_nginx-service$"})
Для проверки — увеличиваем кол-во контейнеров с NGINX в Docker Swarm (был 1):
[simterm]
root@jm-swarm-manager:/home/setevoy# docker service scale foo-services_nginx-service=5 foo-services_nginx-service scaled to 5 overall progress: 5 out of 5 tasks 1/5: running [==================================================>] 2/5: running [==================================================>] 3/5: running [==================================================>] 4/5: running [==================================================>] 5/5: running [==================================================>] verify: Service converged
[/simterm]
Проверяем:
[simterm]
root@jm-swarm-manager:/home/setevoy# docker service ps foo-services_nginx-service ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS 75b98zrj71yi foo-services_nginx-service.1 nginx:latest jm-swarm-worker Running Running 4 minutes ago r7hnaptsdtm7 foo-services_nginx-service.2 nginx:latest jm-swarm-worker Running Running 3 minutes ago 8c63j3e9acjk foo-services_nginx-service.3 nginx:latest jm-swarm-worker Running Running 3 minutes ago 9ujlqgyoywsu foo-services_nginx-service.4 nginx:latest jm-swarm-worker Running Running 3 minutes ago s1nj49fz0hv0 foo-services_nginx-service.5 nginx:latest jm-swarm-worker Running Running 3 minutes ago
[/simterm]
Проверяем Value выражения count(container_last_seen{container_label_com_docker_swarm_service_name=~"^foo-services_nginx-service$"}):
Теперь — добавим условие в alert.rules:
...
- alert: nginxCountLessThen5
expr: count(container_last_seen{container_label_com_docker_swarm_service_name=~"^foo-services_nginx-service$"}) < 5
for: 1s
labels:
severity: alarm
annotations:
summary: "Instance {{ $labels.instance }} down"
description: "{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 10 seconds."
Перезапускаем Prometheus, проверяем Alerts:
Уменьшаем кол-во контейнеров с NGINX:
[simterm]
root@jm-swarm-manager:/home/setevoy# docker service scale foo-services_nginx-service=1 foo-services_nginx-service scaled to 1 overall progress: 1 out of 1 tasks 1/1: running [==================================================>] verify: Service converged
[/simterm]
Ждём обновления значения:
И получаем сработавший алерт:
absent() и комбинирование
В алерте InstanceDown есть одна проблема: если метрики от инстанса не получаются вообще (когда контейнер «убит» и cAdvisor не возвращает метрики с тегами этого интанса) — то правило не сработает:
Что бы избежать этого — в alert.rules можно добавить условие or и absent():
...
- alert: nginxCountLessThen5
expr: count(container_last_seen{container_label_com_docker_swarm_service_name=~"^foo-services_nginx-service$"}) < 5 or absent(container_last_seen{container_label_com_docker_swarm_service_name=~"^foo-services_nginx-service$"})
for: 1s
labels:
severity: alarm
annotations:
summary: "Instance {{ $labels.instance }} down"
description: "{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 10 seconds."
...
Функция absent() проверяем наличие метрики по фильтру, а or заставляет алерт сработать при выполнении одного из условий.
time()
Теперь рассмотрим пример использования time() для определения того, что контейнер не отвечает.
Функция time() возвращает время с 1 января 1970 (unix-epoch) по настоящий момент.
Метрика container_last_seen — возвращает кол-во секунд с 1 января 1970 до момента, когда Prometheus получил последний результат (выполнил успешний srape) от таргета.
Проверяем — выполняем:
container_last_seen{container_label_com_docker_swarm_service_name="foo-services_nginx-service"}
и:
time()
Справа, в колонке Value, получаем секунды и на основе этих данных можем выяснить время с момента последнего успешного результата, выполнив запрос вида:
time() - container_last_seen{container_label_com_docker_swarm_service_name="foo-services_nginx-service"}:
Справа внизу — 5 секунд.
Теперь, зная, что scrape_interval у нас 15 секунд, мы можем узнать, что контейнер упал (перестал отвечать), если интервал запроса выше станет более 15 секунд.
Обновляем alert.rules:
...
- alert: nginxNoReply15Sec
expr: (time() - container_last_seen{container_label_com_docker_swarm_service_name=~"^foo-services_nginx-service$"}) > 15
for: 5s
labels:
severity: alarm
annotations:
summary: "Instance {{ $labels.instance }} does not reply over 15 seconds"
Перезапускаем, проверяем алерты:
На nginxCountLessThen5 не обращаем внимания — в Compose дефолтное значение 1 сервис, а алерт делался на < 5.
Теперь — стопаем NGINX на jm-swarm-manager:
[simterm]
root@jm-swarm-manager:/home/setevoy# docker service rm foo-services_nginx-service foo-services_nginx-service
[/simterm]
Проверяем значения в graphs:
И алерты:
Slack:
alert_relabel_configs
Для самого Alertmanager можно выполнить отдельную настройку меток.
Сейчас алерт nginxNoReply15Sec в сообещнии Slack передаёт кучу меток — избавимся от них (см ниже настройку шаблонов для Slack):
Обновляем prometheus.yml, в блок alerting добавляем alert_relabel_configs:
...
alerting:
alert_relabel_configs:
- regex: '(container_label_com_docker_swarm_node_id|container_label_com_docker_swarm_service_id|container_label_com_docker_swarm_task_id|container_label_com_docker_swarm_task_name|container_label_maintainer|id|image)'
action: labeldrop
alertmanagers:
- static_configs:
- targets:
- alertmanager:9093
...
Перезапускаем, проверяем:
И Slack:
Другие примеры alert.rules
Пример уведомления для blackbox-експортёра.
Ранее уже рассматривали результута probe_success и fail_if_not_matches_regexp — если сайт не вернул слово DevOps в теле ответа — то blackbox будет считать его «упавшим»:
Используя Value из этой метрики — можем добавить правило:
...
- alert: siteDown
expr: probe_success == 0
labels:
restype: website
...
Сообщение в Slack:
Шаблоны Alertmanager
Что бы изменить сообщение в Slack — можно использовать шаблонизатор Prometheus.
Документация — тут>>> и тут>>>.
К примеру, мы хотим в Slack-сообщении передавать summary и descriptionиз правила:
- alert: siteDown
expr: probe_success == 0
labels:
restype: website
annotations:
summary: "Site {{ $labels.instance }} down"
description: "Site {{ $labels.instance }} of job {{ $labels.job }} have failed fail_if_not_matches_regexp code."
В $labels.* указываем любые существующие у нас метрики.
Переходим к файлу настроек Alertmanager, в данном примере это alertmanager_config.yml, обновляем slack_configs:
... receivers: - name: 'default' email_configs: - to: '[email protected]' slack_configs: - api_url: https://hooks.slack.com/services/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 send_resolved: true title: '[{{ .Status | toUpper }}] {{ .CommonAnnotations.summary }}' text: '{{ .CommonAnnotations.description }}'
И результат:
Что тут используется:
titleиtext: поля из Slack API.Status: поля из AlertstoUpper: функция Go для строк.CommonAnnotations— данные из структуры, переданной Prometheus на Alertmanager.
Ссылки по теме
Controlling the instance label
Monitoring a Docker Swarm Cluster with Prometheus
Practical Services Monitoring with Prometheus and Docker
Delete Time Series from Prometheus 2.0
Swarmprom — Prometheus Monitoring for Docker Swarm
Docker Swarm instrumentation with Prometheus
Monitor your applications with Prometheus
Metrics
Tracking request duration with Prometheus
Alertmanager
AlertManager not sending summary or description
Notification Template Reference
Alertmanager Notification Templating with Slack
TSDB














































