Modul 5 — Webové servery a reverzní proxy

24. Ladění výkonu webového serveru a cachování

Gzip/brotli komprese, HTTP cachovací hlavičky, cachování na úrovni Nginx a základní zátěžové testování.

Odhadovaná délka studia: 55 minut · Stav: nehotovo

Technologie: Nginx

Úvod a kontext

Funkční webový server (předchozí tři lekce) je první krok — druhý je
udělat ho rychlý i pod zátěží. Dva nejúčinnější nástroje, které má
provozní inženýr k dispozici bez zásahu do samotné aplikace, jsou
komprese přenášených dat a cachování (ukládání odpovědí do
mezipaměti, aby se opakované požadavky nemusely znovu draze počítat).
Tato lekce shrnuje obě techniky na úrovni Nginx a ukazuje základní způsob,
jak zjistit, jestli změna výkon skutečně zlepšila.

Teorie

Proč záleží na výkonu webového serveru

Pomalá odpověď serveru znamená déle čekajícího uživatele, vyšší zátěž
serveru při stejném počtu požadavků a horší chování pod špičkovou zátěží
(víc souběžně otevřených spojení, protože každé trvá déle). Dvě hlavní
páky, které nevyžadují zásah do aplikace samotné:

  • méně dat na drátě — komprese,
  • méně výpočtů/dotazů na backend — cachování.

Komprese (gzip, brotli)

HTTP odpovědi (zejména textové — HTML, CSS, JavaScript, JSON) lze před
odesláním komprimovat; prohlížeč je na straně klienta automaticky
rozbalí. Textový obsah se kompresí typicky zmenší o 60–80 %.

http {
    gzip on;
    gzip_types text/plain text/css application/javascript application/json;
    gzip_min_length 1024;
    gzip_comp_level 5;
}
  • gzip_types — komprimují se jen vyjmenované typy obsahu; obrázky
    (JPEG, PNG) a videa jsou už komprimované samy o sobě, další komprese by
    jen plýtvala CPU bez přínosu,
  • gzip_min_length 1024 — nemá smysl komprimovat velmi malé odpovědi,
    režie komprese by převážila úsporu,
  • gzip_comp_level — úroveň komprese 1–9; vyšší = menší výstup, ale víc
    CPU. Hodnota 5–6 je obvykle rozumný kompromis.

Modernější a účinnější alternativou je brotli (vyžaduje doplňkový
modul, není součástí standardní instalace Nginx) — dosahuje o něco menší
výsledné velikosti než gzip při srovnatelné rychlosti.

HTTP cachovací hlavičky

Server může prohlížeči (nebo mezilehlé cachi) říct, jak dlouho si má
odpověď uložit, aby ji příště nemusel stahovat znovu:

location /static/ {
    expires 30d;
    add_header Cache-Control "public, immutable";
}
  • expires 30d — nastaví hlavičku Expires a Cache-Control: max-age=
    na 30 dní; prohlížeč po tuto dobu soubor vůbec znovu nestáhne,
  • immutable — signalizuje, že se obsah souboru na dané URL nikdy
    nezmění (typické pro statické soubory se "fingerprintem" v názvu, např.
    app.a3f21c.js — když se obsah změní, změní se i název souboru).

Pro dynamický obsah, který se často mění, je naopak vhodné cachování
explicitně zakázat:

location /api/ {
    add_header Cache-Control "no-store";
}

no-store říká prohlížeči i mezilehlým cachím, ať odpověď vůbec
neukládají — vhodné pro citlivá nebo často se měnící data.

Cachování na úrovni Nginx (proxy cache)

Nginx umí cachovat i odpovědi z backendu, na který funguje jako reverzní
proxy (viz minulá lekce) — opakované požadavky na stejnou URL pak vůbec
nedojdou k aplikaci:

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=moje_cache:10m max_size=1g;

