Задача – настроить 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
What is a Time Series Database?