Posterous theme by Cory Watilo

Filed under: Newbie

PowerDNS - master and slave

В связи с переездом на новый сервер пришлось переносить и кучу сервисов, которые крутились на старом. Одним из них был DNS.

Настоятельно рекомендуется делать master и slave (primary и secondary) на разных машинах, дабы в случае отказа одного из них второй мог полноценно работать. Ну это в теории, на практике все несколько упрощается до одного сервера, который если ляжет – то DNS будет уже не столь важен.

Начал подымать master на OpenSuSE 11.1. Конфиг простой и незамысловатый:

#each slave should be put here (space separated) to allow zone details transfer
allow-axfr-ips=1.1.1.2
disable-axfr=no
daemon=yes
guardian=yes
default-ttl=3600
soa-refresh-default=3600
lazy-recursion=yes
local-address=0.0.0.0
master=yes
recursor=127.0.0.1:5300
launch=gmysql
gmysql-host=localhost
gmysql-port=3306
gmysql-dbname=powerdns
gmysql-user=powerdns
gmysql-password=powerdns
gmysql-socket=/var/run/mysql/mysql.sock

и рекурсор:

chroot=/var/lib/pdns
local-address=127.0.0.1
local-port=5300
setgid=pdns
setuid=pdns

Базу для него создавал из PowerDNS on Rails, хотя можно ограничиться и той, что в документации.

И напоролся на первый занятный баг – со включенным режимом master он взлетает и долбит базу одинаковыми запросами (пытается получить все записи). На стоящей рядом ubuntu с той же версией такого не наблюдается. Долго пытался отловить что, где и почему – и решил пересобрать из сорсов. Когда обнаружилась нехватка boost я решил проверить, а нет ли готового свеженького пакета с собраным powerdns? Таки есть – надо было всего лишь добавить в списки репо вот этот:

http://download.opensuse.org/repositories/server:/dns/openSUSE_11.1/

и была установлена версия pdns-2.9.22-1.1. Проблема с нагрузкой на БД полечилась, но появилась новая – с репликацией на slave.

В PowerDNS есть такое понятие как supermaster. В общем случае нужно прописывать зону на мастере и слейве. Когда слейв получит от мастера notify, он запросит у мастера записи из этой зоны. Но если сервер, от которого пришел notify, будет обнаружен в списке supermasters, и такой зоны на слейве не будет описано, она создастся автоматически. И потом в нее будут добавлены записи с мастера. Это приятно упрощает жизнь.

В табличке запись довольно простая – ip мастера, имя слейва (ns2.example.org), имя аккаунта (на работу никак не влияет – просто будет фигурировать в описании зоны; удобно для указания клиента, например).

В порядке эксперимента попинал слейв нотификейшенами для разных доменов. Не с первого раза, но заработало:

pdns_control notify example.org

С первого раза не заработало потому, что в PowerDNS on Rails в имя поддомена по привычке вписал “@”, и потом получил записи вида “@.example.org” вместо “example.org” либо “@”. Прошелся простым апдейтом по базе и убрал этот баг.

PS: Хочу выразить благодарность Патрику Фею за его недавний пост, который помог постичь смысл supermaster'ов.

PPS: конфиг pdns для слейва мало чем отличается от мастера:

config-dir=/etc/powerdns
daemon=yes
disable-axfr=yes
guardian=yes
launch=gmysql
lazy-recursion=yes
local-address=0.0.0.0
local-port=53
module-dir=/usr/lib/powerdns
setgid=pdns
setuid=pdns
slave=yes
socket-dir=/var/run
version-string=powerdns
gmysql-host=localhost
gmysql-port=3306
gmysql-dbname=powerdns
gmysql-user=powerdns
gmysql-password=powerdns

MacOS X Leopard php extensions

Столкнулся с необходимостью добавить к PHP в макоси несколько экстеншенов. Апач в MacOS работает в x86_64. Да-да, там бинарник для нескольких архитектур:

file `which httpd`

Проблема в том что по умолчанию экстеншены будут собираться под i386. И потому из php-cli они доступны и работают, в то время как в mod_php их нет, а в логах апача видно примерно такое:

PHP Warning:  PHP Startup: Unable to load dynamic library '/usr/lib/php/extensions/no-debug-non-zts-20060613/mcrypt.so' - (null) in Unknown on line 0

Для сборки под несколько архитектур надо собирать все зависимости также под несколько архитектур. Я решил ограничиться вариантом x86_64 и i386.

Итак, в /opt/local/etc/macports/macports.conf добавим требуемые архитектуры:

