Posterous theme by Cory Watilo

Filed under: lighttpd

PowerDNS URL records and redirector

В PowerDNS есть пара плюшек, которые я раньше не замечал. Одну из них - записи "URL" я сегодня попробовал, настроил и остался вполне доволен. Что же такое URL-запись? Это фича PowerDNS, при помощи которой можно делать редирект домена на заданный URL. Например, запись выглядит как [cc lang="text"] some.domain.com URL http://other.domain.com/some_domain_com [/cc] На самом деле в поле со значением записи может быть ссылка куда угодно. В конфиге сервера нужно указать [cc lang="ini"] ... fancy-records=yes urlredirector=77.120.99.180 ... [/cc] Когда приходит запрос к DNS'у для домена some.domain.com отдается 77.120.99.180, указанный в конфиге, как если бы это была обычная "А"-запись. Браузер отрезолвив имя идет на http://some.domain.com. На этом адресе висит http-сервер (я повесил lighttpd), у которого скрипт лезет в БД и для указанного домена ищет соответствующую ему URL-запись и редиректит браузер туда. Все просто и тривиально. Для lighttpd у меня конфиг дополнился такими строками: [cc lang="ini"] $SERVER["socket"] == "77.120.99.180:80" { $HTTP["host"] =~ "(.*)" { magnet.attract-raw-url-to = ("/var/www/lighttpd/pdns-url-redirector.lua") } } [/cc] То есть все что приходит незамедлительно обрабатывается упомянутым lua-скриптом. Почему lua? С ним работает mod_magnet, и мне было интересно написать этот скриптик. Вот он: [cc lang="lua"] require "luasql.mysql" env = assert (luasql.mysql()) con = assert (env:connect("dnsdb", "dns_user", "dns_pass_for_readonly_records_table", "localhost")) domain = lighty.request["Host"] mysql_replacements = { ["\0"] = "\\0", ["\n"] = "\\n", ["\r"] = "\\r", ["\'"] = "\\\'", ["\""] = "\\\"", ["\026"] = "\\Z", ["\b"] = "\\b", ["\t"] = "\\t", } function fixsql (s) return (string.gsub (tostring (s), "[%z\n\r\'\"\026\b\t]", function (str) return mysql_replacements [str] or str end )) end -- fixsql cur = assert (con:execute( string.format("SELECT content FROM records WHERE `type` = 'URL' AND `name` = '%s'", fixsql(domain) ) ) ) result = cur:fetch() if result then redirect_to = string.format("%s", result) else redirect_to = "http://daemons.org.ua/" end cur:close() con:close() env:close() --lighty.content = { string.format("SELECT content FROM records WHERE `type` = 'URL' AND `name` = '%s'", fixsql(domain) ) } --lighty.header["Content-Type"] = "text/plain" --return 200 lighty.header["Location"] = redirect_to return 301 [/cc] Комментарий начинается с "--". Закоментарены строки для отладки скрипта. Для соединения с мускулем из lua доставил пакет liblua5.1-sql-mysql-2. Скрипт писал долго, но это было интересно. И небесполезно =)
UPD: во избежание DoS/DDoS атаки на мускуль возможны два варианта апгрейда метода: 1. По крону раз в 5 минут генерить sqlite-БД состоящую из одной таблицы из двух полей - domain, target 2. Раз в 5 минут генерить сам скрипт с вынесенными в него в виде хеша записями. Тогда скрипт вообще никуда подглядывать не будет.

Lighttpd and execwrap

В качестве вэб-сервера я использую lighttpd. И сегодня я наткнулся на старую проблему - вэб-сервер работает из-под пользователя www-data, и когда пользователь pupkin загружает какой-то файл - этот файл принадлежит не pupkin'у, а www-data. И через ftp удалить его он не может, и вынужден искать некие web-file-manager'ы. Так вот я погуглил и наткнулся на execwrap. Он очень мелкий, почти ничего ему не нужно стороннего для работы, конфигурировать и использовать его крайне просто (да-да, я знаю про suExec, но он мне не сильно понравился). В результате конфиг для сайта у меня выглядит так: [cc lang="text"] $HTTP["host"] == "pupkin.com" { server.name = "pupkin.com" site-root = "/home/pupkin/www/" + server.name server.document-root = site-root + "/www" accesslog.filename = site-root + "/log/access.log" setenv.add-environment = ( "AWSTATS_FORCE_CONFIG" => server.name ) auth.backend.htpasswd.userfile = site-root + "/awstats.passwd" fastcgi.server = ( ".php" => (( "bin-path" => "/var/www/lighttpd/execwrap", "socket" => "/tmp/" + server.name + ".phpsocket", "max-procs" => 10, "bin-environment" => ( "UID" => "9999", "GID" => "9999", "TARGET" => site-root + "/cgi-bin/php", "CHECK_GID" => "1", "CGI_BIN_DIR" => site-root + "/cgi-bin/", "PHP_FCGI_CHILDREN" => "2", "PHP_FCGI_MAX_REQUESTS" => "5000" ) ))) } [/cc] где 9999 - ID пользователя и группы (у меня они часто совпадают). А в директории /home/pupkin/www/pupkin.com/cgi-bin лежит два файла - симлинк на php.ini и исполняемый файлик "php" следующего содержания: [cc lang="bash"] #!/bin/bash cd $CGI_BIN_DIR exec /usr/bin/php-cgi -c ./php.ini [/cc] Как видите, если надо то конфиг php.ini можно пользователю задать персональный. А файлик php обязательно должен принадлежать этому же пользователю. Когда все сделано - рестартуем lighttpd и смотрим, нет ли в логах ругательств с кодом ошибки 22 - это, как видно из сорса execwrap, несоответствие прав. Enjoy =) UPD: execwrap умер и воскрес тут: http://cgit.stbuehler.de/gitosis/execwrap/

Webservers benchmark

Решил потестировать PHP в разных связках, а именно - Apache + mod_php, Apache + mod_fcgid + php, Lighttpd + mod_fastcgi + php. Все это еще в двух вариантах - с APC (Advanced PHP Cache) и без него. Тестировал выполнением вот такой команды: [cc lang="bash"] ab -c 5 -n 500 http://dmitry.shaposhnik.name/ [/cc] Выполнял команду на другом сервере чтобы снизить влияние случайных факторов. И вот что получилось в результате:
Вот полный вывод в текстовом виде: testing results UPD: вот на том же сервере решил протестировать приложение-блогодвижек (Записки айтишника) на рельсах той же командой.

Read the rest of this post »