Postavit vlastní CDN server: jednou stáhnout, tisíckrát vydat

Publikováno 8 min čtení

CDN server stáhne každý soubor ze zdroje jednou a potom ho tisíckrát vydá sám. Zdrojový server vidí už jen zlomek požadavků a návštěvníci dostanou krátké doby načítání. Takto si jeden postavíte.

Každý soubor, který si návštěvník z vaší stránky stáhne, vytáhne váš zdrojový server ze souborového systému, zabalí ho do odpovědi a vypraví ho na cestu. Při deseti návštěvnících za den si toho nikdo nevšimne. Při deseti tisících současných návštěvníků je přesně to důvod, proč se stránka zpomalí, přestože samotná aplikace nemá vůbec co dělat.

CDN server se před něj postaví. Každý soubor stáhne ze zdroje (origin) jednou a potom ho vydává sám, tolikrát, kolikrát je potřeba. Zdroj tím vidí už jen zlomek požadavků a návštěvníci dostávají své soubory ze serveru, který nedělá nic jiného než výdej. Tento článek ukazuje, kolik to skutečně přinese, jak takový server postavíte s nginx a na čem záleží u trafficu a lokality.

Co CDN server skutečně dělá

Celý postup se vypráví ve třech větách:

  • Návštěvník si vyžádá soubor od CDN serveru.
  • Pokud tam už leží, jde okamžitě ven. To je zásah.
  • Pokud tam neleží, CDN server si ho vyzvedne ze zdroje, odloží ho a vydá ho. To je minutí a na každý soubor se stane jen jednou za dobu platnosti.

Princip je stejný jako u streamovacího serveru: jednou stáhnout, mnohokrát vydat. Rozdíl je jen v tom, že streamovací server rozdává běžící tok dat a CDN server hotové soubory.

Výpočet: jak moc cache odlehčí

Rozhodující je úspěšnost cache, tedy podíl požadavků, které cache odpoví sama. Při milionu požadavků za den vypadá odlehčení takto:

Úspěšnost cachePožadavky na zdrojiOdlehčení
50 %500 000faktor 2
80 %200 000faktor 5
95 %50 000faktor 20
99 %10 000faktor 100

U statických souborů je 95 procent a více normální stav, protože obrázky, skripty a fonty se mění zřídka. Skok z 95 na 99 procent je přitom cennější, než čísla naznačují: zbývající zátěž ještě jednou zmenší na pětinu.

Zároveň se přesune traffic. Zdroj přenáší už téměř nic, CDN server o to víc. Při 500 000 vyvoláních obrázků za den po 400 KB je to 200 GB denně, tedy přibližně 6 TB za měsíc, jen za obrázky. Proto není u uzlu CDN důležitým číslem CPU, ale konektivita a objem.

Kdy se vlastní CDN server vyplatí

Komerční poskytovatelé CDN účtují za každý přenesený gigabyte. To je pohodlné a při malém objemu výhodné. Od určitého množství se poměr obrací:

PřípadVlastní CDN serverÚčtování za gigabyte
Několik GB za měsícvyplatí se málovýhodnější
Mnoho TB za měsícpevná měsíční cenaroste lineárně s objemem
Velké soubory ke stažení, videoarchivyjasně ve výhoděrychle se prodraží
Plná kontrola nad pravidly a protokolyúplnáomezená

K tomu přichází bod, který se dá špatně vyčíslit: u vlastního uzlu rozhodujete sami, co se cachuje, jak dlouho a kdy to zmizí. Nejste vázáni pravidly cizího poskytovatele a můžete vydávat i věci, které se do cizích schémat nevejdou.

Stavba s nginx jako cache

Instalace

apt update
apt install -y nginx

Víc není potřeba, cache je v nginx zabudovaná. Vytvořte adresář, v němž mají soubory ležet:

mkdir -p /var/cache/cdn
chown www-data:www-data /var/cache/cdn

Konfigurace

Cache se jednou popíše v bloku http a potom se používá v jednotlivých blocích location:

proxy_cache_path /var/cache/cdn
                 levels=1:2
                 keys_zone=cdn:100m
                 max_size=200g
                 inactive=30d
                 use_temp_path=off;

proxy_cache_key "$scheme$request_method$host$request_uri";

Hodnoty znamenají jednotlivě: keys_zone=cdn:100m stačí asi na 800 000 záznamů, max_size omezuje místo na disku, inactive=30d zahodí soubory, na které 30 dní nikdo nepožádal, a use_temp_path=off se vyhne nepotřebnému přesouvání mezi adresáři.

Pod tím vlastní server:

server {
    listen 443 ssl;
    http2 on;
    server_name cdn.example.com;

    ssl_certificate     /etc/letsencrypt/live/cdn.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/cdn.example.com/privkey.pem;

    location / {
        proxy_pass https://origin.example.com;
        proxy_set_header Host origin.example.com;
        proxy_http_version 1.1;

        proxy_cache cdn;
        proxy_cache_valid 200 301 302 24h;
        proxy_cache_valid 404 1m;

        # sloučit několik současných minutí do jednoho požadavku
        proxy_cache_lock on;
        proxy_cache_lock_timeout 10s;

        # při problémech na zdroji vydávat dál
        proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
        proxy_cache_background_update on;

        # cookies relace nesmí cache roztrhat
        proxy_ignore_headers Set-Cookie;
        proxy_hide_header Set-Cookie;

        add_header X-Cache-Status $upstream_cache_status always;
    }
}

Tři řádky z toho si zaslouží zvláštní pozornost. proxy_cache_lock se stará o to, aby u nového souboru nepožádalo zdroj sto návštěvníků současně, ale jen jeden a ostatní počkali. proxy_cache_use_stale drží výdej v chodu, zatímco se zdroj znovu spouští nebo odpovídá pomalu. A skrytí Set-Cookie zabrání tomu, aby jediná odpověď s cookie relace znehodnotila celou cache.

Zkontrolovat, zda to zabírá

nginx -t && systemctl reload nginx
curl -sI https://cdn.example.com/bild.jpg | grep -i x-cache-status
curl -sI https://cdn.example.com/bild.jpg | grep -i x-cache-status

Při prvním vyvolání tam stojí MISS, při druhém HIT. Stojí-li i podruhé MISS, je to téměř vždy kvůli jednomu ze tří obvyklých podezřelých: Set-Cookie v odpovědi, Cache-Control: no-store ze zdroje nebo chybějící údaj o délce.

Co se cachuje a co ne

Čisté řešení je, že to řekne zdroj sám. Zná svůj obsah, cache ne. Nastaví k tomu vhodné hlavičky:

ObsahHlavička na zdroji
Soubor s kontrolním součtem v názvuCache-Control: public, max-age=31536000, immutable
Obrázek nebo soubor ke stažení bez verzováníCache-Control: public, max-age=86400
HTML veřejné stránkyCache-Control: public, max-age=300
Přihlášená sekce, nákupní košíkCache-Control: private, no-store

Chcete-li rozhodovat v cache sami, jde to přes vlastní bloky location pro každý typ souboru:

location ~* \.(jpg|jpeg|png|gif|webp|avif|svg|ico|css|js|woff2|mp4|zip)$ {
    proxy_pass https://origin.example.com;
    proxy_cache cdn;
    proxy_cache_valid 200 7d;
    add_header Cache-Control "public, max-age=604800";
    add_header X-Cache-Status $upstream_cache_status always;
}

Cíleně se zbavit obsahu

Nejjednodušší cesta je nemuset mazat vůbec: kdo každému změněnému obrázku dá nový název souboru, třeba logo.a1b2c3.png, tento problém nemá. Starý soubor někdy sám vypadne z cache.

Pokud to nejde, pomůže u nginx volný modul ngx_cache_purge. Bez doplňkového modulu lze cestu vypočítat z klíče cache, protože nginx používá jeho součet MD5 jako název souboru:

KEY="httpsGETcdn.example.com/bild.jpg"
H=$(printf '%s' "$KEY" | md5sum | cut -d' ' -f1)
rm -f "/var/cache/cdn/${H: -1}/${H: -3:2}/$H"