sudo echo 'universal_archs x86_64 i386'>> /opt/local/etc/macports/macports.conf

Чудесно. Теперь на примере gettext.

sudo port install gettext +universal
file `which gettext`

В качестве вывода мы должны получить что-то вроде

dm@Loki ~$ file `which gettext`
/opt/local/bin/gettext: Mach-O universal binary with 2 architectures
/opt/local/bin/gettext (for architecture i386): Mach-O executable i386
/opt/local/bin/gettext (for architecture x86_64):   Mach-O 64-bit executable x86_64

Далее нужны сорсы php. В сорсах есть папка ext.

cd php-5.2.8/ext/gettext
phpize

CFLAGS="-arch i386 -arch x86_64 -g -Os -pipe -no-cpp-precomp" \
CCFLAGS="-arch i386 -arch x86_64 -g -Os -pipe" \
CXXFLAGS="-arch i386 -arch x86_64 -g -Os -pipe" \
LDFLAGS="-arch i386 -arch x86_64 -bind_at_load" \
./configure --with-gettext=/opt/local

make
sudo make install
sudo echo 'extension=gettext.so' >> /etc/php.ini
sudo apachectl restart

Предварительно убедитесь, что у Вас есть файл /etc/php.ini. По умолчанию его нет.

После этого модуль должен завестись в mod_php.

UPD: а чобы (по возможности) все из портов собиралось с флагом universal надо сделать

echo '+universal' >> /opt/local/etc/macports/variants.conf

LVM Rocks

Давно заметил что в Ubuntu (server edition) во время инсталляции начали предлагать использовать LVM. Но я все не решался поставить production на него. Затем пообщался со теми кто его использовал, почитал доку - и последний год стал его использовать, так как постиг скрытую в нем мощь :) Допустим, у нас есть простенький бюджетный сервер. Мы развернули новое приложение, и его база стала расти весьма стремительно. Итого - база, веб-файлы и система живут на одном физическом диске. Был куплен диск WD Razor, и на него перенесли базу. Нагрузка диска (iostat -x -m 1) составила 1-2%. Решено перенести туда же и веб-файлы, однако решение это пришло лишь через пару дней. Так что получилось наглядно продемонстрировать возможности LVM.

Part1. Creating...

Когда поставили разор на нем создали один раздел - LVM: [cc lang="bash"] # fdisk -l Disk /dev/sdb: 74.3 GB, 74355769344 bytes 255 heads, 63 sectors/track, 9039 cylinders Units = cylinders of 16065 * 512 = 8225280 bytes Disk identifier: 0x00000000 Device Boot Start End Blocks Id System /dev/sdb1 1 9039 72605736 8e Linux LVM [/cc] Пару слов об организации LVM. Уровень 1: volume group (vg). Это наивысший уровень абстракции, объединяющий в себе logical volumes и physical volumes. Уровень 2: physical volume (pv). Это некое блочное устройство, способное хранить данные (HDD, RAID, ...) Уровень 3: logical volume (lv). Это эквивалент раздела на жестком диске. Таким образом в группу добавляются физические тома (pv), и потом во всем этом пространстве свободного места создаются разделы (lv), на которых уже создается файловая система. Итак, сначала создавался pv: [cc lang="bash"] pvcreate /dev/sdb1 [/cc] Затем vg: [cc lang="bash"] vgcreate sys_vg /dev/sdb1 [/cc] И затем на все свободное место указанного pv (/dev/sdb1) создали lv с именем var_lib_mysql: [cc lang="bash"] lvcreate -L 100%PVS -nvar_lib_mysql sys_vg /dev/sdb1 [/cc] Дело за малым: [cc lang="bash"] mkreiserfs /dev/mapper/sys_vg-var_lib_mysql mount /dev/mapper/sys_vg-var_lib_mysql /mnt /etc/init.d/mysql stop mv /var/lib/mysql/* /mnt/ mv /var/lib/mysql/.* /mnt/ umount /mnt mount /dev/mapper/sys_vg-var_lib_mysql /var/lib/mysql chown -R mysql:mysql /var/lib/mysql /etc/init.d/mysql start [/cc] Вот собственно и почти все. Последний штрих - прописать монтирование раздела в fstab, дабы это происходило при загрузке автоматом. Можно скучно сделать это через blkid, увидеть там нужный UUID (например, f6946e54-c7d6-4688-8fac-05dcb1bf9973), скопировать его, открыть /etc/fstab и вставить туда строку вида: [cc lang="bash"] UUID=f6946e54-c7d6-4688-8fac-05dcb1bf9973 /var/lib/mysql reiserfs defaults 0 2 [/cc] а можно сделать так: [cc lang="bash"] printf "\nUUID=`blkid | grep sys_vg-var_lib_mysql | sed -r 's/.*UUID="([^"]*).*/\1/i'`\t/var/lib/mysql reiserfs defaults 0 2\n" >> /etc/fstab [/cc] Да, если сделать umount /var/lib/mysql && mount /var/lib/mysql до ребута - то /dev/disk/by-uuid/f6946e54-c7d6-4688-8fac-05dcb1bf9973 (или какой там получится) там еще не будет. Для того чтоб появился до ребута надо перезапустить udev: [cc lang="bash"] /etc/init.d/udev restart [/cc]

