Ansible: роли для Docker Compose, Prometheus и node_exporter

Автор: | 10/02/2017
 

Перебирал несколько ролей в Ansible Galaxy для установки и настройки Prometheus — но в конце-концов решил делать всё по-своему.

Будем использовать Docker Compose, который будет запускать сам Prometheus и node_exporter.

Роль Docker Compose

Начнём с добавления роли docker-compose.

В корне репозитория создаём каталог roles/docker-compose/tasks:

mkdir -p roles/docker-compose/tasks

Создаём файл roles/docker-compose/tasks/main.yml:

- name: Installing docker compose
  pip:
    name: docker-compose
    state: present

pip устанавливается из роли common:

cat roles/common/tasks/main.yml
- name: Update APT
apt:
update_cache: yes
- name: Install python-apt
package:
name: python-apt
state: latest
- name: Install python-pip
package:
name: python-pip
state: latest

Добавляем вызов docker-compose в provision.yml, сейчас полностью он выглядит так:

- hosts: all
  become:
    true
  roles:
    - role: common
    - role: jnv.unattended-upgrades
      unattended_mail: user@domain.com
#    - role: jaywink.letsencrypt
#      letsencrypt_domain: "{{ inventory_hostname }}"
#      letsencrypt_email:user@domain.com
#      letsencrypt_request_www: false
#      letsencrypt_pause_services: nginx
    - role: nginx
      nginx_allow_kiev_ip: 194.***.***.45
      nginx_allow_berlin_ip: 37.***.***.130
    - role: mongrelion.docker
    - role: docker-compose

Роль letsencrypt закомментирована, что бы не вызывать её каждый раз, т.к. Lets Encrypt перестанет выполнять авторизацию вообще с сообщением «Error creating new cert :: too many certificates already issued for exact set of domains«.

Есть повод переписать и эту роль, и добавить проверку наличия сертификата перед тем, как вызывать certbot-auto.

Пушим:

git add provision.yml && git commit -m "docker compose install" && git push

Запускаем Jenkins:

...
TASK [docker-compose : Installing docker compose] ******************************
sudo
-H -S -n
changed: [dev.monitoring.domain.ms]

PLAY RECAP *********************************************************************
dev.monitoring.domain.ms      : ok=25   changed=5    unreachable=0    failed=0   

[Pipeline] }
[Pipeline] // stage

[Pipeline] }
$ docker stop --time=1 17711b30159a4f21ec5dce3cb5e7d03dabf5b0c4afa0c178e8df97d3290b126d
$ docker rm -f 17711b30159a4f21ec5dce3cb5e7d03dabf5b0c4afa0c178e8df97d3290b126d
[Pipeline] // withDockerContainer
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS

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

root@jm-monitoring-dev-vm:~# docker-compose --version
docker-compose version 1.16.1, build 6d1ac219

Роль Prometheus сервера

Следующим шагом — создадим свою роль для установки и запуска Prometheus сервера.

Создаём каталоги:

mkdir -p roles/prometheus/{tasks,handlers,files,vars}

files

В каталог files добавим compose-файл для запуска.

Создаём roles/prometheus/files/docker-compose.yml (v2, т.к. Ansible модуль docker_service  3 версию (пока) не поддерживает):

version: '2'

services:
  prometheus:
    image: prom/prometheus

    expose:
      - 9090
    ports:
      - 9090:9090

vars

Перед тем, как создать tasks — добавим несколько переменных в файл roles/prometheus/vars/main.yml:

prometheus_user: prometheus
prometheus_group: prometheus
prometheus_home: /opt/prometheus

tasks

Переходим к tasks, для запуска Docker Compose используем docker_service модуль.

Потребуется:

  1. создать пользователя
  2. создать каталог для файлов Prometheus
  3. скопировать docker-compose.yml
  4. запустить контейнер

Создаём roles/prometheus/tasks/main.yml:

- name: Create Prometheus user
  user:
    name: "{{ prometheus_user }}"
    shell: /bin/false
    groups: "{{ prometheus_group }}"

- name: Create Prometheus Opt directory
  file:
    path: "{{ prometheus_home }}"
    state: directory
    owner: "{{ prometheus_user }}"
    group: "{{ prometheus_group }}"
    mode: 0775
    recurse: yes

