Saját CDN-szerver felépítése: egyszer lehozni, ezerszer kiszolgálni

Közzétéve 8 perc olvasás

A CDN-szerver minden fájlt egyszer hoz le a forrásról, és onnantól ezerszer maga szolgálja ki. A forrásszerver már csak a kérések töredékét látja, a látogatók pedig rövid betöltési időt kapnak. Így épít fel egyet.

Minden fájlt, amelyet egy látogató letölt az oldaláról, a forrásszerver (origin) kikeres a fájlrendszerből, becsomagol egy válaszba, és elküld. Napi tíz látogatónál ezt senki nem érzi meg. Tízezer egyidejű látogatónál viszont pontosan ez az oka annak, hogy az oldal lassú lesz, miközben a tulajdonképpeni alkalmazásnak semmi dolga sincs.

A CDN-szerver ez elé áll. Minden fájlt egyszer hoz le a forrásról, és onnantól maga szolgálja ki, ahányszor csak kell. A forrás ezért már csak a kérések töredékét látja, a látogatók pedig olyan szervertől kapják a fájljaikat, amely semmi mást nem csinál, csak kiszolgál. Ez a cikk bemutatja, mennyit hoz ez valójában, hogyan állít fel egy ilyen szervert nginxszel, és min dől el a forgalom és a helyszín kérdése.

Mit tesz valójában egy CDN-szerver

A működés három mondatban elbeszélhető:

  • Egy látogató fájlt kér a CDN-szervertől.
  • Ha az már ott van, azonnal kimegy. Ez egy találat.
  • Ha nincs ott, a CDN-szerver lehozza a forrásról, eltárolja és kiszolgálja. Ez egy tévesztés, és fájlonként csak egyszer történik meg érvényességi időszakonként.

Az elv ugyanaz, mint a streamszerver esetében: egyszer lehozni, sokszor kiszolgálni. A különbség csak annyi, hogy a streamszerver futó adatfolyamot oszt szét, a CDN-szerver pedig kész fájlokat.

A számítás: mennyit vesz le egy gyorsítótár

A döntő a találati arány, vagyis azoknak a kéréseknek a hányada, amelyeket a gyorsítótár maga válaszol meg. Napi egymillió kérésnél a tehercsökkenés így alakul:

Találati arányKérések a forrásonTehercsökkenés
50%500 000kétszeres
80%200 000ötszörös
95%50 000hússzoros
99%10 000százszoros

Statikus fájloknál 95 százalék és annál több a normális eset, mert képek, szkriptek és betűtípusok ritkán változnak. A 95-ről 99 százalékra tett lépés közben értékesebb, mint amit a számok sejtetnek: a maradék terhelést még egyszer ötödére csökkenti.

Ezzel egy időben áthelyeződik a forgalom. A forrás már alig visz át valamit, a CDN-szerver annál többet. Napi 500 000 képletöltésnél, egyenként 400 KB-tal ez naponta 200 GB, vagyis havonta körülbelül 6 TB, csak a képekre. Egy CDN-csomópontnál ezért nem a processzor a fontos szám, hanem a hálózati kapcsolat és a mennyiség.

Mikor érdemes saját CDN-szerver

A kereskedelmi CDN-szolgáltatók átvitt gigabájtonként számláznak. Ez kényelmes, és kis mennyiségnél kedvező. Egy bizonyos mennyiség felett viszont megfordul az arány:

EsetSaját CDN-szerverGigabájtonkénti számlázás
Néhány GB havontaalig érdemeskedvezőbb
Sok TB havontafix havi díjlineárisan nő vele
Nagy letöltések, videóarchívumokvilágosan előnybenhamar drága lesz
Teljes kontroll a szabályok és a protokollok felettteljeskorlátozott

Jön ehhez egy pont, amelyet nehéz számmal kifejezni: saját csomópontnál Ön dönti el, mi kerül gyorsítótárba, meddig, és mikor tűnik el. Nem köti idegen szolgáltató szabályrendszere, és olyan dolgokat is kiszolgálhat, amelyek nem illenek idegen sémákba.

