Ansible: Prometheus provisioning – роли common, exim, nginx, logrotate и unattended-upgrades

Автор: | 14/05/2018
 

Продолжение сетапа из AWS: создание стека в AWS – bash-скрипт и CloudFormation шаблон.

Далее потребуется добавить Ansible playbook и роли для настройки хоста.

В принципе – ничего особенно отличного от того, что описано в постах серии RTFM migration, например – Ansible: миграция RTFM 2.9 – монтирование EBS и настройка NGINX на Bastion.

Что надо будет выполнить:

  1. добавить роль common, в которой будет выполняться:
    • монтирование data-диска (EBS)
    • установка набора утилит – mailutils, curl, dnsutils, telnet, unzip
    • установка hostname
    • копирование .bashrc и .vimrc на хост
    • настройка таймзоны
    • настройка почтового ящика root
  2. роль exim для настройки отправки уведомлений
  3. роль nginx, понятно для чего (пока без SSL)
  4. роль manala.logrotate
  5. роль jnv.unattended-upgrades

А далее можно будет приступить к созданию ролей для самого стека Prometheus.

hosts

Начинаем с создания файла hosts.

Имени для сервера пока нет, поэтому используем IP:

[all:vars]
data_volume_mount_path="/data"

[btrm-mon-dev]
52.***.***.233

[btrm-mon-dev:vars]
data_volume_id="/dev/xvdb"
set_hostname="btrm-mon-dev"

Тут:

  • data_volume_mount_path, которая будет указывать на точку монтирования подключаемого EBS
  • data_volume_id – сам диск EBS, который подключается к системе CloudFormation
  • set_hostname – будет задано в качестве имени хоста

playbook

Теперь создадим файл плейбука – btrm-monitoring-ansible-provision.yml, пока только с одной ролью common:

- hosts: all
  become:
    true
  roles:
    - role: common

роль common

Создаём роль common – добавляем каталоги:

[simterm]

$ mkdir -p roles/common/{tasks,files}

[/simterm]

В roles/common/tasks создаём файл main.yml с одной задачей – вызывать модуль ping:

- name: Execute ping
  ping:

Время тестить!

Вызываем ansible-playbook:

[simterm]

$ ansible-playbook -i hosts --limit=btrm-mon-dev --private-key=../../aws-credentials/btrm-mon.pem btrm-monitoring-ansible-provision.yml

PLAY [all] ****

TASK [Gathering Facts] ****
fatal: [52.***.***.233]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: Permission denied (publickey).\r\n", "unreachable": true}
        to retry, use: --limit @/home/username/Work/btrm-monitoring/ansible/btrm-monitoring-ansible-provision.retry

PLAY RECAP ****
52.***.***.233               : ok=0    changed=0    unreachable=1    failed=0   

[/simterm]

ОК, отлично.

Имя пользователя. На хосте запущен AWS AMI с Debian, добавляем admin как ansible_ssh_user в hosts:

[all:vars]
data_volume_mount_path="/data"
ansible_ssh_user=admin
...

Повторяем:

[simterm]

$ ansible-playbook -i hosts --limit=btrm-mon-dev --private-key=../../aws-credentials/btrm-mon.pem btrm-monitoring-ansible-provision.yml

PLAY [all] ****

TASK [Gathering Facts] ****
ok: [52.***.***.233]

TASK [common : Execute ping] ****
ok: [52.***.***.233]

PLAY RECAP ****
52.***.***.233               : ok=2    changed=0    unreachable=0    failed=0

[/simterm]

Работает.

Хорошо, поехали дальше – добавляем задачи для роли common.

parted

Первой задачей будет создать раздел на диске /dev/xvdb, если его ещё нет.

Для этого надо будет установить саму утилиту parted, а раз так – то установим сразу всё необходимое.

Обновляем roles/common/tasks/main.yml, убираем ping, добавляем задачи – обновить кеш apt, потом обновить все установленные пакеты, потом установить набор дефолтных утилит:

- name: Upgrade all packages to the latest version
  apt:
    update_cache: yes
    upgrade: yes

- name: Install common packages
  package:
    name: "{{ item }}"
    state: present
  with_items:
       - mailutils
       - curl
       - dnsutils
       - telnet
       - unzip
       - parted

Третьей задачей вызываем parted и создаём раздел номер 1 на диске из переменной data_volume_id:

...
- name: Create partiniom on {{ data_volume_id }}
  parted:
    device: "{{ data_volume_id }}"
    number: 1
    state: present

Проверяем разделы на хосте сейчас:

[simterm]