server {
    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_cache moje_cache;
        proxy_cache_valid 200 10m;
        proxy_cache_valid 404 1m;
        add_header X-Cache-Status $upstream_cache_status;
    }
}
  • proxy_cache_path — kam a jak velkou cache ukládat na disk
    (keys_zone=moje_cache:10m vyhrazuje 10 MB paměti pro metadata cache,
    max_size=1g je limit velikosti na disku),
  • proxy_cache_valid 200 10m — úspěšné odpovědi (200) se cachují na 10
    minut, 404 jen na 1 minutu (aby se chybějící stránka rychle "opravila"
    poté, co ji administrátor doplní),
  • X-Cache-Status — přidá do odpovědi hlavičku ukazující, jestli odpověď
    přišla z cache (HIT) nebo z backendu (MISS) — nejrychlejší způsob,
    jak si přímo v prohlížeči nebo přes curl -I ověřit, že cachování
    funguje.

Proxy cache je mocný nástroj, ale je potřeba ji používat opatrně u
obsahu specifického pro konkrétního uživatele (přihlášený uživatel,
osobní data) — bez správného rozlišení klíče cache (proxy_cache_key)
hrozí, že jeden uživatel uvidí cachovanou odpověď určenou jinému.

Základní zátěžové testování

Než a po jakékoliv úpravě výkonu je vhodné změnu změřit, ne jen
"odhadnout, že to bude rychlejší". Jednoduchý nástroj ab (Apache
Bench, součást balíčku apache2-utils, funguje i pro testování Nginx):

ab -n 1000 -c 50 http://example.cz/
  • -n 1000 — celkem 1000 požadavků,
  • -c 50 — 50 souběžných spojení najednou.

Výstup obsahuje mimo jiné Requests per second (kolik požadavků server
zvládne za sekundu) a Time per request (průměrná doba odpovědi) — tato
dvě čísla se porovnávají před a po úpravě konfigurace, aby bylo vidět,
jestli změna skutečně pomohla.

Praktický příklad

Kombinovaná konfigurace se statickým obsahem s dlouhou platností v cachi,
kompresí a proxy cache pro dynamický obsah z backendu:

http {
    gzip on;
    gzip_types text/plain text/css application/javascript application/json;
    gzip_min_length 1024;

    proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=kurzy_cache:10m max_size=500m;

    server {
        listen 80;
        server_name kurzy.example.cz;

        location /static/ {
            alias /home/petr/ai/tutor/staticfiles/;
            expires 30d;
            add_header Cache-Control "public, immutable";
        }

        location / {
            proxy_pass http://127.0.0.1:8000;
            proxy_set_header Host $host;
            proxy_cache kurzy_cache;
            proxy_cache_valid 200 5m;
            add_header X-Cache-Status $upstream_cache_status;
        }
    }
}

Ověření, že proxy cache funguje, dvěma po sobě jdoucími požadavky:

curl -sI http://kurzy.example.cz/ | grep X-Cache-Status   # MISS (první požadavek)
curl -sI http://kurzy.example.cz/ | grep X-Cache-Status   # HIT (druhý požadavek, z cache)

Porovnání výkonu s cachí a bez ní (pro test dočasně vypnuté proxy_cache):

ab -n 500 -c 20 http://kurzy.example.cz/

Shrnutí

Komprese (gzip) snižuje objem přenášených dat, dlouhé cachovací hlavičky
(expires, Cache-Control) šetří opakované stahování na straně klienta a
proxy cache na úrovni Nginx šetří výpočetní zátěž backendu tím, že
opakované požadavky vůbec nedojdou k aplikaci. Hlavička
X-Cache-Status (HIT/MISS) je nejrychlejší způsob ověření, že
cachování funguje, a nástroj ab umožňuje změnu výkonu skutečně změřit,
místo aby se jen odhadovala.

Kontrolní otázky

  1. Proč nemá smysl komprimovat obrázky ve formátu JPEG stejným způsobem
    jako HTML nebo CSS?
  2. K čemu slouží hlavička Cache-Control: no-store a kdy byste ji
    použili místo dlouhé platnosti (expires 30d)?
  3. Co znamená hlavička X-Cache-Status: HIT a jak byste ji použili k
    ověření, že proxy cache skutečně funguje?
  4. Proč je nebezpečné zapnout proxy_cache bez rozmyslu pro odpovědi
    obsahující data specifická pro přihlášeného uživatele?

Lekce na sebe nejsou zamčené — libovolnou lekci můžete otevřít i označit jako hotovou v jakémkoliv pořadí.