Тема: DNS slave и записи в MySQL
Танцы с бубном продолжаются...
Что мы имеем, pdns сервер с записями в mysql и несколько bind'ов которые используют его как мастера. Все бы хорошо да вся эта цепочка глючит так как в бинды надо добавлять новые зоны достаточно часто и обновлять. Зон окото 10000 и в день 200-300 меняются, плюс появляются новые и умирают старые. Рещение простое, сделать еще пару pdns серверов, которые будут кешировать записи из главного и выдавать их. Проблема основная в том что нельзя включить бинарные логи, а следовательно нельзя использовать репликации.
Задача простая, надежность и дуракоустойчивость.
Прилюдия: все работает так как работает и менятся неможет, тоесть mysql с записями есть, обновляется через спецсофт и берет оттуда все pdns. это неменяется ни при каких обстоятельствах.
Задачи:
1. Поднять вторичный mysql который будит переодичиски синхронизировать записи и выступать их, гарант работы в случае если загнется первый
2. DNS сервер должен искать записи во вторичном мускуле и при его отказе спрашивать записи у главного сервера. Это для того чтобы уменьшить нагрузку.
Поехали.
== Mysql
Поскольку репликации использовать я немогу, появилась мысля использовать federated tables (кто-то мне пытался расказать что с ними нельзя организовать некоторые действия, так вот, можно, было бы желание...)
Основная система собиралась в джаиле из портов, никакой самодеятельности, все по мануалу, мне еще работать с этим, а не дыры латать. После сборки mysqla (кстати freebsd не потому что я большой патриот, первоначально все планировалось на centos, просто работа этой системы на линуксе меня неустроила, а именно mysql тупит метсами) создаем пользователя на mysql-master.domain.com (назавем его так) и вытаскиваем mysqldumpом те таблицы которые я хочу перенести на mysql-slave.domain.com. Таблицы три
dm_record, dm_zone, pdns_data
CREATE TABLE `dm_record` (
`record_id` bigint(20) unsigned NOT NULL auto_increment,
`zone_id` int(10) unsigned NOT NULL default '0',
`name` varchar(255) NOT NULL default '',
`ttl` int(10) unsigned default NULL,
`class` varchar(5) NOT NULL default '',
`rr_type` tinyint(3) unsigned NOT NULL default '0',
`value` varchar(255) NOT NULL default '',
`type_txt` varchar(10) NOT NULL default '',
`priority` int(10) unsigned default NULL,
`request_id` int(10) unsigned default NULL,
`is_edit_allowed` tinyint(3) unsigned default '1',
`descr` varchar(255) default '',
`ptr_direct_zone` varchar(255) default NULL,
PRIMARY KEY (`record_id`),
KEY `record__request_id` (`request_id`),
KEY `record__name` (`name`),
KEY `record__zone_id` (`zone_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;CREATE TABLE `dm_zone` (
`zone_id` int(10) unsigned NOT NULL auto_increment,
`account_no` int(10) unsigned NOT NULL default '0',
`zone_type` tinyint(3) unsigned NOT NULL default '0',
`zone` varchar(255) NOT NULL default '',
`is_manual` tinyint(4) default NULL,
`is_active` tinyint(4) default '1',
PRIMARY KEY (`zone_id`),
KEY `zone__zone` (`zone`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;CREATE TABLE `pdns_data` (
`zone_id` int(10) unsigned NOT NULL default '0',
`notified_serial` int(10) unsigned default NULL,
PRIMARY KEY (`zone_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
эти три таблицы создаем как фетеральные на mysql-slave.domain.com. Тоесть меняем тока конец таблицы, сама она остается идентичной
CREATE TABLE `pbas_dm_record` (
`record_id` bigint(20) unsigned NOT NULL auto_increment,
`zone_id` int(10) unsigned NOT NULL default '0',
`name` varchar(255) NOT NULL default '',
`ttl` int(10) unsigned default NULL,
`class` varchar(5) NOT NULL default '',
`rr_type` tinyint(3) unsigned NOT NULL default '0',
`value` varchar(255) NOT NULL default '',
`type_txt` varchar(10) NOT NULL default '',
`priority` int(10) unsigned default NULL,
`request_id` int(10) unsigned default NULL,
`is_edit_allowed` tinyint(3) unsigned default '1',
`descr` varchar(255) default '',
`ptr_direct_zone` varchar(255) default NULL,
PRIMARY KEY (`record_id`),
KEY `record__request_id` (`request_id`),
KEY `record__name` (`name`),
KEY `record__zone_id` (`zone_id`)
) ENGINE=FEDERATED DEFAULT CHARSET=utf8 CONNECTION='mysql://rpdns:[email protected]/db/dm_record';CREATE TABLE `pbas_dm_zone` (
`zone_id` int(10) unsigned NOT NULL auto_increment,
`account_no` int(10) unsigned NOT NULL default '0',
`zone_type` tinyint(3) unsigned NOT NULL default '0',
`zone` varchar(255) NOT NULL default '',
`is_manual` tinyint(4) default NULL,
`is_active` tinyint(4) default '1',
PRIMARY KEY (`zone_id`),
KEY `zone__zone` (`zone`)
) ENGINE=FEDERATED DEFAULT CHARSET=utf8 CONNECTION='mysql://rpdns:[email protected]/db/dm_zone';CREATE TABLE `pbas_pdns_data` (
`zone_id` int(10) unsigned NOT NULL default '0',
`notified_serial` int(10) unsigned default NULL,
PRIMARY KEY (`zone_id`)
) ENGINE=FEDERATED DEFAULT CHARSET=utf8 CONNECTION='mysql://rpdns:[email protected]/db/pdns_data';
Собственно говоря все, основная задача сделана, данные с мастера доступны на слайве. Теперь надо сделать кеширование, так как эти таблиды доступны только при установленном конекте, тоесть упал мастер - неработает слайв. Для этого создаем еще раз эти таблицы, но уже как MyISAM
CREATE TABLE `buffer_dm_record` (
`record_id` bigint(20) unsigned NOT NULL auto_increment,
`zone_id` int(10) unsigned NOT NULL default '0',
`name` varchar(255) NOT NULL default '',
`ttl` int(10) unsigned default NULL,
`class` varchar(5) NOT NULL default '',
`rr_type` tinyint(3) unsigned NOT NULL default '0',
`value` varchar(255) NOT NULL default '',
`type_txt` varchar(10) NOT NULL default '',
`priority` int(10) unsigned default NULL,
`request_id` int(10) unsigned default NULL,
`is_edit_allowed` tinyint(3) unsigned default '1',
`descr` varchar(255) default '',
`ptr_direct_zone` varchar(255) default NULL,
PRIMARY KEY (`record_id`),
KEY `record__request_id` (`request_id`),
KEY `record__name` (`name`),
KEY `record__zone_id` (`zone_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;CREATE TABLE `buffer_dm_zone` (
`zone_id` int(10) unsigned NOT NULL auto_increment,
`account_no` int(10) unsigned NOT NULL default '0',
`zone_type` tinyint(3) unsigned NOT NULL default '0',
`zone` varchar(255) NOT NULL default '',
`is_manual` tinyint(4) default NULL,
`is_active` tinyint(4) default '1',
PRIMARY KEY (`zone_id`),
KEY `zone__zone` (`zone`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;CREATE TABLE `buffer_pdns_data` (
`zone_id` int(10) unsigned NOT NULL default '0',
`notified_serial` int(10) unsigned default NULL,
PRIMARY KEY (`zone_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
и делаем процедуру которая при живом мастеер обновляет запси каждые 10 минут:
DELIMITER ;;
DROP PROCEDURE IF EXISTS `update_from_pbas` ;;
CREATE PROCEDURE `update_from_pbas`()
DETERMINISTIC
begin
declare x int;
declare pbas_alive boolean default true;
declare curx cursor for select record_id from pdns.pbas_dm_record;
declare continue handler for SQLSTATE 'HY000' set pbas_alive = false;
open curx;if (pbas_alive) then
replace into pdns.buffer_dm_record select * from pdns.pbas_dm_record;
replace into pdns.buffer_dm_zone select * from pdns.pbas_dm_zone;
replace into pdns.buffer_pdns_data select * from pdns.pbas_pdns_data;
end if;
end ;;DELIMITER ;
и еще одну, которая каждую ночь удаляем все и создает заного, чтобы очистить таблицу от уже несуществующих зон.
DELIMITER ;;
DROP PROCEDURE IF EXISTS `delandupdate_from_pbas` ;;
CREATE PROCEDURE `delandupdate_from_pbas`()
DETERMINISTIC
begin
declare x int;
declare pbas_alive boolean default true;
declare curx cursor for select record_id from pdns.pbas_dm_record;
declare continue handler for SQLSTATE 'HY000' set pbas_alive = false;
open curx;if (pbas_alive) then
delete from pdns.buffer_dm_record;
replace into pdns.buffer_dm_record select * from pdns.pbas_dm_record;
delete from pdns.buffer_dm_zone;
replace into pdns.buffer_dm_zone select * from pdns.pbas_dm_zone;
delete from pdns.buffer_pdns_data;
replace into pdns.buffer_pdns_data select * from pdns.pbas_pdns_data;
end if;
end ;;DELIMITER ;
после чего в крон добавляем
*/10 * * * * root /usr/local/bin/mysql -se 'call update_from_pbas;' pdns >/dev/null 2>&1
25 4 * * * root /usr/local/bin/mysql -se 'call delandupdate_from_pbas;' pdns >/dev/null 2>&1
== PDNS
pdns.conf
daemon=yes
distributor-threads=10
launch=gmysql
loglevel=5
query-logging=yes
recursor=dns-master.domain.com:53
setgid=pdns
setuid=pdns
slave=slave
gmysql-user=pdns
gmysql-dbname=pdns
gmysql-password=pass
gmysql-socket=/tmp/mysql.sock
gmysql-info-all-master-query=SELECT z.zone_id, z.zone, NULL, NULL, p.notified_serial, 'MASTER' FROM buffer_dm_zone z LEFT JOIN buffer_pdns_d
ata p ON (z.zone_id = p.zone_id) WHERE z.is_active > 0
gmysql-info-all-slaves-query=SELECT * from buffer_dm_zone where 1 = 2
gmysql-update-serial-query=REPLACE INTO buffer_pdns_data (notified_serial, zone_id) VALUES (%d, %d)
gmysql-info-zone-query=SELECT z.zone_id, z.zone, NULL, NULL, p.notified_serial, 'MASTER' FROM buffer_dm_zone z LEFT JOIN buffer_pdns_data p
ON (z.zone_id = p.zone_id) WHERE z.zone = '%s' and z.is_active > 0
gmysql-list-query=SELECT r.value, r.ttl, r.priority, r.type_txt, r.zone_id, r.name FROM buffer_dm_record r, buffer_dm_zone z WHERE z.zone_id
= r.zone_id and r.zone_id = %d and z.is_active > 0
gmysql-id-query=SELECT r.value, r.ttl, r.priority, r.type_txt, r.zone_id, r.name FROM buffer_dm_record r, buffer_dm_zone z WHERE z.zone_id =
r.zone_id and r.type_txt = '%s' and r.name = '%s' and r.zone_id = %d and z.is_active > 0
gmysql-any-query=SELECT r.value, r.ttl, r.priority, r.type_txt, r.zone_id, r.name FROM buffer_dm_record r, buffer_dm_zone z WHERE z.zone_id
= r.zone_id and r.name = '%s' and z.is_active > 0
gmysql-any-id-query=SELECT r.value, r.ttl, r.priority, r.type_txt, r.zone_id, r.name FROM buffer_dm_record r, buffer_dm_zone z WHERE r.zone_
id = z.zone_id and r.name = '%s' and z.is_active > 0
gmysql-basic-query=SELECT r.value, r.ttl, r.priority, r.type_txt, r.zone_id, r.name FROM buffer_dm_record r, buffer_dm_zone z WHERE z.zone_i
d = r.zone_id and r.type_txt = '%s' and r.name = '%s' and z.is_active > 0
Все!
Вопросы/Пожелания/Предлажения принимаются