Одним из компонентов 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, куда уже включен похожий механизм поиска.