$ ansible -i hosts btrm-mon-dev --private-key=../../aws-credentials/btrm-mon.pem -a lsblk
52.***.***.233 | SUCCESS | rc=0 >>
NAME    MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvda    202:0    0   8G  0 disk 
└─xvda1 202:1    0   8G  0 part /
xvdb    202:16   0   8G  0 disk 

[/simterm]

Проверяем синтаксис плейбука:

[simterm]

$ ansible-playbook -i hosts --syntax-check --limit=btrm-mon-dev btrm-monitoring-ansible-provision.yml

playbook: btrm-monitoring-ansible-provision.yml

[/simterm]

И запускаем применение common:

[simterm]

$ ansible-playbook -i hosts --limit=btrm-mon-dev --private-key=../../aws-credentials/btrm-mon.pem btrm-monitoring-ansible-provision.yml

PLAY [all] ****

TASK [Gathering Facts] ****
ok: [52.***.***.233]

TASK [common : Upgrade all packages to the latest version] ****
 [WARNING]: Could not find aptitude. Using apt-get instead.

ok: [52.***.***.233]

TASK [common : Install common packages] ****
changed: [52.***.***.233] => (item=mailutils)
ok: [52.***.***.233] => (item=curl)
changed: [52.***.***.233] => (item=dnsutils)
changed: [52.***.***.233] => (item=telnet)
changed: [52.***.***.233] => (item=unzip)
changed: [52.***.***.233] => (item=parted)

TASK [common : Create partition on /dev/xvdb] ****
changed: [52.***.***.233]

PLAY RECAP ****
52.***.***.233               : ok=4    changed=2    unreachable=0    failed=0

[/simterm]

Проверяем разделы теперь:

[simterm]

$ ansible -i hosts btrm-mon-dev --private-key=../../aws-credentials/btrm-mon.pem -a lsblk
52.***.***.233 | SUCCESS | rc=0 >>
NAME    MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvda    202:0    0   8G  0 disk 
└─xvda1 202:1    0   8G  0 part /
xvdb    202:16   0   8G  0 disk 
└─xvdb1 202:17   0   8G  0 part 

[/simterm]

Раздел 1 на диске xvdb создан – /dev/xvdb1.

filesystem

Далее – создаём файловую систему на /dev/xvdb1, используем модуль filesystem:

...
- name: Create a ext4 filesystem on /dev/{{ data_volume_id }}1
  filesystem:
    fstype: ext4
    dev: "{{ data_volume_id }}1"

Запускаем, проверяем:

[simterm]

$ ansible-playbook -i hosts --limit=btrm-mon-dev --private-key=../../aws-credentials/btrm-mon.pem btrm-monitoring-ansible-provision.yml
...
TASK [common : Create partition on /dev/xvdb] ****
ok: [52.***.***.233]

TASK [common : Create a ext4 filesystem on /dev/xvdb1] ****
changed: [52.***.***.233]

PLAY RECAP ****
52.***.***.233               : ok=5    changed=1    unreachable=0    failed=0

[/simterm]

mount

Следующим шагом – надо создать каталог /data, и смонтировать в него раздел /dev/xvdb1.

Используем file для создания каталога и mount – для монтирования диска:

...
- name: Create {{ data_volume_mount_path }} directory
  file:
    path: "{{ data_volume_mount_path }}"
    state: directory
    mode: 0755
    recurse: yes

- name: Mount data-volume {{ data_volume_id }}1 to {{ data_volume_mount_path }}
  mount:
    path: "{{ data_volume_mount_path }}"
    src: "{{ data_volume_id }}1"
    state: mounted
    fstype: ext4

Запускаем:

[simterm]

$ ansible-playbook -i hosts --limit=btrm-mon-dev --private-key=../../aws-credentials/btrm-mon.pem btrm-monitoring-ansible-provision.yml
...
TASK [common : Create /data directory] ****
changed: [52.***.***.233]

TASK [common : Mount data-volume /dev/xvdb1 to /data] ****
changed: [52.***.***.233]

PLAY RECAP ****
52.***.***.233               : ok=7    changed=2    unreachable=0    failed=0

[/simterm]

Проверяем:

[simterm]

$ ansible -i hosts btrm-mon-dev --private-key=../../aws-credentials/btrm-mon.pem -a "ls -l /data"
52.***.***.233 | SUCCESS | rc=0 >>
total 16
drwx------ 2 root root 16384 тра 10 15:29 lost+found

[/simterm]

hostname

Следующим шагом надо поменять имя хоста на значение, указанное в переменной set_hostname нашего файла hosts.

