Зеркалируем форумы по-взрослому
Не секрет что в нашей деревне трафик стоит на порядок (кто не знает - "порядок" - это степень десяти :) ), а то и на пару дороже чем в москве и вообще во всей цивилизованной европе, а в японии, говорят, вообще бесплатно :) .
Потому, если есть возможность всякое трафигогенерирующее и притом не сильно требующее присмотра - лучше услать в москву, европу или вообще в японию :).
Итак, имеем два сервера.
Постановка задачи.
Не секрет что в нашей деревне трафик стоит на порядок (кто не знает - "порядок" - это степень десяти :) ), а то и на пару дороже чем в москве и вообще во всей цивилизованной европе, а в японии, говорят, вообще бесплатно :) .
Потому, если есть возможность всякое трафигогенерирующее и притом не сильно требующее присмотра - лучше услать в москву, европу или вообще в японию :).
Итак, имеем два сервера.
Один стоит у нас здесь, под боком, второй в мастерхосте (ахтунг, реклама!) - очень качественный хостинг провайдер, который за всего то пару сотен американских рублей согласился приютить наш сервер. В нашей деревне трафик этого сервера стоил бы в разы дороже. :(
Первый сервер назовем сервер1, второй сервер (кто догадается?, правильно) сервер2.
Теперь самое главное - сервер1 - это сервер с апачем и форумом, причем форум для определенности - Инвижн, база соответственно MySQL.
Хотим чтобы местные клиенты (чей трафик нам дешев или вообще бесплатен) - ходили на сервер1, а всякие москвичи и другие японцы - на мастерхостовский сервак (раз у них трафик такой дешевый :) ).
Сложно? начинаем думать про всякие "hosts - сказать клиентам чтоб поправили", "копировать базу раз в день - типа будет почти онлайн" ?
НИФИГА! Это не есть путь правильного пертса!
Наш ответ - правильно настроенный DNS и репликация баз.
Ну вот, все сказал, теперь даже неинтересно :).
Решение задачи
1. Правильно настроенный днс
Пусть у нас форумы для определенности имеют адрес www.forum.ru. Нам нужно чтобы для местных клиентов выдавался адрес сервера1, а для всех остальных адрес сервера2
Идем на http://www.isc.org и находим по адресу http://www.isc.org/sw/bind/arm93/ сцылку на доки. Внимательно внимательно читаем, и понимаем - ключевое слово - view !
То есть это механизм который позволяет в зависимости от ип-адреса клиента который делает днс-запрос выдавать разную инфу
Пример - пусть наши местные сетки это адреса 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 и, например, 85.234.0.0/19. Им надо отдавать в качестве адреса форумов адрес сервера1, всем остальным - адрес сервера2.
Вот так примерно выглядит /etc/named.conf
options { directory "/var/named"; }; acl trusted { trusted_dns_serv_ip1; trusted_dns_serv_ip2; trusted_dns_serv_ip3; // ... если надо - добавляем столько сколько надо ип-адресов серверов // кому будет разрешено схавать с нас зону полностью - ну т.е. описываем слейвы 10.100.100.2; //по поводу этого адреса хинт позже - запомните его! }; view "local_clients" { // вью для местных клиентов match-clients { 10.0.0.0/8; 172.16.0.0/12; 192.168.0.0/16; // ... по желанию можно добавить еще местных клиентов 85.234.0.0/19; }; recursion yes; // разрешаем кроме всего прочего местным делать рекурсивные запросы, // а всем остальным - только запросы в зоны которые мы сами и обслуживаем, ничего лишнего zone "." IN { // для местных клиентов чтобы рекурсивные запросы работали - описываем // самое что ни на есть начало интернета, то бишь рутовые днс-сервера :) type hint; file "caching-example/named.ca"; }; zone "forum.ru" IN { type master; file "master/forum.ru-local"; allow-update { none; }; allow-transfer { trusted; }; // разрешаем полностью забирать зону только доверенным ип-адресам }; // в файлике /var/named.master/forum.ru-local - будет описание зоны forum.ru для местных клиентов include "/etc/named.common.conf"; // в файле /etc/named.common.conf будут описания всех зон, // которые одинаково описываются и для "местных" и для "чужих" }; view "outside_clients" { // настройка "вью" для всех остальных клиентов match-clients { any; }; // сюда попадут все остальные что не попали в предыдущие вью (причем view ведь может быть и не два, а больше :) ) recursion no; // рекурсивные запросы от "чужих" выполнять не будем - у них свои днс сервера для этого должны быть, // потому в этом вью и зону с рутовыми днс-серверами не описываем zone "forum.ru" IN { type master; file "master/forum.ru-outside"; allow-update { none; }; allow-transfer { trusted; }; }; // в файлике /var/named.master/forum.ru-outside - будет описание зоны forum.ru уже для "чужих" товарищей include "/etc/named.common.conf"; // здесь уже понятно - в /etc/named.common.conf будут описания всех зон, // которые одинаково описываются и для "местных" и для "чужих" };
Теперь сами файлы /var/named.master/forum.ru-local и /var/named.master/forum.ru-outside Шапки опускаю, перехожу сразу к описанию хостов. Файлик /var/named.master/forum.ru-local
$ORIGIN forum.ru. www 86400 IN A ип_адрес_сервера1И описание файлика /var/named.master/forum.ru-outside
$ORIGIN forum.ru. www 86400 IN A ип_адрес_сервера2
Ну вот собственно и все... Казалось бы :)
Это мы настроили мастера. А нужно ведь настраивать и слейв. НО!!! Если тупо прописать его как обычный слейв - он ведь возьмет только ту зону которая описана в соответсвующем для него вью, т.е. если его ип попадает в описание view "local_clients" он сольет зону которая лежит в forum.ru-local и соответственно если попадает в view "local_clients" то подхватится описание зоны из forum.ru-local.
Конечно же, ничего не мешает точно так же сделать несколько вью, и ручками скопировать недостающий файлик forum.ru-outside или forum.ru-local, и затем, при каждом изменении в зоне на мастере его редактировать и на слейве, но ведь это не путь настоящего пертса? Я ведь прав?
Для этого делаем такой финт - поднимем туннельный интерфейс с ип-адресом из другого view. :)
Не поняли? Обьясняю.
Пусть ип-адрес слейва не попадает в эти диапазоны для "local_clients" (допустим Слейв_ИП)
match-clients { 10.0.0.0/8; 172.16.0.0/12; 192.168.0.0/16; 85.234.0.0/19; };
Заводим простейший туннель. На слейве
modprobe ipip iptunnel add tunl1 mode ipip remote Мастер_ИП local Слейв_ИП ifconfig tunl1 10.200.200.2 netmask 255.255.255.252А на мастере соответственно
modprobe ipip iptunnel add tunl2 mode ipip remote Слейв_ИП local Мастер_ИП ifconfig tunl1 10.200.200.1 netmask 255.255.255.252
Т.е. выбран простой тип туннеля - "ipip", для его поддержки подгружаем модуль (если что - необходимо подгрузить его один раз :) ) Номера туннелей могут не совпадать. Более подробно я конечно хотел рассказать в статьях про недетский рутинг в линуксе... Но забыл/забил...
На этот интерфейс навесили адрес, который уже попадает в диапазоны для "local_clients". И соответственно при таком /etc/named.conf на слейве
... // мышь отгрызла view "local_clients" { match-clients { 10.0.0.0/8; 172.16.0.0/12; 192.168.0.0/16; 85.234.0.0/19; }; zone "forum.ru" IN { type slave; file "slave/forum.ru-local"; masters { 10.200.200.1; }; }; include "/etc/named.common.conf"; }; view "outside_clients" { match-clients { any; }; recursion no; include "/etc/named.common.conf"; zone "forum.ru" IN { type slave; file "slave/forum.ru-outside"; masters { Мастер_ИП; }; }; };
все изменения на мастере будут применятся и на слейве, причем тоже в файлах для разных вью. Если адреса мастера и слейва у вас относятся к другим диапазонам - нужно если надо поменять порядок вью, или даже добавить нужный отдельный вью друг для друга для днс-серверов. Помните что порядок вью имеет значение и первый применяется первым.
Итак, с днсами разобрались. Нужным клиентам выдаются нужные адреса и все это делается автоматически и максимально удобно.
Приступаем ко второй части
2. Репликация баз данных
Итак, если бы у нас был статический контент - все было бы уже решено, что уж статические хтмл-ки то не зазеркалить? :)
Но у нас форум, причем инвижновский. Который все данные хранит в базе. MySQL.
Ага! скажут самые ленивые, давай мы скрипты форумов настроим на одну и ту же базу, выберем например на сервере2 и все.
Но нет, строго ответим мы. Какой толк от такого решения - трафик то не сэкономится! Представьте, куча локальных клиентов делают запрос странички с сервера1 (днс то мы им правильно настроили :) )
Сервер1 обращается N раз (по кол-ву запросов) к мускульной базе сервера2 - а это между прочим то же трафик и трафик нехилый, и только после этого сформированные странички с сервера1 попадут к местным клиентам.
Что же делать? (кто виноват вы и так все знаете - Билл Гейтс! :) )
Надо очень хитро синхронизировать базы между собой - может даже придумать какой-то хитрый механизм. Вы удивитесь, но такой механизм был придуман уже давно :). Хотя в мускуле появился _относительно_ недавно :)
Называется он репликация.
Вот про нее я и распишу, относительно конкретной конечно же задачи - задаче про зеркалирование форумов.
Тем более схема, которую я реализовывал на тот момент, в доке была описана нехотя, более того, говорилось вроде даже что типа работать может и будет, но болеть будет часто. Сейчас мускул уже подрос, стал сильным, 5 ветка уже пошла, даже триггеры хранимые процедуры и т.п. - все в общем как у взрослых появилось. Но я настраивал на 4.0.x, а начинал вообще на 3.23.x
Для начала опять-таки очень очень рекомендую почитать доки http://dev.mysql.com/doc/refman/4.1/en/replication.html
Идея очень простая - один из серверов будет у нас работать и как мастер и одновременно как слейв
На Сервере1 выполняем командочки
mysql> GRANT REPLICATION SLAVE ON invb_db.* TO 'user_for_repl_slave_on_serv1'@'ИП_Сервера2' IDENTIFIED BY 'pass_for_repl_slave_on_serv1'; mysql> flush privileges;обьяснять где выполняем эти команды надеюсь не надо :)
и дорисовываем такую конфигу в /etc/my.cnf
server-id = 2 master-host = сервер2 master-user = user_for_repl_slave_on_serv2 master-password = pass_for_repl_slave_on_serv2 log-bin replicate-do-db=invb_db slave-skip-errors=1062,1053
На Сервере2 соответственно выполняем
mysql> GRANT REPLICATION SLAVE ON invb_db.* TO 'user_for_repl_slave_on_serv2'@'ИП_Сервера1' IDENTIFIED BY 'pass_for_repl_slave_on_serv2'; mysql> flush privileges;
и дорисовываем аналогичную Серверу1 конфигу в /etc/my.cnf
server-id = 3 master-host = сервер1 master-user = user_for_repl_slave_on_serv1 master-password = pass_for_repl_slave_on_serv1 log-bin replicate-do-db=invb_db slave-skip-errors=1062,1053
log-bin - означает что все меняющие (INSERT UPDATE DELETE) базу операции будут писаться в специальный лог-файл, который как раз будет читать слейв и применять к своей копии базы.
При этом, эти операции на слейве по умолчанию не попадают в собственный log-bin (туда попадают только "собственные" операции). За включение отвечает опция --log-slave-updates
Я намеренно не говорю что будут записыватся и другие операции, типа CREATE DROP ALTER и т.п. - будем считать что структура базы у нас не меняется и таких операций просто не будет. А если и будет - в принципе у меня работало :)
replicate-do-db=invb_db - Этой командой мы указываем реплицировать только одну базу (если у нас их много и вдруг проскочит команда для неизвестной слейву таблицы - слейв остановит процесс репликации)
slave-skip-errors=1062,1053 - Этим мы указываем игнорировать ошибки вида "Duplicate entry '%s' for key %d" и "Server shutdown in progress"
Т.е. первый тип ошибки - это дублированные данные. А кто сказал что будет легко? :). Все делается на грани фола :). Поскольку инвижн частенько пишет во всякие "временные" таблицы состояния пользователей - для одного ид будет разные записи :). Тут дело в том что инвижн использует ключи для таблицы "кто в онлайне" которые могут пересекатся. На таблице постов все ок. Зато появляется следующая фича, когда заходите на сервер и смотрите кто в онлайне на форумах - всегда видно юзеров с вашего же сервера :)
Теперь надо озаботиться чтобы на обоих серверах была одинаковая копия базы - как это сделать, описывать не буду, это в принципе простая задача, либо если MyISAM - остановить базу и тупо скопировать бинарные файлы, если InnoDB - чуток сложнее, закрыть сначала базу для изменений
mysql> FLUSH TABLES WITH READ LOCK;
Затем слить через mysqldump или LOAD DATA FROM MASTER - кому как удобнее
Ну а дальше все просто, запускаем мускул на обоих серверах, смотрим что показывает show master status; show slave status
Если на сервере2 что-то вроде этого
mysql> show master status; +-------------------+----------+--------------+------------------+ | File | Position | Binlog_do_db | Binlog_ignore_db | +-------------------+----------+--------------+------------------+ | сервер2-bin.051 | 12913646 | | | +-------------------+----------+--------------+------------------+ 1 row in set (0.00 sec) mysql> show slave status; +-------------+------------------------------+-------------+---------------+-----------------+---------------------+-----------------------+---------------+ | Master_Host | Master_User | Master_Port | Connect_retry | Master_Log_File | Read_Master_Log_Pos | Relay_Log_File | Relay_Log_Pos | +-------------+------------------------------+-------------+---------------+-----------------+---------------------+-----------------------+---------------+ | ип_сервер1 | user_for_repl_slave_on_serv1 | 3306 | 60 | сервер1-bin.001 | 236602231 | сервер2-relay-bin.051 | 17609694 | +-------------+------------------------------+-------------+---------------+-----------------+---------------------+-----------------------+---------------+ +-----------------------+------------------+-------------------+-----------------+---------------------+------------+------------+--------------+---------------------+-----------------+ | Relay_Master_Log_File | Slave_IO_Running | Slave_SQL_Running | Replicate_do_db | Replicate_ignore_db | Last_errno | Last_error | Skip_counter | Exec_master_log_pos | Relay_log_space | +-----------------------+------------------+-------------------+-----------------+---------------------+------------+------------+--------------+---------------------+-----------------+ | сервер1 -bin.001 | Yes | Yes | invb_db | | 0 | | 0 | 236602231 | 17609694 | +-----------------------+------------------+-------------------+-----------------+---------------------+------------+------------+--------------+---------------------+-----------------+ 1 row in set (0.00 sec)
А на другом сервере1 что-то вроде этого
mysql> show master status; +-----------------+-----------+--------------+------------------+ | File | Position | Binlog_do_db | Binlog_ignore_db | +-----------------+-----------+--------------+------------------+ | сервер1-bin.001 | 236597467 | | | +-----------------+-----------+--------------+------------------+ 1 row in set (0.00 sec) mysql> show slave status; +-------------+------------------------------+-------------+---------------+-----------------+---------------------+-----------------------+---------------+ | Master_Host | Master_User | Master_Port | Connect_retry | Master_Log_File | Read_Master_Log_Pos | Relay_Log_File | Relay_Log_Pos | +-------------+------------------------------+-------------+---------------+-----------------+---------------------+-----------------------+---------------+ | ип_сервер2 | user_for_repl_slave_on_serv2 | 3306 | 60 | сервер2-bin.051 | 12912968 | сервер1-relay-bin.001 | 127136254 | +-------------+------------------------------+-------------+---------------+-----------------+---------------------+-----------------------+---------------+ +-----------------------+------------------+-------------------+-----------------+---------------------+------------+------------+--------------+---------------------+-----------------+ | Relay_Master_Log_File | Slave_IO_Running | Slave_SQL_Running | Replicate_do_db | Replicate_ignore_db | Last_errno | Last_error | Skip_counter | Exec_master_log_pos | Relay_log_space | +-----------------------+------------------+-------------------+-----------------+---------------------+------------+------------+--------------+---------------------+-----------------+ | сервер2-bin.051 | Yes | Yes | invb_db | | 0 | | 0 | 12912968 | 127136254 | +-----------------------+------------------+-------------------+-----------------+---------------------+------------+------------+--------------+---------------------+-----------------+ 1 row in set (0.00 sec)
То это значит что теперь ваши форумы зеркалируются совсем по-взрослому. А если репликация еще и не сломается после первого поста ;) - то вообще все зашибись.
P.S. чтоб совсем все было в шоколоде - можно еще всякий аплоуды с форумов зазеркалировать - но это совсем просто, идея такая - периодически составляется список нужных файлов на каждом сервере, забивается в базу или просто список генерится, сравнивается со своим и нужные файлы скачиваются.