Jul 13

Вот такую картину я увидел сегодня у себя в админке на Linode. Что-то в ней мне показалось странным. Через пару секунд я понял, что именно - надпись “Linode 512″, хотя я точно помнил, что у меня до этого было 360 мегабайт оперативки. Законнектился на сервак, проверил - действительно 360 мегабайт доступно. Зашел на страницу тарифных планов - действительно 360 поменялось на 512, да и остальные планы явно получили побольше оперативки. Как оказалось - увеличение оперативки произошло еще месяц назад, а я его не заметил, т.к. ни разу не перезагружал сервак с того момента, как он прекратил зависать, а для того, чтобы получить заветный прирост ресурсов - нужна перезагрузка.

written by fxposter \\ tags: ,

Jul 11

Некоторые свои посты я дополняю постами-линками на Хабрахабре. Последний подобный пост - “Особенности работы jQuery.live()“. Он так и не попал на главную, но в данном случае это не очень важно, т.к. свой “хабраэффект” я поймал.

До этого у меня случались странности на сервере - он начинал есть все 400% процессора и наглухо зависал - к нему было невозможно подключиться и единственным вариантом восстановления был шатдаун инстанса в админке Linode. Это случалось нечасто и не особо меня парило. Но когда на мой сайт стали постоянно заходить люди с хабра - такое поведение сервера стало постоянным. Он начинал есть весь процессор. После перезагрузки он пару минут держался, а потом опять начинал свой бравое дело. Стало ясно - что-то явно не так в настройках.

После довольно недолгих разбирательств стало ясно, что виноват Apache (у меня стоит Apache + mod_prefork + php + ruby via passenger). Параметры prefork-а:

<IfModule mpm_prefork_module>
StartServers          5
MinSpareServers       5
MaxSpareServers      10
MaxClients          150
MaxRequestsPerChild   0
</IfModule>

Теперь мне стало очевидно всё, кроме одной вещи - почему же все-таки был загружен процессор… А причина зависаний сервера была проста - параметр MaxClients отвечает за то, сколько процессов апач может запустить для обработки пользователей (в prefork-е каждый запрос обрабатывается отдельным процессом, который либо запускается, либо вытаскивается из пула свободных запущенных процессов). Процессы апача ну очень недешевые в плане ресурсов - честно говоря, я до сих пор не знаю, как можно посмотреть, сколько реально кушает процесс апача - по текущей нагрузке на сервак при создании новых процессов апач кушает лишние 10-20мб памяти. 150 процессов * 10 мегабайт… Ну вы поняли. У меня сейчас обычныя VPS-ка на 360Мб. В общем, причина зависаний была в том, что апач создавал кучу процессов и уходит в своп по самое нехочу. А вот вернуться оттуда он уже сам не мог - ему нужна была помощь.

Выход - снижать количество максимально процессов апача. Методом научного тыка была выбрана цифра в 20 процессов. Собственно, сейчас эта цифра вполне себе хорошо работает. После уменьшения MaxClients и перезапуска сервера он падать перестал. Но появилась новая проблема - из-за хабраэффекта сервак хоть и не падал, но  отдавал ответы очень долго. Эту проблему я решил не решать, т.к. в тот момент был на работе. Сейчас вот появилось немного времени и я решил протестировать свой блог на отзывчивость.

ab -c 1 -n 100 http://blog.fxposter.org/

Эмм… Результата я не дождался.

Для тех, кто не работал с ab и не знает, что это такое - Apache Benchmark.

ab -c 1 -n 10 http://blog.fxposter.org/

Результат меня неприятно удивил. Результаты, к сожалению, не сохранились, но могу сказать точно, что время генерации страницы было около 1.5 секунды (судя по тому, что сейчас показывает генерация страницы, если её нет в кеше).

Собственно, стало понятно, как бороть “хабраэффект” - нужно сильно ускорить отдачу странички. Как? Да очень просто - кешированием. Слава богу, что это WordPress и тут есть такие плагины, как wp-cache - прикручивается за несколько минут и пока что отлично работает. Если бы такого плагина не было, то, наверное, у меня бы была сейчас статья о том, что такое Reverse Proxy и как его использовать для кеширования. :) А так - поставил, включил. Результаты:

Заодно решил немного пооптимизировать клиентскую часть - добавить expires и сжатие контента (использовался модуль апача deflate).

Приведу конечные файлы:

# /etc/apache2/mods-available/expires.conf
<IfModule mod_expires.c>

ExpiresActive On

ExpiresByType image/gif "access plus 1 weeks"
ExpiresByType image/png "access plus 1 weeks"
ExpiresByType image/jpeg "access plus 1 weeks"