Pro každodenní provoz obvykle stačí malý skript, který tyto řádky provede pro seznam cest.

Velké soubory a videa

U souborů nad několik set megabajtů potřebujete rozložení na bloky, jinak cache při každém skoku ve videu stáhne celý soubor znovu:

location /video/ {
    proxy_pass https://origin.example.com;
    proxy_cache cdn;
    proxy_cache_valid 200 206 7d;

    slice 1m;
    proxy_set_header Range $slice_range;
    proxy_cache_key "$uri$slice_range";
}

Tím stáhne nginx jen bloky, které jsou skutečně vyžádané. Skočí-li divák do poloviny filmu, projde linkou několik megabajtů místo několika gigabajtů.

TLS, HTTP/2 a HTTP/3

Uzel CDN vydává mnoho malých souborů do mnoha spojení, přesně tam se novější protokoly vyplatí. HTTP/2 je vyřízené pomocí http2 on;. HTTP/3 přinese něco především na mobilních připojeních, protože kvůli přepnutí sítě spojení nespadne:

listen 443 quic reuseport;
listen 443 ssl;
http2 on;
add_header Alt-Svc 'h3=":443"; ma=86400';

Nezapomeňte při tom otevřít ve firewallu UDP 443. To je nejčastější důvod, proč je HTTP/3 nastavené a přesto ho nikdo nepoužívá.

Více lokalit

Jediný uzel přinese už největší část přínosu, totiž odlehčení zdroje. Druhý přínos, krátké cesty k návštěvníkovi, přichází teprve s více lokalitami.

Rozdělení běží přes DNS: stejný název ukazuje podle původu návštěvníka na jiný uzel. Potřebujete k tomu poskytovatele DNS s geografickým přiřazením. Všechny uzly dostanou stejnou konfiguraci a stejný zdroj, nevědí o sobě nic, a právě to dělá celou stavbu tak odolnou: vypadne-li uzel, vyndáte ho z DNS a ostatní to převezmou.

KernelHost nabízí lokality v Německu, na západním pobřeží USA, na východním pobřeží USA a také v Singapuru a Jižní Koreji. Přehled s latencemi najdete na stránce lokality serverů. Jako pravidlo palce: jeden uzel na každý kontinent, na němž sedí významná část vašeho publika.

Měřit místo hádání

Zapisujte stav cache do logu, pak můžete úspěšnost cache kdykoli spočítat:

log_format cdn '$remote_addr $status $body_bytes_sent '
               '$upstream_cache_status "$request"';
access_log /var/log/nginx/cdn.log cdn;

Vyhodnotit se to dá jednořádkovým příkazem:

awk '{print $4}' /var/log/nginx/cdn.log | sort | uniq -c | sort -rn

Leží-li podíl HIT u statických souborů pod 90 procenty, hledání příčiny se téměř vždy vyplatí: obvykle jsou to cookies v odpovědích, příliš krátké doby platnosti nebo požadavky s měnícími se parametry na konci adresy, které pro cache pokaždé vypadají jako nový soubor.

Který server se hodí

Uzel CDN je výdejní stroj. Pořadí důležitých vlastností proto vypadá jinak než u aplikačního serveru:

  1. Traffic a konektivita. Tady vzniká celá zátěž. Uzel za provozu pravidelně přenese několik terabajtů za měsíc.
  2. Disk. NVMe je výrazně ve výhodě, protože cache se skládá z mnoha malých náhodných přístupů. Velikost se řídí tím, kolik z vašeho obsahu je horké, ne tím, jak velký je obsah celkem.
  3. RAM. Operační systém používá volnou paměť jako druhou cache před diskem. Více RAM tu znamená přímo více tempa.
  4. CPU. Nejméně kritické, je potřeba jen pro TLS, a to moderní procesory zvládají mimochodem.