Part2. Resizing...

Как я говорил раньше, с опозданием пришла мысль о том, что неплохо бы вынести и статические файлы на этот же винт. И сделать это совсем просто! Для этого от того lv что был создан раньше (и именуется var_lib_mysql) откусим немного места. Сначала остановим все службы (говорят, reiserfs увеличивается/уменьшается без проблем налету, но я этого пока не пробовал на себе): [cc lang="bash"] /etc/init.d/mysql stop umount /var/lib/mysql [/cc] Затем уменьшим файловую систему, а затем и lv на 20ГБ: [cc lang="bash"] resize_reiserfs -s-20G /dev/mapper/sys_vg-var_lib_mysql lvreduce -L-20G /dev/mapper/sys_vg-var_lib_mysql [/cc] На всякий случай я предпочел проверить фс на ошибки (а вдруг!): [cc lang="bash"] reiserfsck /dev/mapper/sys_vg-var_lib_mysql [/cc] Ну и возвращаем обратно MySQL: [cc lang="bash"] mount /var/lib/mysql /etc/init.d/mysql start [/cc] Теперь создадим lv для веб-файлов, и так как их намного меньше 20ГБ, я решил оставить 5ГБ про запас, никому их не присвоив. Потом можно будет налету добавить туда где закончится место. [cc lang="bash"] lvcreate -L 15G -nvar_www sys_vg mkreiserfs /dev/mapper/sys_vg-var_www [/cc] Далее - перенос файлов: [cc lang="bash"] /etc/init.d/nginx stop /etc/init.d/apache2 stop mount /dev/mapper/sys_vg-var_www /mnt mv /var/www/* /mnt/ mv /var/www/.* /mnt/ umount /mnt mount /dev/mapper/sys_vg-var_www /var/www /etc/init.d/apache2 start /etc/init.d/nginx start [/cc] И опять не забываем про fstab: [cc lang="bash"] printf "\nUUID=`blkid | grep sys_vg-var_www | sed -r 's/.*UUID="([^"]*).*/\1/i'`\t/var/lib/mysql reiserfs defaults 0 2\n" >> /etc/fstab [/cc] По материалам:
  1. http://wiki.linuxquestions.org/wiki/LVM#example
  2. http://www.tldp.org/HOWTO/LVM-HOWTO/reducelv.html
PS: после изменения размера мог измениться UUID для lv var_lib_mysql, хотя я и не уверен в этом. Но проверить не помешает. PS2: если работаете удаленно - не забывайте про screen. PS3: писалось по памяти, так что могут быть некоторые неточности. Тупой копипаст без вовлечения мыслительного процесса чреват боком. Я предупредил ;)

Server moving adventures