Кроме того, что бы задать имя хоста – надо отредактировать /etc/hosts, но т.к. это AWS EC2 – то /etc/hosts будет перезаписано, ибо при перезагрузке задаётся стартап-скриптами (см. Update Etc Hosts). Что бы отключить это – надо изменить файл /etc/cloud/cloud.cfg.d/01_debian_cloud.cfg и значение manage_etc_hosts в нём с True на False.

Добавляем ещё три задачи – задать имя хоста, добавить его в /etc/hosts для резолва на 127.0.0.1, и отключить обновление /etc/hosts:

...
- name: Set hostname
  hostname:
    name: "{{ set_hostname }}"

- name: Add hostname to /etc/hosts
  lineinfile:
    dest: /etc/hosts
    regexp: '^127\.0\.0\.1[ \t]+localhost'
    line: "127.0.0.1 localhost {{ set_hostname }}"
    state: present

- name: Update /etc/cloud/cloud.cfg.d/01_debian_cloud.cfg
  lineinfile:
    dest: /etc/cloud/cloud.cfg.d/01_debian_cloud.cfg
    regexp: '^manage_etc_hosts: true'
    line: "manage_etc_hosts: false"
    state: present

Проверяем имя сейчас:

[simterm]

$ ansible -i hosts btrm-mon-dev --private-key=../../aws-credentials/btrm-mon.pem -a hostname
52.***.***.233 | SUCCESS | rc=0 >>
ip-10-0-1-82

[/simterm]

Запускаем:

[simterm]

$ ansible-playbook -i hosts --limit=btrm-mon-dev --private-key=../../aws-credentials/btrm-mon.pem btrm-monitoring-ansible-provision.yml
...
TASK [common : Set hostname] ****
changed: [52.***.***.233]

TASK [common : Add hostname to /etc/hosts] ****
changed: [52.***.***.233]

TASK [common : Update /etc/cloud/cloud.cfg.d/01_debian_cloud.cfg] ****
changed: [52.***.***.233]

PLAY RECAP ****
52.***.***.233               : ok=10   changed=4    unreachable=0    failed=0

[/simterm]

Проверяем сейчас:

[simterm]

$ ansible -i hosts btrm-mon-dev --private-key=../../aws-credentials/btrm-mon.pem -a hostname
52.***.***.233 | SUCCESS | rc=0 >>
btrm-mon-dev

[/simterm]

Отлично.

timezone

Время выставляем с помощью модуля timezone:

...
- name: Set timezone to Europe/Kiev
  timezone:
    name: Europe/Kiev

root mail

Настраиваем почту для root.

В файл плейбука btrm-monitoring-ansible-provision.yml добавляем блок переменных и переменную notify_email:

- hosts: all
  vars:
    notify_email: [email protected]
  ...

В roles/common/tasks/main.yml добавляем обновление /etc/aliases и вызов newaliases:

...
- name: Change root mailbox
  lineinfile:
    dest: /etc/aliases
    regexp: '^root: '
    line: "root: {{ notify_email }}"
    state: present

- name: Update mail aliases
  shell:
    newaliases

Прочее

Последним для этой роли осталось скопировать .bashrc и .vimrc.

В roles/common/files/ добавляем файл bashrc:

PS1='\t \[[\e[0;COLORm\]\u\[\e[m\]@\e[0;37m\]\h\[\e[m\] \[\e[1;34m\]\w\[\e[m\]]\[\e[0;31m\] \$\[\e[m\]\[\e[0;37m\] '

export EDITOR=vim

# aliases
alias osupgrade="sudo apt-get update && sudo apt-get upgrade"

И vimrc:

set tabstop=4
set shiftwidth=4
set softtabstop=4
set expandtab
set paste
set smartindent
syntax on

Добавляем их копирование:

...
- name: Copy .bashrc to root
  copy:
    src: files/bashrc
    dest: /root/.bashrc

- name: Set username color in /root/.bashrc
  replace:
    path: /root/.bashrc
    regexp: 'COLOR'
    replace: '31'

- name: Copy .bashrc to admin
  copy:
    src: files/bashrc
    dest: /home/admin/.bashrc

- name: Set username color in /home/admin/.bashrc
  replace:
    path: /home/admin/.bashrc
    regexp: 'COLOR'
    replace: '32'

- name: Copy .vimrc to root
  copy:
    src: files/vimrc
    dest: /root/.vimrc

- name: Copy .vimrc to admin
  copy:
    src: files/vimrc
    dest: /home/admin/.vimrc

Запускаем все последние изменения:

[simterm]