- name: Copy compose file
  copy:
    src: files/docker-compose.yml
    dest: "{{ prometheus_home }}/docker-compose.yml"
    owner: "{{ prometheus_user }}" 
    group: "{{ prometheus_group }}"

- name: Start Prometheus service
  docker_service:
    project_src: "{{ prometheus_home }}"

Добавляем роль в provision.yml:

...
    - role: prometheus

Вроде всё ОК — сохраняем, пушим:

git status roles/prometheus/
On branch master
Your branch is up-to-date with 'origin/master'.
Untracked files:
(use "git add <file>..." to include in what will be committed)
roles/prometheus/files/
roles/prometheus/tasks/
roles/prometheus/vars/
git add -A && git commit -m "prometheus role" && git push

Запускаем Jenkins:

...
TASK [prometheus : Create Prometheus user] *************************************
ok: [dev.monitoring.domain.ms]
sudo
-H -S -n

TASK [prometheus : Create Prometheus Opt directory] ****************************

sudo
-H -S -n
ok: [dev.monitoring.domain.ms]

TASK [prometheus : Copy compose file] ******************************************

sudo
-H -S -n
sudo
-H -S -n
ok: [dev.monitoring.domain.ms]

TASK [prometheus : Start Prometheus service] ***********************************

sudo
-H -S -n
changed: [dev.monitoring.domain.ms]

PLAY RECAP *********************************************************************
dev.monitoring.domain.ms      : ok=5    changed=1    unreachable=0    failed=0   

[Pipeline] }
[Pipeline] // stage
[Pipeline] }
$ docker stop --time=1 980dfa37ed7ce303810a73b2c5389d5dd1cd97696e63fd0aa0ee0bd73d52a36e
$ docker rm -f 980dfa37ed7ce303810a73b2c5389d5dd1cd97696e63fd0aa0ee0bd73d52a36e
[Pipeline] // withDockerContainer
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS

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

root@jm-monitoring-dev-vm:~# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS                    NAMES
78c35a2e8fc4        prom/prometheus     "/bin/prometheus -..."   About a minute ago   Up 8 seconds        0.0.0.0:9090->9090/tcp   prometheus_prometheus_1

Файл настроек:

root@jm-monitoring-dev-vm:~# docker exec -ti 78c35a2e8fc4 cat /etc/prometheus/prometheus.yml
my global config
global:
scrape_interval:     15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
scrape_timeout is set to the global default (10s).
Attach these labels to any time series or alerts when communicating with
external systems (federation, remote storage, Alertmanager).
external_labels:
monitor: 'codelab-monitor'
Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
- "first.rules"
- "second.rules"
A scrape configuration containing exactly one endpoint to scrape:
Here it's Prometheus itself.
scrape_configs:
The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
- job_name: 'prometheus'
metrics_path defaults to '/metrics'
scheme defaults to 'http'.
static_configs:
- targets: ['localhost:9090']

Каталог:

root@jm-monitoring-dev-vm:~# ls -l /opt/prometheus/
total 4
-rwxrwxr-x 1 prometheus prometheus 120 Sep 29 11:48 docker-compose.yml

Пользователь:

root@jm-monitoring-dev-vm:~# id prometheus
uid=999(prometheus) gid=998(prometheus) groups=998(prometheus)

Готово.

Продолжаем.

Роль node_exporter

node_exporter можно запустить либо как сервис на хост-системе, либо — контейнером из того же docker-compose.yml, который используется для запуска самого Prometheus, используя образ node-exporter.

Пример запуска контейнером можно посмотреть тут>>>, но т.к. всё-таки он не относится к самому стеку prometheus-сервера, а является «внешним» сервисом (как по мне, ибо  экспортёров будет много) — то пока запустим его сервисом на хосте.

Для node_exporter — вполне работает вариант из Ansible Galaxy — UnderGreen.prometheus-node-exporter.

Обновляем requirements.yml:

- src: jnv.unattended-upgrades
- src: mongrelion.docker
- src: jaywink.letsencrypt
- src: UnderGreen.prometheus-node-exporter