Второй день занимаюсь переездом содержимого одного сервера на другой. Другой - VPS под FreeBSD (привет, ДЦ Воля). В общем, это последний раз когда я до оплаты сказал что он неплох. Теперь только реальные сервера. Ну и может VDS под Linux... В общем, именно эта реализация ужасна. Меня мало интересует как и что - факт налицо.
Первая ласточка - mysql. Сообщение о нехватке памяти в логах: [cc lang="text"] 080806 13:39:10 [ERROR] /usr/local/libexec/mysqld: Out of memory (Needed 1043824 bytes) [/cc] Судя по найденому в гугле и попыткам что-либо изменить, это вылазит из-за дефолтного в i386 FreeBSD значения максимального количества памяти на процесс. И изменить его у меня не удалось. В результате куцые буфера, и веселые запросы толпятся в очереди, а MySQL уверенно пухнет. И опухает: [cc lang="text"] [root@vps ~]# ps axu | grep mysql bash: fork: Cannot allocate memory [/cc] Причем, такое поведение я уже встречал ранее. Дважды. Тогда еще и файловые дескрипторы заканчивались (привет phpbb с кучей плагинов). Но все был бы ничего, однако базы в большинстве своем живут в MyISAM, но самая тяжелая - как и полагается, в InnoDB. И вот любой запрос с джоинами на ней ложил тачку. Во время разборок с мускулем был применен киллер, найденый в темном переулке на форумах мускуля: [cc lang="php"] #!/usr/local/bin/php 10 && $row['User']!='root' ) { $sql="KILL $process_id"; mysql_query($sql); } } ?> [/cc] Теперь появилось время на мысли. mtop помог отследить, что во всем виновата одна эта БД. После запора в ней более-менее тяжелые запросы в других БД тоже застряют. Результатом был переезд этой большой базы на другой хост, и использование ее оттуда. Сервер с двухядреным оптероном и 4ГБ памяти не заметил появления балласта: запросы пролетали мгновенно. И это без попыток тюнинговать мускуль.
Далее начались проблемы с милой связкой nginx+apache. Как описано у Алексея, все заработало. Но некоторые странички отказывались показываться - браузер ругался на невозможность понять что же ему пришло. Такой ошибки я не встречал, и как оказалось никто из моего контакт-листа тоже. А получилось следующее: обожаемый ExpressionEngine (и тебе привет) пытался все отдать за-gzip-леное. Апач справедливо отдавал это как HTTP/1.1 Transfer-Encoding: chunked. Но это в ответ на запрос HTTP/1.0 от nginx! Последний нифига не понимал и результирующий фарш доставлялся браузеру. Еще бы, он не хотел это нечто отображать... Выключением опции gzip-сжатия в ExpressionEngine 1.5.3 это полечилось, однако...
... приключения с этим белым и пушистым зверьком не закончились. В форуме при публикации сообщения символы кириллицы отсутствовали. Долго я искал помощи через гугль, пока не полез в код. А в коде методом тыка нашел, что это все виноват xss_clean в версии 1.5.3. Заменив строки с [cc lang="php"] ... $str = preg_replace('#(&\#*\w+)[\x00-\x20]+;#u',"\\1;",$str); ... $str = preg_replace('#(&\#x*)([0-9A-F]+);*#iu',"\\1\\2;",$str); ... [/cc] на [cc lang="php"] $str = preg_replace('#(&\#?[0-9a-z]+)[\x00-\x20]*;?#i', "\\1;", $str); ... $str = preg_replace('#(&\#x?)([0-9A-F]+);?#i',"\\1\\2;",$str); [/cc] проблему вылечил. С нетерпением жду что обнаружится дальше...

Migration to PowerDNS

Наверняка многие видели сообщение об уязвимости в DNS. Во многих дистрибах линукса весьма скоро появились секьюрити-патчи, которые эту уязвимость исправляли. Уязвимость, может, и не слишком уж страшная, но она напомнила мне о PowerDNS, который я с интересом рассматривал в начале 2008го года. Расматривал я его в основном из-за родной интеграции с MySQL. Мне весьма понравилось как она реализована. Однако у меня были уже рабочие конфиги BIND9, и я просто ленился испытать PowerDNS на собственной шкуре. А зря боялся, как оказалось. Упомянутая уязвимость не задела PowerDNS, и этим мое внимание было вновь привлечено к нему. И переход таки состоялся.

Что дал переход

Во-первых, я получил возможность использовать найденый вчера poweradmin. Штука оказалась немного неуклюжая, но порадовала меня тем, что не надо более ковыряться в конфигах. Во-вторых, при изменении/добавлении/удалении записей в зоне маркер (Serial) в SOA меняется сам. Мелочь, но приятно. В-третьих, теперь можно давать доступ к редактированию зон другим людям. Вот то, что бросилось мне в глаза. А теперь более подробно о самом переходе.

Процесс перехода

