Translate

пʼятниця, 19 серпня 2016 р.

Puppet. Arrays of values from Hiera

Це вже буде п'ята стаття про Puppet, котру я розміщую у своєму блозі. Раніше я писав про masterless конфігурацію, клієнт-сервер і навіть про установку та конфігурацію Foreman, що по замовчуванню також працює з Puppet. Наразі піде мова про інструмент організації ієрархії Hiera.

Якщо говорити коротко і не вдаваючись в подробиці, Hiera - це спосіб опису змінних що стосуються окремого хосту чи групи хостів. Тобто змінні, що оголошені для групи віртуальних машин, можуть перезаписуватись іншими значеннями, що описані для конкретної машини, рівнем вище. На практиці це виглядає так:

# cat hiera.yaml

:backends:
  - yaml
:yaml:
  :datadir: /etc/puppet/hieradata
:merge_behavior: deeper
:hierarchy:
  - "nodes/%{::environment}/%{::hostname}"
  - "%{::environment}"
  - "versions"

Тобто yaml-файли, що знаходяться вище в переліку hierarchy мають вищий пріоритет оголошення змінних. Наприклад, якщо змінна variable буде описана в nodes/dev.my.com/host.yaml та nodes/dev.my.com.yaml то пріоритетнішим буде вважатись значення, що описане в nodes/dev.my.com/host.yaml ("nodes/%{::environment}/%{::hostname}").

Звісно, що на вхід ці дані приймає Puppet і, в залежності від значень змінних, виконує відповідні інструкції. Надалі в цій статті піде мова про те, як оголосити асоціативний масив (в термінології Python - це словник) в Hiera і яку користь з цього можна отримати.

Як приклад демонстрації цього оберемо задачу по налаштуванню програми Сurator, за допомогою якої ми будемо проводити очистку старих індексів Elasticsearch.

Опишемо в Хієрі для тестового хосту імена індексів та кількість днів, на протязі яких індекси необхідно зберігати:

# cat host.yaml
...
elasticsearch_curator4::indices:
  - name:'logstash'
    retain_days:'30'
  - name:'docker'
    retain_days:'60'
  - name:'.marvel'
    retain_days:'45'
  - name:'test'
    retain_days:'2'
  - name: 'app-index'
    retain_days:'12'
...

Відповідно управляти цими змінними можна для кожного хосту окремо, основні маніфести чіпати не потрібно. Опишемо основний маніфест для цієї задачі:

# vim /etc/puppet/modules/elasticsearch_curator4/manifests/elasticsearch_curator4.pp

# Class: elasticsearch_curator4
#
# This module manages installation of elasticsearch curator (cleaner of old indexes)
#
class elasticsearch_curator4 (
  $indices = undef,
){

  include installs::elasticsearch_curator

  define create_curator(
    $indices,
    $hour       = 3,
    $minute     = 0,
    $timestring = "'%Y-%m-%d'",
  ) {

    $retain_days = $indices[$name][retain_days]

    file { "/etc/elasticsearch-curator4/clean_${name}.yml":
      ensure  => 'file',
      content => template('actionfile.yml.erb'),
    }

    cron { "Cron for cleaning ${name} indices":
      command => "/usr/local/bin/curator --config /etc/elasticsearch-curator4/config.yml \
      /etc/elasticsearch-curator4/clean_${name}.yml 2>&1 | logger -t CURATOR",
      hour    => $hour,
      minute  => $minute,
    }
  }

  file { '/etc/elasticsearch-curator4':
    ensure => 'directory',
  }->

  file { '/etc/elasticsearch-curator4/config.yml':
    content => source('config.yml'),
  }

  $indices_array = keys($indices)
  # For debugging only
  notify {"\nThis is all our indices ${indices}\n":}
  notify {"This are keys of indices ${indices_array}\n":}

  create_curator { $indices_array:
    indices => $indices,
  }
}

Дещо прокоментую наведений вище код. Змінна $indices йде як аргумент до класу elasticsearch::elasticsearch_curator4. У разі, якщо вона оголошена в Hiera, її значення буде змінене. У нашому випадку це якраз і відбувається, адже для хосту host ми оголосили значення elasticsearch_curator4::indices. Далі створюємо масив ключів із асоціативного масиву $indices, що буде збережений в змінній $indices_array. Наступні notify якраз все і покажуть:

Notice: This are keys of indices logstashdocker.marveltestapp-index

Notice: This is all our indices {"logstash"=>{"retain_days"=>"30"}, "docker"=>{"retain_days"=>"60"}, ".marvel"=>{"retain_days"=>"45"}, "test"=>{"retain_days"=>"2"}, "app-index"=>{"retain_days"=>"12"}}

Далі створений масив ключів передаємо як аргумент створеній функції create_curator. Для кожного значення з масиву $indices_array буде виконуватись функція create_curator і щоразу аргументом буде передаватись повний асоціативний масив $indices. Для кожного ключа з $indices_array буде отримано його значення (значення протягом скількох останніх днів не видаляти індекси):

$retain_days = $indices[$name][retain_days]

