Puppet: Маленькие хитрости

Puppet-590x310

Одним из компонентов puppet, с которым мы работаем на стороне сервера является — внешний классификатор узла (External Node Classifier).

External Node Classifier

Этот компонент принимает имя узла и возвращает простую структуру данных (как правило в YAML формате), содержащую спецификацию конфигурации для данного узла.

# ./ENC.rb youhost.ru
---
classes:
- puppet
- ca-cert
- sudo
- ntp
- nis
- local-users
- ssh
- yumrepo
- bash-completion
- monit
- monit::puppet
- vmware-guest

Целью этого компонента является установление того, к каким функциональным классам принадлежит указанный узел и того, какие параметры должны быть использованы для конфигурации этих классов.

В моем случае ENC является ruby скрипт. Для успешной работы которого необходимо задать 2 опции в в /etc/puppet/puppet.conf

[master]
  node_terminus  = exec
  external_nodes = /etc/puppet/ENC.rb

Некоторые особенности реализации данного скрипта, я попытаюсь описать чуть ниже.

Одним из свойств языка Puppet, связанным с переменными, является интеграция с инструментом получения информации о машине — facter. Эта утилита возвращает информацию, специфичную для данной машины, в виде пар «ключ-значение», которые в Puppet превращаются в одноименные переменные.

Мы можем использовать эти специфичные значения при работе ENC, например для того что-бы ставить vmware tools только на виртуальные машины или от типа операционной системы разворачивать только нужные локальные репозитарии.

Для начала в /etc/puppet/auth.conf необходимо убедиться в наличии доступа, нас интересует путь «/facts»

path /facts
auth any
method find, search
allow localhost

Непосредственно ruby код «сборщика»:

#!/usr/bin/ruby
 
require 'yaml'
require 'uri'
require 'net/http'
 
# Fetch facts from puppet master
URL     = "https://localhost:8140/production/facts/"
CA_PATH = "/var/lib/puppet/ssl/ca/ca_crt.pem"
 
NODE    = ARGV.first
 
ca_path = ENV[ 'PUPPET_CA_PATH' ] || CA_PATH
url     = ENV[ 'PUPPET_URL' ] || URL
 
uri = URI.parse( "#{url}#{NODE}" )
require 'net/https' if uri.scheme == 'https'
request = Net::HTTP::Get.new( uri.to_s, initheader = { 'Accept' => 'yaml' } )
http = Net::HTTP.new( uri.host, uri.port )
if uri.scheme == 'https'
  http.use_ssl = true
  http.ca_file = ca_path
  http.verify_mode = OpenSSL::SSL::VERIFY_PEER
end
res = http.start { |http| http.request( request ) }
 
node = YAML::parse( res.body )
facts = node.select( "values" )[0]

Если вернуться к определению виртуализации, то нам необходим фактор vmware.rb

require 'facter'
 
# vmware installed?
Facter.add("vmtools_installed") do
  setcode do
    File.exists?('/usr/bin/vmware-toolbox-cmd')
  end
end
 
# vmware-running?
Facter.add("vmtools_version") do
  setcode do
    Facter::Util::Resolution::exec('/usr/bin/vmware-toolbox-cmd -v')
  end
end

Непосредственное добавление класса в ENC, будет выглядеть примерно так, где classes — это выходной массив:

# Add hypervisor specific classes for virtual environments
if facts["is_virtual"].value && ( facts["operatingsystem"].value == 'CentOS' || facts["operatingsystem"].value == 'RedHat' )
  case facts["virtual"].value
    when "vmware" then classes << 'vmware-guest'   
end 
end 
puts YAML.dump( "classes" => classes )

Задаем классы puppet через базу данных

Как уже писалось выше, часть логики по навешиванию классов на объект, можно задать в ENC: описывать хосты в массивах, после чего проверять вхождение и вешать классы.

snmp_host = [
 'host1.youdomain.ru',
 'host2.youdomain.ru',
]
 
if snmp_host.include?( NODE )
  classes >> 'snmp'
end

Можно поступить иначе — хранить привязки во внешней системе, например в базе данных.

url = 'https://youdomain.ru/api/getclasses.php?node='
uri = URI.parse( "#{url}#{NODE}" )
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Get.new(uri.request_uri)
response = http.request(request)
 
 
node2 = YAML::load( response.body )
node3 = node2["classes"]
if !node3.nil?
    classes = classes | node3
end

Php скрипт для отдачи выглядит достаточно просто, массив $aClasses по названию хоста наполняется данными и отдается через yaml_emit:

$aClasses= array('classes'=>array('test','2','3'));
echo yaml_emit($aClasses);

Для удобства пользования, можно создать web форму, например через php jqgrid

Puppet резервные копии файлов (filebucket)

Puppet достаточно сильно снижает энтропию, приводя конфигурацию к определенному виду.
Этот вид не всегда соответствует действительности, посему мы будем делать резервные копии всех файлов именных с помощью Puppet.

Делается это штатным механизмом под названием filebucket.

В /etc/puppet/manifests/site.pp прописываем следующие строчки (предварительно заменив puppethost.ru на свой хост):

filebucket { 'main':
  path   => false,                # This is required for remote filebuckets.
  server => 'puppethost.ru', # Optional; defaults to the configured puppet master.
}
 
File { backup => main }

При изменении файла, все изменения записываются на сервер, md5 хеш-сумма измененного файла как правило находится в логе messages клиента:

Jun 10 09:21:38 host puppet-agent[25430]: (/Stage[main]/Test/File[/tmp/test.conf]/ensure) defined content as '{md5}e1c06d85ae7b8b032bef47e42e4c08f9'
Jun 10 09:22:14 host puppet-agent[29880]: (/Stage[main]/Test/File[/tmp/test.conf]/content) content changed '{md5}edb7c24a038cc9c9cb40e791e50ce407' to '{md5}e1c06d85ae7b8b032bef47e42e4c08f9'

Найти файлы до изменения можно при помощи небольшого скрипта, зная md5 хеш-сумму:

find /var/lib/puppet/bucket -name paths -execdir pwd \; -execdir cat {} \; > bucketindex.txt

Как вариант можно установить Puppet Dashboard, куда уже включен похожий механизм поиска.

Вы можете оставить комментарий ниже.