Felépítés nginxszel mint gyorsítótárral

Telepítés

apt update
apt install -y nginx

Ennél több nem kell, a gyorsítótár be van építve az nginxbe. Hozza létre a könyvtárat, amelyben a fájlok lesznek:

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

Beállítás

A gyorsítótárat egyszer kell leírni a http blokkban, és utána az egyes location blokkokban használni:

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";

Az értékek külön-külön: a keys_zone=cdn:100m körülbelül 800 000 bejegyzésre elég, a max_size korlátozza a lemezen elfoglalt helyet, az inactive=30d eldobja azokat a fájlokat, amelyeket 30 napig senki nem kért, a use_temp_path=off pedig elkerüli a könyvtárak közötti fölösleges mozgatást.

Ez alatt jön a tulajdonképpeni szerver:

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;

        # több egyidejű tévesztés összevonása egyetlen kérésbe
        proxy_cache_lock on;
        proxy_cache_lock_timeout 10s;

        # kiszolgálás folytatása, ha a forráson gond van
        proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
        proxy_cache_background_update on;

        # a munkamenet-sütik nem boríthatják fel a gyorsítótárat
        proxy_ignore_headers Set-Cookie;
        proxy_hide_header Set-Cookie;

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

Ebből három sor külön figyelmet érdemel. A proxy_cache_lock gondoskodik arról, hogy egy új fájl esetén ne száz látogató kérdezze meg egyszerre a forrást, hanem csak egy, a többi pedig várjon. A proxy_cache_use_stale fenntartja a kiszolgálást, amíg a forrás újraindul vagy lassan válaszol. A Set-Cookie elrejtése pedig megakadályozza, hogy egyetlen munkamenet-sütit hordozó válasz használhatatlanná tegye az egész gyorsítótárat.

Ellenőrzés, hogy működik-e

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

Az első hívásnál MISS áll ott, a másodiknál HIT. Ha másodszorra is MISS van, az szinte mindig a három szokásos gyanúsított egyikén múlik: egy Set-Cookie a válaszban, egy Cache-Control: no-store a forrástól, vagy egy hiányzó hosszmegadás.

Mi kerül gyorsítótárba és mi nem

A tiszta megoldás az, hogy a forrás maga mondja meg. Ő ismeri a tartalmait, a gyorsítótár nem. Ehhez megfelelő fejléceket állít be:

TartalomFejléc a forráson
Fájl ellenőrzőösszeggel a nevébenCache-Control: public, max-age=31536000, immutable
Kép vagy letöltés verziózás nélkülCache-Control: public, max-age=86400
Nyilvános oldal HTML-jeCache-Control: public, max-age=300
Bejelentkezett terület, kosárCache-Control: private, no-store

Ha magában a gyorsítótárban akar dönteni, az fájltípusonként külön location blokkokkal lehetséges:

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;
}

Tartalmak célzott eltávolítása

A legegyszerűbb út az, ha egyáltalán nem kell törölni: aki minden megváltozott képnek új fájlnevet ad, például logo.a1b2c3.png, annak nincs ilyen gondja. A régi fájl valamikor magától kiesik a gyorsítótárból.

Ha ez nem megy, az nginxnél segít a szabad ngx_cache_purge modul. Kiegészítő modul nélkül az elérési út kiszámolható a gyorsítótárkulcsból, mert az nginx annak MD5-összegét használja fájlnévként:

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"

A napi üzemhez általában elég egy kis szkript, amely ezeket a sorokat egy elérési utakból álló listára futtatja le.

Nagy fájlok és videók

Néhány száz megabájt felett a fájlokat blokkokra kell bontani, különben a gyorsítótár a videóban tett minden ugrásnál újra letölti az egész fájlt:

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";
}

Ezzel az nginx csak azokat a blokkokat hozza le, amelyeket valóban kérnek. Ha egy néző egy film közepére ugrik, néhány megabájt megy át a vonalon több gigabájt helyett.