Přesně pro tento případ jsou udělané naše root servery s neomezeným trafficem: skutečných 2 × 1 Gbit/s na server bez omezování rychlosti, neomezený objem za pevnou měsíční cenu, datacentrové NVMe v RAIDu a ochrana proti floodu 3,2 Tbps z výroby. Příprava trvá méně než 30 sekund, s plným root přístupem, IPv4 a sítí /64 IPv6. Protože se účtuje na předplacené bázi s možností zrušit kdykoli na konci měsíce, můžete začít s jedním uzlem a kdykoli přidat druhou lokalitu, až se vaše publikum rozloží.

Časté dotazy

Co přesně CDN server dělá?
Sedí mezi vašimi návštěvníky a vaším zdrojovým serverem. První požadavek na soubor předá zdroji a odpověď si při tom odloží. Všechny další požadavky na stejný soubor odpoví sám, bez toho, aby zdroj obtěžoval.
Jak moc to skutečně odlehčí?
Záleží na úspěšnosti cache. Při 95 procentech vidí váš zdroj už jen každý dvacátý požadavek, zátěž tedy spadne na jednu dvacetinu. Při 80 procentech je to stále jedna pětina. U statických souborů jako obrázky, skripty, fonty a soubory ke stažení je 95 procent a více normální.
Potřebuji k tomu komerčního poskytovatele?
Nutně ne. Vlastní CDN server se vyplatí hlavně u velkých souborů a velkého objemu, protože komerční poskytovatelé účtují za každý přenesený gigabyte. U serveru s neomezeným trafficem stojí stý terabyte totéž jako první, tedy nic navíc.
Které soubory mám cachovat a které ne?
Všechno, co vypadá pro všechny návštěvníky stejně: obrázky, videa, CSS, JavaScript, fonty, PDF, instalační soubory. Cachovat byste neměli personalizované stránky, nákupní košíky, přihlášené sekce a všechno, co nese cookie relace. Rozhodnutí nejlépe udělá zdroj přes hlavičku Cache-Control.
Jak dlouho si má cache soubory držet?
U souborů s číslem verze nebo kontrolním součtem v názvu klidně rok, ty se nikdy nemění. U obrázků a souborů ke stažení bez verzování jsou běžné hodiny až dny. U HTML, pokud ho vůbec cachujete, minuty. Důležité není přesné číslo, ale to, abyste měli způsob, jak cíleně smazat jednotlivé soubory.
Jak smažu soubor z cache?
Nejjednodušeji přes název souboru: kdo při každé změně přidělí nový název, nemusí mazat vůbec. Pokud to nejde, pomůže u nginx buď komerční modul Purge, nebo volný ngx_cache_purge. V nouzi smažete soubor cache přímo v souborovém systému, cestu lze vypočítat z klíče cache.
Co se stane, když zdrojový server vypadne?
Při správném nastavení cache dál vydává, i když záznamy už vlastně vypršely. Postarají se o to proxy_cache_use_stale s error a timeout a také proxy_cache_background_update. Vaše stránka tak zůstane dostupná, zatímco se na zdroji dělá údržba nebo se zdroj znovu spouští.
Funguje to i pro velké videosoubory?
Ano, ale jen s nastavením slice. Bez něj stáhne cache při každém skoku ve videu celý soubor ze zdroje znovu. S slice rozloží nginx soubor na bloky například po jednom megabajtu a odloží si jen ty části, které byly skutečně vyžádané.
Kolik lokalit potřebuji?
Začněte s jednou, a to tam, kde je většina vašich návštěvníků. Druhá lokalita se vyplatí teprve tehdy, když významná část publika sedí na jiném kontinentu. Rozdělení pak běží přes DNS, každý návštěvník skončí u nejbližšího uzlu.
Jak poznám, že cache funguje?
Vypisujte proměnnou upstream_cache_status jako hlavičku. Při druhém vyvolání stejného souboru tam musí stát HIT. Pokud stejnou hodnotu zapisujete navíc do logu, můžete úspěšnost cache kdykoli spočítat. Pod 90 procent u statických souborů obvykle není něco v pořádku s hlavičkami nebo s cookies.

CDN Caching nginx Reverse Proxy Performance HTTP Unlimited Traffic