ExpiresByType text/css "access plus 1 weeks"
ExpiresByType application/javascript "access plus 1 weeks"
</IfModule>
# cat /etc/apache2/mods-available/deflate.conf
<IfModule mod_deflate.c>
 # AddOutputFilterByType DEFLATE text/html text/plain text/xml

 DeflateCompressionLevel 9

 # Insert filter
 SetOutputFilter DEFLATE

 # Netscape 4.x has some problems...
 BrowserMatch ^Mozilla/4 gzip-only-text/html

 # Netscape 4.06-4.08 have some more problems
 BrowserMatch ^Mozilla/4\.0[678] no-gzip

 # MSIE masquerades as Netscape, but it is fine
 # BrowserMatch \bMSIE !no-gzip !gzip-only-text/html

 # NOTE: Due to a bug in mod_setenvif up to Apache 2.0.48
 # the above regex won't work. You can use the following
 # workaround to get the desired effect:
 BrowserMatch \bMSI[E] !no-gzip !gzip-only-text/html

 # Don't compress images
 SetEnvIfNoCase Request_URI \
 \.(?:gif|jpe?g|png)$ no-gzip dont-vary

 <IfModule mod_headers.c>
 # Make sure proxies don't deliver the wrong content
 Header append Vary User-Agent env=!dont-vary
 </IfModule>
</IfModule>

Если вас интересует, почему эти файлы выглядят именно так - читайте документацию к модулям апача.

На другой ОС (у меня Ubuntu 9.04) эти файлы, наверняка, должны будут находиться в других местах. У себя я их разместил так, потому что эти конфиги автоматически подключаются вместе с подключением соответствующих модов апача (при использовании команды a2enmod).

По идее теперь сайт должен получше откликаться и, думаю, все-таки теперь он выдержит хабраэффект. :)

PS. С точки зрения клиентской оптимизации - css-ки и js-ки лучше бы собрать в один файл, а кучу запросов к изображениям заменить спрайтами. Заниматься первым особо не вижу смысла, т.к. и стилей и скриптов на блоге немного, а изображениями заниматься лень, т.к. все равно собираюсь переезжать на “Twenty Ten“.

written by fxposter \\ tags: , , , , ,

Jul 07

Есть такая фича в Rubygems, как установка гем-ов в директорию пользователя ~/.gem, если gem install запускается без sudo и, соответственно, у пользователя нет разрешения установить что-либо в дефолтную директорию.

В Rubygems до версии 1.3.6 это решалось просто - автоматически. Т.е. если вы вместо sudo gem install пишете gem install - вам выдается warning, но гем успешно устанавливается:

[fxposter@kitty ~]$ gem install haml
WARNING:  Installing to ~/.gem since /usr/lib/ruby/gems/1.8 and
/usr/bin aren't both writable.
Successfully installed haml-3.0.13
1 gem installed
Installing ri documentation for haml-3.0.13...
Installing RDoc documentation for haml-3.0.13...

Вроде бы всё замечательно. Но с версии 1.3.6 это дефолтное поведение изменилось, и при обновлении с помощью gem update --system об этом честно пишется:

–user-install is no longer the default. If you really liked it, see Gem::ConfigFile to learn how to set it by default. (This change was made in 1.3.6)

Но кто реально читают все эти тексты? Да и не все обновляют Rubygems таким образом. В нашем случае произошла смена сервера. Причем на старом сервере стояла старая версия Ruby Enterprise Edition и дефолтные Rubygems 1.3.5. На новом же серваке наши бравые админы опять всё “покомпилили” и вместе с REE у нас там обновился и Rubygems - теперь там стояла версия 1.3.7. И… сначала отвалился деплой, т.к. по умолчанию rake gems:install у нас там не включен. Запустили rake gems:install - не работает. Запустили gem install - не работает. Всё просто замечательно.

Разбирались долго. Сначала думали на админов, которые в рельсах плохо шарят и думали, что они там “понастраивали”. Кстати, понастраивали они действительно знатно - скомпилированный REE почему-то стоит поверх стандартного Ruby 1.8.5 (у нас там CentOS 5, в ближайшем времени переезжаем на Amazon EC2 на Ubuntu). Но это неважно. После долгого разговора с админом и ковыряния серваков я чисто случайно обратил внимание на разные версии Rubygems. Заподозрил неладное. Обновил их на старом серваке на 1.3.7 и… получил сообщение, написанное выше. Отлично. Теперь осталось восстановить поведение старых Rubygems.

В итоге всё оказалось довольно просто. В файл ~/.gemrc нужно дописать следующую строку: "install: --user-install". После чего мой .gemrc стал выглядеть так:

[waysgo@web-waysgo ~]$ cat .gemrc
---
:benchmark: false
:update_sources: true
:sources:
- http://rubygems.org/
- http://gems.github.com/
:bulk_threshold: 1000
:verbose: true
:backtrace: false
install: --user-install

written by fxposter \\ tags: ,