TLS, HTTP/2 és HTTP/3

Egy CDN-csomópont sok kis fájlt szolgál ki sok kapcsolaton, és pontosan itt térülnek meg az újabb protokollok. A HTTP/2 a http2 on; sorral elintézett. A HTTP/3 főleg mobilhálózaton hoz hasznot, mert hálózatváltáskor nem szakad meg a kapcsolat:

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

Ne felejtse el közben megnyitni az UDP 443-at a tűzfalban. Ez a leggyakoribb oka annak, hogy a HTTP/3 be van állítva, és mégsem használja senki.

Több helyszín

Egyetlen csomópont már a nyereség legnagyobb részét meghozza, vagyis a forrás tehermentesítését. A második nyereség, a látogatóhoz vezető rövid út, csak több helyszínnel jön el.

A szétosztás DNS-en keresztül megy: ugyanaz a név a látogató származási helye szerint más csomópontra mutat. Ehhez földrajzi hozzárendelést támogató DNS-szolgáltató kell. Minden csomópont ugyanazt a beállítást és ugyanazt a forrást kapja, egymásról nem tudnak semmit, és pontosan ez teszi a felépítést ilyen szívóssá: ha egy csomópont kiesik, kiveszi a DNS-ből, a többi pedig átveszi a munkát.

A KernelHost németországi, az Egyesült Államok nyugati partján és keleti partján lévő, valamint szingapúri és dél-koreai helyszíneket kínál. Késleltetésekkel együtt készült áttekintést a szerverhelyszínek oldalon talál. Ökölszabály: egy csomópont minden olyan kontinensre, amelyen a közönségének említésre érdemes része van.

Mérés találgatás helyett

Írja a gyorsítótár állapotát a naplóba, akkor bármikor kiszámolhatja a találati arányt:

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

Kiértékelni egy egysorossal lehet:

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

Ha a HIT aránya statikus fájloknál 90 százalék alatt van, szinte mindig megéri a hiba okát keresni: általában sütik vannak a válaszokban, túl rövidek az érvényességek, vagy a cím végén változó paraméterekkel jönnek a kérések, amelyek a gyorsítótár számára minden alkalommal új fájlnak látszanak.

Melyik szerver a megfelelő

A CDN-csomópont kiszolgálógép. A fontos jellemzők sorrendje ezért másképp alakul, mint egy alkalmazásszervernél:

  1. Forgalom és hálózati kapcsolat. Itt keletkezik a teljes terhelés. Egy csomópont üzem közben rendszeresen több terabájtot visz át havonta.
  2. Lemez. Az NVMe jelentős előnyben van, mert a gyorsítótár sok kis, véletlenszerű hozzáférésből áll. A méret ahhoz igazodik, hogy a készletéből mennyi forró, nem ahhoz, hogy a készlet összességében mekkora.
  3. Memória. Az operációs rendszer a szabad memóriát második gyorsítótárként használja a lemez előtt. Több memória itt közvetlenül több tempót jelent.
  4. Processzor. A legkevésbé kritikus, csak a TLS-hez kell, és azt a modern processzorok mellékesen elvégzik.

Pontosan erre az esetre készültek a korlátlan forgalmú root szervereink: valódi 2 × 1 Gbit/s szerverenként sávszélesség-korlátozás nélkül, korlátlan mennyiség fix havi díjon, adatközponti NVMe RAID-ben és gyárilag 3,2 Tbps floodvédelem. Az üzembe állítás 30 másodperc alatt megvan, teljes root hozzáféréssel, IPv4-címmel és egy /64-es IPv6-hálózattal. Mivel a számlázás PrePaid alapú és havonta felmondható, kezdheti egyetlen csomóponttal, és bármikor hozzávehet egy második helyszínt, ha a közönsége szétszóródik.

Gyakori kérdések