PowerDNS состоит из двух компонентов - основной сервер имен и преобразователь имен. Основной сервер имен выдает информацию только о тех записях, которые в нем есть. Преобразователь имен же помогает резолвить то, чего нету непосредственно у данного сервера. Итак, понадобится выполнить следующие действия: [cc lang="text"] apt-get install pdns-server pdns-recursor pdns-backend-mysql [/cc] Желательно во время установки стопнуть bind, потому как dpkg будет ругаться из-за неудачного запуска демона. После установки можно тушить pdns и стартовать bind до окончания миграции. В конфиге /etc/powerdns/pdns.conf надо сделать следующие изменения: [cc lang="text"] ... #allow-recursion=127.0.0.1 ... launch=gmysql ... local-address=_real_external_ip_here_ ... recursor=127.0.0.1 ... [/cc] Этим мы будем передавать все неизвестные запросы от основного сервера к recursor'у. У рекурсора в конфиге стоит local-address=127.0.0.1 по умолчанию, если мне не изменяет память. В /etc/powerdns/pdns.d/pdns.local вписываем [cc lang="text"] gmysql-host=localhost gmysql-port=3306 gmysql-dbname=_username_here_ gmysql-user=_dbname_here_ gmysql-password=_dbpass_here_ gmysql-socket=/var/run/mysqld/mysqld.sock [/cc] Проверяем, есть ли нужная БД и пользователь, и может ли он подключиться к базе. Пример начальной базы есть в /usr/share/doc/pdns-backend-mysql. Советую туда сразу добавить в конец [cc lang="sql"] ALTER TABLE records ADD CONSTRAINT `records_ibfk_1` FOREIGN KEY (`domain_id`) REFERENCES `domains` (`id`) ON DELETE CASCADE; [/cc] При его тестировании было замечено что работает он бытрее с таблицами InnoDB. Наверное, из-за большого кеша у этого типа таблиц. Я не стал менять таблицы на MyISAM, однако добавил при создании таблиц default collate utf8_general_ci - для надежности. Потом надо втянуть записи имеющиеся у BIND. Для этого в комплекте есть утилита zone2sql. Рекомендую использовать ее с ключиком --gmysql - так она правильнее работает, создавая записи и в domains и в records. [cc lang="text"] zone2sql --named-conf=/etc/bind/named.conf --gmysql > import.sql [/cc] и вначале выкосил все до основных зон. Потом скормил это mysql'ю. Также можно во всех записях SOA поставить Serial в 0 - так PowerDNS будет вычислять его автоматически. Пробуем стопнуть bind9 и запустить pdns и pdns-recursor. Если все правильно - все взлетит, и в сислоге будут сообщения о состоянии полета. Проверить можно набрав [cc lang="text"] nslookup - dns.yourserver.com [/cc] и попробовать ввести что-то существующее на сервере и что-то несуществующее. У меня основной сервер без recursor'а не хотел отдавать записи типа CNAME при просмотре nslookup'ом. Пришлось установит recursor и настроить их общение. Ну а установка poweradmin для управления сервером не должна вызвать трудностей вообще.

FAM and Gamin

Вчера потратил немного времени и произвел скрипт-демон для отлова изменений в указанной папке. Но вот работает связка только с FAM. А в Linux сейчас уже используется более продвинутый Gamin. Как подружить Ruby с Gamin?

Read the rest of this post »

Linux tips v.1

Серия относится к категории "newbies". Используемый дистрибутив - Ubuntu. В этой части:
  • как сменить разрешение экрана вручную
  • как сменить видеодрайвер
  • как восстановить загрузчик (grub) после установки Windoze
  • запись в NTFS

Read the rest of this post »

NAT with IPTables

Итак, специально для kislo_metal постю сюда тривиальную вещь - простейший метод для поднятия NAT. Я назвал его iptables.sh и положил в /etc/init.d/ [cc lang="bash"] #!/bin/bash INET_IFACE="eth0" IPTABLES="/sbin/iptables" echo 1 > /proc/sys/net/ipv4/ip_forward #Loading modules... modprobe ip_tables modprobe ipt_helper modprobe ip_conntrack modprobe ip_conntrack_ftp modprobe ip_conntrack_irc modprobe iptable_nat modprobe ip_nat_ftp modprobe ip_nat_irc modprobe ip_gre modprobe ip_nat_pptp #Adding default actions $IPTABLES -P INPUT ACCEPT $IPTABLES -P OUTPUT ACCEPT $IPTABLES -P FORWARD ACCEPT #Clear all current rules $IPTABLES -F $IPTABLES -X $IPTABLES -t nat -F ##Creating NAT #for my PC: $IPTABLES -t nat -A POSTROUTING -s 10.1.1.2 -d 0/0 -j MASQUERADE #for virtual machine $IPTABLES -t nat -A POSTROUTING -s 192.168.0.2 -d 0/0 -j MASQUERADE [/cc] Если скрипт не сработал - смотрим iptables-save. Если там хоть где-то есть DROP - то уже разбирайтесь сами, ибо по умолчанию политики стоят в ACCEPT на все. Чтобы в Debian-based дистрибутивах скрипт подымал NAT автоматом надо сделать [cc lang="bash"] sudo ln -s /etc/init.d/iptables.sh /etc/rc2.d/S99iptables.sh sudo chmod +x /etc/init.d/iptables.sh [/cc]