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

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

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

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

Роль Docker Compose

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

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

[simterm]

$ mkdir -p roles/docker-compose/tasks

[/simterm]

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

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

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

[simterm]

$ 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

[/simterm]

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

- hosts: all
  become:
    true
  roles:
    - role: common
    - role: jnv.unattended-upgrades
      unattended_mail: [email protected]
#    - role: jaywink.letsencrypt
#      letsencrypt_domain: "{{ inventory_hostname }}"
#      letsencrypt_email:[email protected]
#      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.

Пушим:

[simterm]

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

[/simterm]

Запускаем 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

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

[simterm]

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

[/simterm]

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

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

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

[simterm]

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

[/simterm]

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

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

[simterm]

$ 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

[/simterm]

Запускаем 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

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

[simterm]

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

[/simterm]

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

[simterm]

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']

[/simterm]

Каталог:

[simterm]

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

[/simterm]

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

[simterm]

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

[/simterm]

Готово.

Продолжаем.

Роль 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 на сервере:

[simterm]

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.

[/simterm]

Добавление 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. и наконец-то начать подключать внешние хосты сервисы под мониторинг

Вроде всё…