Mit tesz pontosan egy CDN-szerver?
A látogatói és a forrásszervere között ül. Egy fájl első kérését továbbadja a forrásnak, és a választ közben eltárolja. Ugyanannak a fájlnak minden további kérését maga válaszolja meg, anélkül hogy a forrást zaklatná.
Mennyire csökkenti ez valóban a terhelést?
Ez a találati arányon múlik. 95 százaléknál a forrás már csak minden huszadik kérést látja, a terhelés tehát a huszadára esik. 80 százaléknál még mindig az ötödére. Statikus fájloknál, például képeknél, szkripteknél, betűtípusoknál és letöltéseknél a 95 százalék és annál több a normális.
Kell ehhez kereskedelmi szolgáltató?
Nem feltétlenül. A saját CDN-szerver főleg nagy fájloknál és nagy mennyiségnél érdemes, mert a kereskedelmi szolgáltatók átvitt gigabájtonként számláznak. Korlátlan forgalmú szervernél a századik terabájt ugyanannyiba kerül, mint az első, vagyis semmi többe.
Mely fájlokat tegyem gyorsítótárba és melyeket ne?
Mindent, ami minden látogatónak ugyanúgy néz ki: képek, videók, CSS, JavaScript, betűtípusok, PDF, telepítőfájlok. Nem érdemes gyorsítótárba tenni a személyre szabott oldalakat, a kosarakat, a bejelentkezett területeket és mindent, ami munkamenet-sütit hordoz. A döntést legjobb a forrásra hagyni a Cache-Control fejlécen keresztül.
Meddig tartsa meg a gyorsítótár a fájlokat?
A nevükben verziószámot vagy ellenőrzőösszeget hordozó fájloknál akár egy évig, azok sosem változnak. Verziózás nélküli képeknél és letöltéseknél órák és napok szokásosak. HTML-nél, ha egyáltalán gyorsítótárba teszi, percek. Nem a pontos szám a fontos, hanem hogy legyen módja egyes fájlokat célzottan törölni.
Hogyan törlök egy fájlt a gyorsítótárból?
A legegyszerűbben a fájlnéven keresztül: aki minden változásnál új nevet ad, annak egyáltalán nem kell törölnie. Ha ez nem megy, az nginxnél vagy a kereskedelmi purge modul, vagy a szabad ngx_cache_purge segít. Végső esetben közvetlenül a fájlrendszerben törli a gyorsítótárfájlt, az elérési út kiszámolható a gyorsítótárkulcsból.
Mi történik, ha a forrásszerver kiesik?
Jól beállítva a gyorsítótár tovább szolgál ki, akkor is, ha a bejegyzések tulajdonképpen lejártak. Ezt a proxy_cache_use_stale biztosítja az error és a timeout értékkel, valamint a proxy_cache_background_update. Az oldala így elérhető marad, amíg a forrást karbantartják vagy újraindul.
Működik ez nagy videófájlokkal is?
Igen, de csak a slice beállítással. Nélküle a gyorsítótár a videóban tett minden ugrásnál újra letölti a teljes fájlt a forrásról. A slice révén az nginx például egymegabájtos blokkokra bontja a fájlt, és csak a valóban kért részeket tárolja el.
Hány helyszínre van szükségem?
Kezdje eggyel, és ott, ahol a látogatói nagy része van. A második helyszín csak akkor érdemes, ha a közönség említésre érdemes része másik kontinensen ül. A szétosztás ilyenkor DNS-en keresztül megy, minden látogató a hozzá legközelebbi csomópontnál érkezik meg.
Miből látom, hogy a gyorsítótár működik?
Adja ki az upstream_cache_status változót fejlécként. Ugyanannak a fájlnak a második hívásánál HIT kell ott állnia. Ha ugyanezt az értéket a naplóba is kiírja, bármikor kiszámolhatja a találati arányt. Statikus fájloknál 90 százalék alatt általában a fejlécekkel vagy a sütikkel van valami gond.

CDN Caching nginx Reverse Proxy Performance HTTP Unlimited Traffic