Кешируем статику

Существует мнение, что nginx — отличный инструмент для отдачи статики.
Есть статьи, где описываются настройки sendfile или aio для «улучшения» отдачи.
На Хабре есть чего почитать о настройке proxy_store с proxy_cache для минимизации проблем со стороны мозгов сайта.
Еще в QA иногда возникают вопросы про кеширование картинок, например.

Зачем заниматься этой ерундой! — говорят опытные пользователи — OS лучше вас знает как кешировать файлы! С кешем и префетчем в современных OS, точнее FS, проблем нет! Зачем плодить свои кеши и списки популярных материалов и все такое?…

Есть только одно вредное «но» — в среде исполнения nginx (в общем случае Linux) понятие «файл» и вообще «файловая система» — просто понятие.
И однажды, когда я, подмонтировав сервер по sshfs, обновил один скриптик, случилось волшебное:

  1. На каждой страничке стало на 4 картинки больше.
  2. Сервера сдохли.

Что поделать — картинки хранились на glusterFS. Наступил полный FUSE.

 

GlusterFS, как и интерфейс FUSE, сильно сжирает производительность, но он того стоит.
Изначально у нас был один сервер. Потом два, второй из который монтировал www с первого по nfs. Потом три, четыре.
Не очень надежно, но так уж исторически сложилось. Для спасения ситуации нужна была некая совершенно прозрачная система, in-place замена nfs. Кластерная ФС.
Сейчас на всех серверах стоит raid 10, и за полтора года эксплуатации он пару раз падал. В том числе совсем. Gluster молча поднимал копию ФС на новой железяке и все продолжало работать. Гластер свою работу знает.

Но на самом деле история начиналось немного не так.

Причина была другая, но решения проблем будет совпадать.
Нет ничего страшнее чем то что «исторически сложилось«.
И в одном моем проекте «исторически сложилась» ситуация, когда картинки отдает php.
В свое время нужно было сделать файловый загрузчик с различными ACL, ресайзами и вотермарками. Это было почти 10 лет назад. Механизм прижился и с тех пор не менялся.
Сейчас для решения таких проблем есть много специализированных решений(ellipticsbackpack и другие; а еще лучше CDN). Но тратить человекомесяцы на переход не хочется. Спать хочется, а не работать.
Да и php чтука, в принципе, неплохая. Но имеет свои пределы.

Но проблема тормозных бэкендов решена

Хабр, в очередной раз подсказал вариант решения — proxy_store и proxy_cache.
Но proxy_store использовать мне нельзя — сохранить негде, и с proxy_cache тоже проблемы.
Есть одно, большое такое, «но» — много лет назад я уже столкнулся с производительностью отдачи файлов и решил «пускай лучше nginx отдает конечные файлы за меня».
X-Accel-Redirect.
Проблема — nginx не может закешировать ответ из прокси если это не ответ, а команда.X-Accel-Redirect — не кешируется.
Выхода нет?

Исходную проблему зафиксировали

Теперь вернемся к началу топика.
Нам нужно отдать статику, на этот раз без вмешательства «умных» бэкэндов. Просто статику, но с glusterfs.
Директив для кеширования статики просто нет — не предусмотрены. Считается что OS сама не дура. Для статики есть только open_file_cache и настройка самой передачи файлов(sendfile/aio и компания).

Что делать?
Усложним задачу, и попробуем решить проблему исключительно средствами nginx, не внося никаких изменений в другие коды.

обычный конфиг
# Static files location
location ~* ^.+\.(jpg|jpeg|gif|png|ico|css|js|swf|txt|ico)$ {             
    root   /var/www/kashey;
}

 

Пропатченый конфиг

Nginx делает proxy_pass сам на себя, кешируя результат. Возможно тут можно использовать fcgi, но так — нагляднее.

# Static files location
location ~* ^.+\.(jpg|jpeg|gif|png|ico|css|js|swf|txt|ico)$ {
    set $dopass 1;
    proxy_set_header   Host             $host;
    #если у нас есть "кука" - отдаем файл с реальной ФС
    if ($http_fiberpass) {
        root   /var/www/kashey;
        #или proxy_pass на бэкэнд
        set $dopass 0;
        break; #но этот break не работает
    }
    #настройки кеширования, не забудьте описать proxy_cache_valid
    proxy_cache static_cache;
    #добавляем прокси "куку"
    proxy_set_header fiberpass "nginx";
    if ($dopass) {
        #отправляем запрос к самому себе.
        proxy_pass		http://127.0.0.1:80;
    }
}

 

И никакой магии.
В случае с X-Accel-Redirect схема такая:

  1. nginx получается запрос
  2. пересылает себе, к сожалению запрос полный и реальный.
  3. пересылает на прокси
  4. получает ответ и отдает файл
  5. ответ кешируется
  6. ???
  7. PROFIT

В случае с обычной статикой сложнее — root не заканчивает выполнение, break и return также не работают. Настройка прокси не может находиться внутри условного блока.

Что это дает?

В смысле с X-Accel-Redirect с apache снимается вал запросов. На загруженной системе разница может составить 50 раз.
Но это понятно — php сам по себе, а через апач тем более, не самое реактивное решение.
Latenсy запроса меньше 40мс, лично у меня, не получался (общее время php+gluster).
В случае использования варианта с proxy_cache скорость отдачи приравнивается к скорости отдачи статики.

А что со статикой?

Отдача с gluster — 13мс — это нормальное время для системы без кеша.
5741036f02e78eea51f87bdc5f5c2b78

Отдача из кеша — 1мс — это замечательное.
1fdba72ed5fa3dfe02b0f8c9146d316f

Важно учесть — сервера находятся в Германии, а клиенты в России.
Нормальные пинги до сайта 60-100мс.
Так что для клиента ускорение практически не заметно.

Но оно заметно для сервера.

  1. Снизилась нагрузка на сервера, в том числе в попугаях Load average и soft-irq.
  2. Снизился трафик между серверами (можно решить включив WORM(Write Once Read Many volume), но это уже не «прозрачно»).

b091fd58577a79d50b2688ffce8b273a
3. Все стало работать чуть быстрее.

В итоге работы которые на 10% снизили скорость реакции на клиенте, получились в результате 10-ти кратного ускорения отдачи статики. Что снизило общую нагрузку на сервера примерно раз в 20.
Обратное утверждение, к сожалению, тоже будет верно — сколько не старайся, клиент может и не заметить.

PS: Многие знакомые сисадмины даже не допускали мысли о возможности работы такой схемы. Не позволяет кешировать/проксировать — значит нельзя.

Хорошие программисты не всегда хорошие сисадмины, а сисадмины, обычно, вообще не программисты.
Настоящие герои всегда идут в обход, в брод, и на велосипедах.