Как да използваме наименовани локации в NGINX

Публикувано на

Или как да имам "спретнат" конфигурационен файл с минимално количество повторение в кода.

Nginx сървърът притежава т.нар наименовани пътища (named location), които изглеждат по следният начин:

location @some_location {
# some stuff
}

Ключовото тук е @some_location и по-скоро символът @. По този начин ние си дефинираме виртуално местоположение само за вътрешна употреба. Основното му предназначение е при използване на error_page и try_files конфигурациите. Например:

location / {
root /some/path;
try_files $uri $uri.html $uri/index.html @not_found;
err_page 403 = @not_found;
err_page 500 = @not_found;
}
location @not_found {
try_files 404.html 500.html
}

Идеята тук е, че може да се ползва като заместител когато try_files не намери нищо, или ако сървъра трябва да върне някоя от изброените грешки (403 и 500). В тези случаи искаме да пробваме да покажем файла 404.html, ако и него го няма пробваме да покажем файла 500.html. Този пример само онагледява използването на наименовани пътища и не е за ползване в реална среда.

В реална среда наименованият път може да бъде някакво прокси към fastCGI, WSGI или какъвто и да е друг сървър. Например ако разползгаме с Django сайт, който върви с uWSGI сървър и искаме за всички статични файлове ако Nginx не намери нищо да се опита да го намери на Django сайта.

root /path/to/www/root;

location /media/ {
try_files $uri @django;
}

location /static/ {
try_files $uri @django;
}

location = robots.txt {
try_files $uri @django;
}

location / {
uwsgi_pass unix:/path/to/uwsgi/socket;
include uwsgi_params;
}
location @django {
uwsgi_pass unix:/path/to/uwsgi/socket;
include uwsgi_params;
}

По този начин ако файл от /media/ или /static/ директориите не бъде намерен ще бъде препратен към uWSGI сървъра на Django сайта, а там вече framework-a обикновенно няма да намери страница за покзване и ще визуализира красива 404 страница. Единственият проблем на този подход е двойното описване на местоположението на uwsgi сървъра. Като човек който пише на Python не обичам да има повторение на код. За статичните файлове също има повторение - try_files $uri@django; , но то е само един ред и е малко код. Лесно може да се избегне чрез иползване на регулярен израз, но от гледна точка на скорост предпочитам да ги държа отделно. Повторението при описване на пътя до uwsgi сървъра е два реда, но може да бъде и повече. Друг момент е, че може да се наложи да се промени и е добре да се промени само на едно място, защото се рискува да остане някъде непроменено (при по-големи конфигурационни файлове) и това да доведе до проблем, който може да бъде забелязан късно.

Порових се из документацията на Nginx отностно наименованите пътища и там не пише да се ползват някъде другаде освен за try_files и error_page. Започнах да търся дали няма някакъв друг изход от проблема тъй като не ми се искаше да правя:

location / {
try_files invalid_path.html @django;
}

По този начин при всяка една заявка ще търси на харда за този файл, който не трябва да съществува, и чак след това ще предаде заявката към django сървъра. Товa може да е удобно когато искаме временно да покзваме статична страничка докато правим нещо със сървъра но на цената на по-бавни заявки. Продължих да се ровя и попаднах на тази статия. Тук човека има същият проблем но има и решение, което се оказва много просто и логично, но малко грозно.

location / {
err_page 403 = @django;
return 403;
}

По този начин задаваме за този път, че при код на грешка 403 ни искаме да се зареди наименованият път и веднага след това казваме на Nginx да върне грешка 403. Tака трафика се пренасочва директно към наименования път, като няма да има забавяне, но не е елегантно решение.

Все пак някой от участниците в дискусията се опитваха да обеснят, че няма никакъв проблем в това да има повторения в конфигурационният файл и ако в бъдеще трябва нещо да се променя може само с един find and replace да се промени. Също така се аргументира с това че при 90к конфигурационен файл е трудно да се проследят релациите между нещата. Явно този човек е свиканл да конфигурира Apache и за това е свикнал да работи с големи конфигурационни файлове, тъй като там няма възможност да си минифицираш нещата. Понеже ако си го оптимизираш няма да ти е 90к а сигурно няма да има и 10к (което пак си е голям файл). 

Все пак от предложените варианти (повторение, try_files или със error_page) ще използвате, си остава ваша преценка. Cпоред мен, определено би било по-добре, ако в самият Nginx има вграден начин за използване на наименованите пътища, не само за try_files и error_page, но и за директно пренасочване на целия трфик от дадено място към наименовано такова. Така конфигурационните файлове могат да станат още по-компактни и по-добре четими.

 

П.П.

Примерът който съм дал използва наименован път наречен django, заради фреймуоркът на който пиша, но това може да се използва за всякакви езици и всякакви сървъри (fastcgi, http proxy и др.)

blog comments powered by Disqus