І власне це значення та назва ключа з $indices_array буде брати участь в створенні окремого clean_${name}.yml для кожного імені індексу зі списку $indices_array та буде поставлена окрема cron-задача для їх запуску. actionfile.yml.erb - темплейт, що також заповнюється змінними, вже згаданими вище:

# vim /etc/puppet/modules/elasticsearch_curator4/templates/actionfile.yml.erb

actions:
  1:
    action: delete_indices
    description: >-
      Delete indices older than <%= retain_days %> days (based on index name), for <%= @name %>
      prefixed indices. Ignore the error if the filter does not result in an
      actionable list of indices (ignore_empty_list) and exit cleanly.
    options:
      ignore_empty_list: True
      timeout_override:
      continue_if_exception: False
      disable_action: False
    filters:
    - filtertype: pattern
      kind: prefix
      value: <%= @name %>-
      exclude:
    - filtertype: age
      source: name
      direction: older
      timestring: <%= timestring %>
      unit: days
      unit_count: <%= retain_days %>
      exclude:

# vim /etc/puppet/modules/elasticsearch_curator4/files/config.yml

client:
  hosts:
    - 127.0.0.1
  port: 9200
  use_ssl: False
  http_auth:
  timeout: 30
  master_only: False

logging:
  loglevel: INFO
  logfile:
  logformat: default

include installs::elasticsearch_curator4 - це маніфест для установки пакету elasticsearch-curator, що по-суті і виконує всю корисну роботу:

# cat /etc/puppet/modules/installs/elasticsearch_curator4.pp

class installs::elasticsearch_curator4(
  $version = 'latest',
) {
  require installs::python-pip

  python::pip { 'elasticsearch-curator':
    ensure  => $version,
    pkgname => 'elasticsearch-curator'
  }
}

Під час роботи маніфесту можна якраз і спостерігати як створюються всі необхідні крон задачі та actionfile.yml для кожного індексу:

# puppet agent -tv
...
Info: Applying configuration version '1471599547
...
Notice: /Stage[main]/Elasticsearch_curator4/Elasticsearch_curator4::Create_curator[.marvel]/File[/etc/elasticsearch-curator4/clean_.marvel.yml]/ensure: defined content as '{md5}9b3a045079a881eae932ca738f55954e'
...
Notice: /Stage[main]/Elasticsearch_curator4/Elasticsearch_curator4::Create_curator[.marvel]/Cron[Cron for cleaning .marvel indices]/ensure: created
...
Notice: Finished catalog run in 40.99 seconds

Попередньо можна перевірити свої маніфести на відповідність стандартам написання та на синтаксичну правильність:

# puppet-lint /etc/puppet/modules/installs/elasticsearch_curator4.pp
WARNING: class not documented on line 1
...
WARNING: string containing only a variable on line 37
ERROR: trailing whitespace found on line 93
...

# puppet parser validate /etc/puppet/modules/installs/elasticsearch_curator4.pp

Якщо варто виконати ту ж дію для декількох змінних (список файлів, імена індексів і т.п.) - можна скористатись звичайним масивом. Для цього yaml в Hiera має виглядати наступним чином:

...
rabbitmq::plugins:
- 'rabbitmq_shovel'
- 'rabbitmq_shovel_management'

А сам маніфест буде виглядати так (його адреса в файловій системі звісно має збігатись з назвою змінної в Hiera):

# Installs RabbitMQ and dependencies
class rabbitmq(
  $cluster_nodes = undef,
  $erlang_cookie = undef,
  ...
  $plugins       = undef,
) {
  ...

  define enable_plugin() {
    rabbitmq_plugin { $name:
      ensure   => present,
      provider => 'rabbitmqplugins',
      require  => Class['rabbitmq::install'],
      notify   => Service['rabbitmq-server'],
    }
  }

  if $plugins {
    enable_plugin { $plugins: }
  }

Змінні rabbitmq::plugins по черзі потраплять у якості аргументу в функцію enable_plugin(). У разі ж пустого масиву rabbitmq::plugins, функція, завдяки if-у, не буде запускатись.

У erb-темплейт список змінних можна додати так:

...
<% if @plugins -%>
list_of_rabbitmq_plugins: [ <%= @plugins.join(',') %> ]
<% end -%>

У разі, якщо змінна plugins, що рівна змінній rabbitmq::plugins і описана в Hiera, буде оголошена, в текстовий файл буде додано наступне:

list_of_rabbitmq_plugins: rabbitmq_shovel, rabbitmq_shovel_management

Попередньо звісно необхідно також оголосити ресурс для цього, щось на зразок:

  file { '/tmp/rabbit_plugins_list':
    content => template('some_path/rabbit_plugin_list.erb'),
  }

Ну власне і все. Надіюсь, комусь будуть корисні наведені вище приклади.

Посилання:
https://docs.puppet.com/hiera/3.2/lookup_types.html
http://tuxmea.blogspot.com/2014/01/puppet-and-hiera-hashes.html
https://gist.github.com/awaxa/beb8d68405bb696628b6
https://ask.puppet.com/question/1133/retrive-list-from-hiera/
http://codingbee.net/tutorials/puppet/puppet-retrieving-data-from-yaml-files-using-hiera/

Немає коментарів:

Дописати коментар