$ !416                                                                                                                                           
ansible-playbook -i hosts --limit=btrm-mon-dev --private-key=../../aws-credentials/btrm-mon.pem btrm-monitoring-ansible-provision.yml
...
TASK [common : Update /etc/cloud/cloud.cfg.d/01_debian_cloud.cfg] ****
changed: [52.***.***.233]

TASK [common : Change root mailbox] ****
changed: [52.***.***.233]

TASK [common : Update mail aliases] ****
changed: [52.***.***.233]

TASK [common : Set timezone to Europe/Kiev] ****
changed: [52.***.***.233]

TASK [common : Copy .bashrc to root] ****
changed: [52.***.***.233]

TASK [common : Set username color in /root/.bashrc] ****
changed: [52.***.***.233]

TASK [common : Copy .bashrc to admin] ****
changed: [52.***.***.233]

TASK [common : Set username color in /home/admin/.bashrc] ****
changed: [52.***.***.233]

TASK [common : Copy .vimrc to root] ****
changed: [52.***.***.233]

TASK [common : Copy .vimrc to admin] ****
changed: [52.***.***.233]

PLAY RECAP ****
52.***.***.233               : ok=19   changed=10   unreachable=0    failed=0

[/simterm]

ОК.

В целом с ролью common на этом закончили.

роль exim

Ещё одной ролью будет роль для настройки exim на отправку почты с хоста (учитывая, что это хост мониторинга – весьма актуальная задача).

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

[simterm]

$ mkdir -p roles/exim/{tasks,templates}

[/simterm]

Создаём два шаблона – mailname.j2 с указанием имени хоста, которое будет использоваться MTA при отправке писем, и update-exim4.conf.conf.j2, в котором указываются настройки для Exim, что бы он мог отправлять почту “в мир (см. Exim: Mailing to remote domains not supported и Ansible: миграция RTFM 2.10 – Let’s Encrypt, NGINX SSL, hostname и exim).

Файл roles/exim/templates/mailname.j2 содержит одну строку:

{{ inventory_hostname }}

Файл update-exim4.conf.conf.j2 немного побольше:

dc_eximconfig_configtype='internet'
dc_other_hostnames='"{{ inventory_hostname }}"'
dc_local_interfaces='127.0.0.1 ; ::1'
dc_readhost=''
dc_relay_domains=''
dc_minimaldns='false'
dc_relay_nets=''
dc_smarthost=''
CFILEMODE='644'
dc_use_split_config='false'
dc_hide_mailname=''
dc_mailname_in_oh='true'
dc_localdelivery='mail_spool'

В файл roles/exim/tasks/main.yml добавляем их добавление на сервер:

- name: Update Exim4 settings
  template:
    src=templates/update-exim4.conf.conf.j2
    dest=/etc/exim4/update-exim4.conf.conf

- name: Update mailname
  template:
    src=templates/mailname.j2
    dest=/etc/mailname

И туда же добавляем ещё три задачи – смена FROM для root, перезапуск exim для применения новго файла настроек, и отправку тестового письма:

...
- name: Change root FROM address
  lineinfile:
    dest: /etc/email-addresses
    regexp: '^root: '
    line: "root: root@{{ inventory_hostname }}"
    state: present

- name: Exim4 restart
  service:
    name=exim4
    state=restarted

- name: Send test email
  shell:
    echo "Eximt4 config complete" | mailx -s "{{ inventory_hostname }} Exim4 test" "{{ notify_email }}"

Добавляем роль exim в плейбук:

- hosts: all
  vars:
    notify_email: [email protected]
  become:
    true
  roles:
    - role: common
    - role: exim

Запускаем, проверяем тестовое письмо:

роль nginx

В этой роли выполним установку NGINX и копирование файла настроек самого NGINX и файла виртуалхоста.

Для сервера уже добавлен домен, который будем использовать в NGINX – dev.monitor.domain.tld, он же добавлен в hosts.

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

[simterm]

$ mkdir -p roles/nginx/{tasks,templates}

[/simterm]

В roles/nginx/templates/ добавляем nginx.conf.j2:

user www-data;
worker_processes auto;
pid /var/run/nginx.pid;

events {
  worker_connections  1024;
}