И provision.yml:

...
    - role: UnderGreen.prometheus-node-exporter
      prometheus_node_exporter_config_flags:
        'web.listen-address': '127.0.0.1:9100'
        'log.level': 'info'

Сохраняем, запускаем Jenkins билд, проверяем метрики node_exporter на сервере:

root@jm-monitoring-dev-vm:~# 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"} 0
go_gc_duration_seconds{quantile="0.25"} 0
go_gc_duration_seconds{quantile="0.5"} 0
go_gc_duration_seconds{quantile="0.75"} 0
go_gc_duration_seconds{quantile="1"} 0
go_gc_duration_seconds_sum 0
go_gc_duration_seconds_count 0
HELP go_goroutines Number of goroutines that currently exist.

Добавление target

Теперь переходим к настройке самого Prometheus.

Что бы получать метрики с node_exporter — нам надо добавить новый target в конфиг prometheus.yml.

В каталоге roles/prometheus/files/ создаём файл prometheus.yml, и в него добавляем таргет localhost:9100:

global:
  scrape_interval:     15s
  evaluation_interval: 15s
  external_labels:
      monitor: 'jm-dev-monitor'

rule_files:
  # - "first.rules"
  # - "second.rules"

scrape_configs:
  - job_name: 'prometheus'

    static_configs:
      - targets: ['localhost:9090']
      - targets: ['localhost:9100']

Далее — это конфиг необходимо скопировать на сервер мониторинга.

В roles/prometheus/vars/main.yml добавляем переменную prometheus_home:

...
prometheus_configs: /etc/prometheus

В roles/prometheus/tasks/main.yml добавляем создание директории для файлов настроек Prometheus и копирование файла prometheus.yml, содержимое tasks/main.yml сейчас получается следующим:

- name: Create Prometheus user
  user:
    name: "{{ prometheus_user }}"
    shell: /bin/false
    groups: "{{ prometheus_group }}"

- name: Create Prometheus Opt directory
  file:
    path: "{{ prometheus_home }}"
    state: directory
    owner: "{{ prometheus_user }}"
    group: "{{ prometheus_group }}"
    mode: 0775
    recurse: yes

- name: Create Prometheus config directory
  file:
    path: "{{ prometheus_configs }}"
    state: directory
    owner: "{{ prometheus_user }}"
    group: "{{ prometheus_group }}"
    mode: 0775
    recurse: yes

- name: Copy Prometheus config
  copy:
    src: files/prometheus.yml
    dest: "{{ prometheus_configs }}/prometheus.yml"
    owner: "{{ prometheus_user }}"
    group: "{{ prometheus_group }}"

- name: Copy compose file
  copy:
    src: files/docker-compose.yml
    dest: "{{ prometheus_home }}/docker-compose.yml"
    owner: "{{ prometheus_user }}"
    group: "{{ prometheus_group }}"

- name: Start Prometheus service
  docker_service:
    project_src: "{{ prometheus_home }}"

Теперь — надо запамить prometheus.yml в контейнер при запуске.

Prometheus по умолчанию ищет файл в /etc/prometheus — добавляем в docker-compose.yml новый volume:

...
    volumes:
      - /etc/prometheus/:/etc/prometheus/

Что бы Prometheus мог обращаться к localhost хоста как localhost саого контейнера — устанавливает тип сети host (см. network_mode)

В результате docker-compose.yml получается сейчас таким:

version: '2'

services:
  
  prometheus:

    network_mode: "host"

    image: prom/prometheus
    expose:
      - 9090
    ports:
      - 9090:9090
    volumes:
      - /etc/prometheus/:/etc/prometheus/

Вроде ОК — пушим, запускаем билд Jenkins и проверяем targets:

Метрики в graphs:

В целом — на это пока всё.

Далее надо будет:

  1. обновить Azure Resource group — добавить подключение диска, в котором будут хранится данные Prometheus
  2. добавить Grafana
  3. добавить и настроить Alertmanager
  4. cAdvisor или container-exporter для мониторинга Docker на самом хосте мониторинга
  5. и наконец-то начать подключать внешние хосты сервисы под мониторинг

Вроде всё…