Puppet: как использовать данные из таблицы MySQL в шаблонах Puppet 3.0?
У меня есть некоторые данные, чей источник истины находится в базе данных MySQL, ожидается, что размер будет максимальным в диапазоне нескольких тысяч строк (в худшем случае), и я хотел бы использовать puppet для настройки файлов на некоторые серверы с этими данными (в основном итерация по этим строкам в шаблоне).
В настоящее время я использую Puppet 3.0.x, и я не могу изменить тот факт, что MySQL будет официальным источником этих данных. Обратите внимание, что данные поступают из внешних источников, а не из марионеток или из управляемых узлов.
Какие возможные подходы есть? Какой из них вы бы порекомендовали?
Были бы здесь полезны классификаторы внешних узлов?
Моим "последним средством" будет регулярный дамп таблицы в файл YAML и чтение этого через Hiera в шаблон Puppet, или прямой вывод таблицы в один или несколько предварительно отформатированных текстовых файлов, готовых для копирования на узлы.,
На SF есть без ответа вопрос о пользователях системы, но фундаментальная проблема, вероятно, похожа на мою - он пытается получить данные из MySQL.
2 ответа
Я иду еще дальше и имею базу данных MySQL, которая является авторитетной для всех данных сервера (на самом деле это приложение django). Вот как я их интегрирую:
Определения узлов
Есть две возможности. В настоящее время мы используем сгенерированный манифест, который включен в site.pp и содержит такие записи, как:
node "avrdb.prod.example.com" {
$sdb_status="live"
$sdb_db_type="slave"
$sdb_db_version="mysql_55"
$sdb_os="co56"
include "s_db::avrdb"
}
Но скоро нам нужно переключить это на ENC, поскольку марионетка больше не поддерживает это. ENC уже написан и использует почти тот же шаблон.
Данные сервера
Некоторые рецепты, такие как наш основной рецепт DNS, требуют дополнительных данных о серверах. Как вы предлагаете, это делается в шаблонах. Вот пример одного такого шаблона: named.conf, который должен знать все вторичные серверы имен. Он просто использует gem mysql, чтобы добраться до базы данных. Структура базы данных, конечно, не имеет значения, но полный пример обычно хорош:)
<%
require 'rubygems'
require 'mysql'
dbh = Mysql.real_connect('serverdb.prod.example.com', 'user', 'pass', 'serverdb')
int_slaves = dbh.query("SELECT ip_address, name FROM network_vip WHERE name LIKE 'idns%' ORDER BY name")
int_hosts = dbh.query("SELECT i.ip_address, a.name FROM servers_interface as i LEFT JOIN servers_asset as a ON i.asset_id=a.id WHERE a.name regexp '^idns-' and i.name='eth0'");
ext_slaves = dbh.query("SELECT ip_address, name FROM network_vip WHERE name LIKE 'edns%' ORDER BY name")
ext_hosts = dbh.query("SELECT i.ip_address, a.name FROM servers_interface as i LEFT JOIN servers_asset as a ON i.asset_id=a.id WHERE a.name regexp '^edns-*' and i.name='eth0'");
%>
options {
directory "/var/named";
dump-file "/var/named/data/cache_dump.db";
statistics-file "/var/named/data/named_stats.txt";
allow-recursion { none; };
allow-transfer { any; };
allow-query { any; };
// default for transfers-in and transfers-out is 10
transfers-in 128;
transfers-out 128;
// default for transfers-per-ns is 2
transfers-per-ns 8;
// serial-query-rate on a master has undocumented side-effects
// of notifying slaves much faster for many zones
serial-query-rate 4096;
// resolvers or authoritatives
also-notify {
// Internal slaves
<% int_slaves.each{|slave| -%>
// <%= slave[1] %>
<%= slave[0] %>;
<% }
int_slaves.data_seek(0) -%>
// Internal dns hosts
// Needed as we move all the boxes over to HA, where they
// query from eth0, not vip
<% int_hosts.each{|slave| -%>
// <%= slave[1] %>
<%= slave[0] %>;
<% }
int_hosts.data_seek(0) -%>
// External slaves
<% ext_slaves.each{|slave| -%>
// <%= slave[1] %>
<%= slave[0] %>;
<% } -%>
<% ext_hosts.each{|slave| -%>
// <%= slave[1] %>
<%= slave[0] %>;
<% }
ext_hosts.data_seek(0) -%>
};
};
include "/etc/rndc.key";
// logging
logging {
channel default_file { file "/var/log/named/named.log" versions 10 size 100m; print-time yes; print-category yes; };
channel security_file { file "/var/log/named/security.log" versions 10 size 100m; print-time yes; print-category yes; };
channel query_file { file "/var/log/named/query.log" versions 10 size 100m; print-time yes; print-category yes; };
channel debug_default { file "/var/log/named/debug.log" versions 10 size 100m; print-time yes; print-category yes; };
category general { default_file; default_debug; };
category security { security_file; };
category default { default_file; };
category queries { query_file; };
category edns-disabled { null; };
};
statistics-channels {
inet 127.0.0.1 port 8080;
};
// Do not put any zone declarations here unless they differ between views
// Put zones with data common to all views in commonzones.conf
// This view goes to all the internal nameservers
view "internal" {
match-clients {
// Internal slaves
<% int_slaves.each{|slave| -%>
// <%= slave[1] %>
<%= slave[0] %>;
<% } -%>
// Internal ds hosts
// Safety catch in case they request an AXFR on their eth0 ip
<% int_hosts.each{|slave| -%>
// <%= slave[1] %>
<%= slave[0] %>;
<% } -%>
};
zone "example.com" IN {
type master;
file "master/example.com-internal.zone";
};
// This path relative to chroot
include "/etc/commonzones.conf";
include "/etc/zones.rfc1918";
};
// This view goes to everyone else
view "others" {
match-clients { any; };
// Special zones that differ between views
zone "example.com" IN {
type master;
file "master/example.com-external.zone";
};
// This path relative to chroot
include "/etc/commonzones.conf";
include "/etc/zones.rfc1918";
};
Вы также можете использовать hiera-mysql backend. Если вы видите исходный код этого бэкэнда, вы можете увидеть, как легко создать новый или настроить его. Подобное использование hiera сделает ваш шаблон / манифест более понятным, чем использование подхода, предложенного Деннисом (который также прекрасно работает)