http {

        sendfile on;
        tcp_nopush on;
        tcp_nodelay on;
        keepalive_timeout 65;
        types_hash_max_size 2048;

        include /etc/nginx/mime.types;
        default_type application/octet-stream;

        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_prefer_server_ciphers on;
        ssl_ciphers  "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH !RC4";
        ssl_session_cache   shared:SSL:10m;
        ssl_session_timeout 1h;
        ssl_stapling on;
        ssl_stapling_verify on;
        ssl_dhparam /etc/nginx/dhparams.pem;

        server_tokens off;

        access_log /var/log/nginx/access.log;
        error_log /var/log/nginx/error.log;

        log_format proxy  '[$time_local] $remote_addr - $server_name to: '
                  '$upstream_addr: $request upstream_response_time '
                  '$upstream_response_time msec $msec request_time $request_time';

        gzip on;
        gzip_disable "msie6";

        include /etc/nginx/conf.d/*.conf;
}

Он нам нужен для настроек SSL и добавления ssl_dhparam.

Сюда же добавляем шаблон для виртуахоста – roles/nginx/templates/dev.monitor.domain.tld.conf.j2:

server {

    listen       80;
    server_name  {{ inventory_hostname }};

    access_log  /var/log/nginx/{{ inventory_hostname }}-access.log proxy;
    error_log /var/log/nginx/{{ inventory_hostname }}-error.log warn;

    location / {
        root   html;
        index  index.html index.htm;
    }
}

Он пока минимальный, SSL и upstream добавим позже.

Добавляем задачи – в файле roles/nginx/tasks/main.yml выполняем установку NGINX, замену его конфига, генерацию ключа Diffie Hellman, копирование конфига виртуалхоста и перезагрузку конфигов NGINX:

- name: Install Nginx
  package:
    name: nginx
    state: latest

- name: Replace NGINX config
  template: 
    src=templates/nginx.conf.j2
    dest=/etc/nginx/nginx.conf

- name: Generate dhparams
  shell: openssl dhparam -out /etc/nginx/dhparams.pem 2048
  args:
    creates: /etc/nginx/dhparams.pem

- name: Add NGINX {{ inventory_hostname }} virtualhost config
  template:
    src=templates/{{ inventory_hostname }}.conf.j2
    dest=/etc/nginx/conf.d/{{ inventory_hostname }}.conf

- name: Service NGINX reload
  service: 
    name=nginx 
    state=restarted

В btrm-monitoring-ansible-provision.yml добавляем роль nginx:

...
  roles:
    - role: common
    - role: exim
    - role: nginx

Запускаем, проверяем:

[simterm]

$ curl -I dev.monitor.domain.com
HTTP/1.1 200 OK
Server: nginx
Date: Mon, 14 May 2018 11:39:34 GMT
...

[/simterm]

Отлично.

роль logrotate

Тут всё просто – используем готовую роль из Ansible Galaxy – manala.logrotate.

В btrm-monitoring-ansible-provision.yml добавляем:

...
...
    - role: manala.logrotate
      manala_logrotate_configs:
        - file: nginx
          config:
            - /var/log/nginx/*.log:
              - size: 100M
              - missingok
              - rotate: 5
              - compress
              - delaycompress:
              - notifempty
              - create: 0640 www-data adm
              - sharedscripts 
              - daily
              - postrotate
                  systemctl reload nginx.service
              - endscript

Создаём файл requirements.yml, добавляем зависимость:

- src: manala.logrotate

роль unattended-upgrades

Аналогично с ролью unattended-upgrades – используем jnv.unattended-upgrades, добавляем её в requirements.yml:

- src: manala.logrotate
- src: jnv.unattended-upgrades

И в btrm-monitoring-ansible-provision.yml, с настройками:

...
    - role: jnv.unattended-upgrades
      unattended_mail: "{{ notify_email }}"
      unattended_automatic_reboot: true
      unattended_automatic_reboot_time: 05:00
      unattended_clean_interval: 10

Загружаем роли:

[simterm]

$ ansible-galaxy install –ignore-certs -r requirements.yml

[/simterm]

И запускаем выполнение плейбука с последними обновлениями:

[simterm]

$ ansible-playbook -i hosts –limit=btrm-mon-dev –private-key=../../aws-credentials/btrm-mon.pem btrm-monitoring-ansible-provision.yml

[/simterm]

Проверяем:

[simterm]

root@btrm-mon-dev:/home/admin# cat /etc/apt/apt.conf.d/50unattended-upgrades | grep Upgr
// Unattended-Upgrade::Origins-Pattern controls which packages are
Unattended-Upgrade::Origins-Pattern {
Unattended-Upgrade::Package-Blacklist {
Unattended-Upgrade::Mail "[email protected]";
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "05:00";

[/simterm]

Готово.

В следующей части добавим роли для запуска самого Prometheus, Alertmanager и Grafana, потом к ним добавим екпортёры и настроим первые алерты.