Modul 5 — Webové servery a reverzní proxy
22. Nginx jako reverzní proxy a load balancer
Konfigurace Nginx jako reverzní proxy před aplikačním serverem a rozložení zátěže mezi více backendů pomocí upstream bloku.
Úvod a kontext
Minulá lekce ukázala Nginx jako server, který přímo servíruje statické
soubory z disku. Mnohem častější nasazení v produkci je ale jiné: Nginx
stojí před aplikačním serverem (Django, Node.js, Java aplikací) a
požadavky mu předává — funguje jako reverzní proxy. Když je backendů
víc, Nginx umí zátěž mezi ně i rozdělovat — funguje jako load
balancer. Tato lekce ukazuje obě role, které jsou v reálném provozu
mnohem běžnější než čistě statický web z minulé lekce.
Teorie
Co je reverzní proxy
Reverzní proxy je server, který přijímá požadavky klientů a předává je
dalšímu serveru (backendu) na pozadí, aniž by klient backend viděl přímo.
Klient komunikuje jen s Nginx; Nginx komunikuje s aplikací. Důvody, proč
se toto uspořádání v produkci prakticky vždy používá:
- TLS terminace — Nginx obstarává HTTPS (šifrování), aplikační server
pak může běžet jednodušeji jen na HTTP dovnitř interní sítě,
viz lekce o HTTP/TLS, - statický obsah — Nginx umí servírovat statické soubory (obrázky,
CSS, JS) mnohem efektivněji než většina aplikačních frameworků, - load balancing — rozložení zátěže mezi víc instancí aplikace,
- bezpečnost — aplikační server (např. Gunicorn pro Django) není
navržený na přímé vystavení internetu; Nginx před ním funguje jako
otestovaná, prověřená vrstva, - jednotný vstupní bod — víc aplikací/služeb na jednom serveru sdílí
port 80/443 a Nginx podle domény nebo cesty směruje na správnou z nich.
Základní konfigurace proxy_pass
server {
listen 80;
server_name app.example.cz;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
proxy_pass http://127.0.0.1:8000— všechny požadavky na tuto cestu se
přepošlou na aplikační server běžící lokálně na portu 8000 (typicky
Gunicorn/uWSGI proces),proxy_set_header Host $host— bez tohoto řádku by aplikace v hlavičce
Hostviděla127.0.0.1, ne skutečnou doménu, kterou zadal klient,X-Real-IPaX-Forwarded-For— předávají skutečnou IP adresu klienta;
bez nich by aplikace viděla jako zdroj všech požadavků jen IP adresu
Nginx serveru (typicky127.0.0.1), což znemožňuje např. rate limiting
podle IP na úrovni aplikace,X-Forwarded-Proto— informuje aplikaci, jestli původní požadavek od
klienta přišel přes HTTP, nebo HTTPS (důležité, pokud Nginx dělá TLS
terminaci a aplikace jinak vidí jen nešifrované HTTP).
upstream blok a load balancing
Pokud aplikace běží ve více instancích (více procesů, více serverů),
definuje se skupina backendů blokem upstream:
upstream aplikace {
server 10.0.0.11:8000;
server 10.0.0.12:8000;
server 10.0.0.13:8000;
}
server {
listen 80;
server_name app.example.cz;
location / {
proxy_pass http://aplikace;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Ve výchozím nastavení Nginx rozkládá požadavky mezi servery v upstream
bloku metodou round-robin (postupně kolem dokola). Další dostupné
strategie:
upstream aplikace {
least_conn; # pošli na server s nejméně aktivními spojeními
server 10.0.0.11:8000;
server 10.0.0.12:8000;
server 10.0.0.13:8000 weight=2; # dostane 2x víc požadavků než ostatní
server 10.0.0.14:8000 backup; # použije se, jen pokud ostatní selžou
}
least_conn— vhodné, když jednotlivé požadavky trvají různě dlouho,weight=N— nerovnoměrné rozložení, typicky pro backendy s různým
výkonem hardwaru,backup— server v záloze, aktivuje se, jen když ostatní nejsou
dostupné.
Health checks na úrovni upstream
Nginx (open-source verze) automaticky vyřadí backend, který přestane
odpovídat, na základě parametrů max_fails a fail_timeout:
upstream aplikace {
server 10.0.0.11:8000 max_fails=3 fail_timeout=30s;
server 10.0.0.12:8000 max_fails=3 fail_timeout=30s;
}
Pokud daný backend selže třikrát (max_fails=3) za sebou, Nginx ho na 30
sekund (fail_timeout=30s) přestane zkoušet a požadavky posílá jen
zbylým funkčním backendům. Po uplynutí této doby ho opět zkusí zahrnout.
Praktický příklad
Kompletní konfigurace reverzní proxy před dvěma instancemi Django
aplikace běžícími na portech 8001 a 8002 stejného serveru (typická
konfigurace, kdy jeden fyzický/virtuální server běží víc worker procesů
aplikace pro využití více CPU jader):
upstream kurzy_app {
least_conn;
server 127.0.0.1:8001 max_fails=3 fail_timeout=30s;
server 127.0.0.1:8002 max_fails=3 fail_timeout=30s;
}
server {
listen 80;
server_name kurzy.example.cz;
location /static/ {
alias /home/petr/ai/tutor/staticfiles/;
expires 30d;
}
location / {
proxy_pass http://kurzy_app;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Statické soubory (/static/) se servírují přímo z disku (rychlejší, bez
zatěžování aplikace), zatímco vše ostatní jde přes proxy_pass na jednu
ze dvou běžících instancí aplikace podle zátěže (least_conn). Po úpravě
opět následuje nginx -t a systemctl reload nginx.
Shrnutí
Nginx jako reverzní proxy předává požadavky aplikačnímu serveru pomocí
proxy_pass, přičemž hlavičky X-Real-IP, X-Forwarded-For a
X-Forwarded-Proto zajišťují, že aplikace nepřijde o informaci o
původním klientovi a protokolu. Blok upstream seskupuje víc backendů a
umožňuje rozložení zátěže (round-robin ve výchozím stavu, least_conn
nebo weight pro pokročilejší scénáře) i automatické vyřazení nedostupných
serverů pomocí max_fails/fail_timeout.
Kontrolní otázky
- Proč se bez hlaviček
X-Real-IPaX-Forwarded-Foraplikace za
reverzní proxy "nedozví" skutečnou IP adresu klienta? - Jaký je rozdíl mezi výchozí round-robin strategií a
least_connv
upstreambloku a kdy byste dali přednost druhé z nich? - K čemu slouží parametry
max_failsafail_timeoutu serveru v
upstreambloku? - Rozšiřte ukázkovou konfiguraci o třetí backend na portu 8003, který má
sloužit jen jako záložní server (backup), a vysvětlete, kdy se
použije.
Lekce na sebe nejsou zamčené — libovolnou lekci můžete otevřít i označit jako hotovou v jakémkoliv pořadí.