Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Varnish beyond basic web acceleration - Symfony Live Berlin 2019

Thijs Feryn
September 26, 2019

Varnish beyond basic web acceleration - Symfony Live Berlin 2019

Advanced VCL examples where Varnish is used for web and API acceleration, but also for OTT video platforms and DIY CDNs.

See https://feryn.eu/speaking/varnish-beyond-basic-web-acceleration-symfony-live-berlin-2019/ for more details

Thijs Feryn

September 26, 2019
Tweet

More Decks by Thijs Feryn

Other Decks in Technology

Transcript

  1. Slow websites
    SUCK

    View full-size slide

  2. WEB PERFORMANCE IS AN
    ESSENTIAL PART OF THE
    USER EXPERIENCE

    View full-size slide

  3. THROWING
    SERVERS
    AT THE PROBLEM

    View full-size slide

  4. MO' MONEY
    MO' SERVERS
    MO' PROBLEMS

    View full-size slide

  5. IDENTIFY SLOWEST PARTS

    View full-size slide

  6. AFTER A WHILE YOU HIT THE LIMITS

    View full-size slide

  7. HI, I'M THIJS

    View full-size slide

  8. I'M AN
    EVANGELIST
    AT

    View full-size slide

  9. 4,800,000 WEBSITES
    19% OF THE TOP 10K WEBSITES

    View full-size slide

  10. I'M @THIJSFERYN

    View full-size slide

  11. BEYOND
    BASIC
    WEB
    ACCELERATION?

    View full-size slide

  12. CONTENT DELIVERY CHALLENGES

    View full-size slide

  13. REDUCE LATENCY

    View full-size slide

  14. IMPACT ON YOUR SERVERS

    View full-size slide

  15. COMPUTING POWER

    View full-size slide

  16. NETWORK
    CAPACITY

    View full-size slide

  17. DON’T RECOMPUTE
    IF THE DATA
    HASN’T CHANGED

    View full-size slide

  18. REVERSE CACHING PROXY

    View full-size slide

  19. NORMALLY
    USER SERVER

    View full-size slide

  20. WITH REVERSE CACHING PROXY
    USER PROXY SERVER

    View full-size slide

  21. WHY NOT
    USE A CDN?

    View full-size slide

  22. VARNISH IS CDN SOFTWARE!

    View full-size slide

  23. ORIGIN SHIELD

    View full-size slide

  24. VARNISH CAN BE
    YOUR CDN

    View full-size slide

  25. WHY IS VARNISH
    SO POWERFUL?

    View full-size slide

  26. WHY IS VARNISH SO POWERFUL?
    ✓ EXTREMELY LOW RESOURCE
    ✓ EXTREMELY STABLE
    ✓ 100 GBIT PER SERVER
    ✓ NO DISK ACCESS AT RUNTIME
    ✓ REQUEST COALESCING
    ✓ VARNISH CONFIGURATION LANGUAGE
    ✓ VMODS
    ✓ COMPLIES TO HTTP BEST PRACTICES
    ✓ ENTERPRISE FEATURES*

    View full-size slide

  27. ORIGIN
    VARNISH
    REQUEST
    COALESCING

    View full-size slide

  28. POWER
    THE REASON WHY PEOPLE TRUST VARNISH

    View full-size slide

  29. HTTP
    THE REASON WHY PEOPLE USE VARNISH

    View full-size slide

  30. HTTP CACHING MECHANISMS
    Expires: Sat, 09 Sep 2017 14:30:00 GMT
    Cache-control: public, max-age=3600, s-maxage=86400
    Cache-control: private, no-cache, no-store

    View full-size slide

  31. CACHE VARIATIONS
    Vary: Accept-Language

    View full-size slide

  32. CONDITIONAL REQUESTS
    HTTP/1.1 200 OK
    Host: localhost
    Etag: 7c9d70604c6061da9bb9377d3f00eb27
    Content-type: text/html; charset=UTF-8
    Hello world output
    GET / HTTP/1.1
    Host: localhost

    View full-size slide

  33. CONDITIONAL REQUESTS
    HTTP/1.1 304 Not Modified
    Host: localhost
    Etag: 7c9d70604c6061da9bb9377d3f00eb27
    GET / HTTP/1.1
    Host: localhost
    If-None-Match: 7c9d70604c6061da9bb9377d3f00eb27

    View full-size slide

  34. IN AN IDEAL WORLD

    View full-size slide

  35. ✓STATELESS
    ✓WELL-DEFINED TTL
    ✓CACHE / NO-CACHE PER RESOURCE
    ✓CACHE VARIATIONS
    ✓CONDITIONAL REQUESTS
    ✓PLACEHOLDERS FOR NON-CACHEABLE CONTENT
    IN AN IDEAL WORLD

    View full-size slide

  36. REALITY
    SUCKS

    View full-size slide

  37. TIME TO LIVE

    View full-size slide

  38. CACHE VARIATIONS

    View full-size slide

  39. VCL
    THE REASON WHY PEOPLE ❤ VARNISH

    View full-size slide

  40. sub vcl_recv {
    if (req.url ~ "^/status\.php$" ||
    req.url ~ "^/update\.php$" ||
    req.url ~ "^/admin$" ||
    req.url ~ "^/admin/.*$" ||
    req.url ~ "^/flag/.*$" ||
    req.url ~ "^.*/ajax/.*$" ||
    req.url ~ "^.*/ahah/.*$") {
    return (pass);
    }
    }
    URL
    blacklist
    example

    View full-size slide

  41. sub vcl_recv {
    if (req.url ~ "^/some-page"
    return (hash);
    }
    }
    URL
    whitelist
    example

    View full-size slide

  42. vcl 4.0;
    sub vcl_recv {
    if (req.http.Cookie) {
    set req.http.Cookie = ";" + req.http.Cookie;
    set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";");
    set req.http.Cookie = regsuball(req.http.Cookie,
    ";(SESS[a-z0-9]+|SSESS[a-z0-9]+|NO_CACHE)=", "; \1=");
    set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", "");
    set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");
    if (req.http.Cookie == "") {
    unset req.http.Cookie;
    }
    else {
    return (pass);
    }
    }
    }
    Only
    keep certain
    cookies

    View full-size slide

  43. sub vcl_recv {
    if (req.http.Cookie) {
    set req.http.Cookie = ";" + req.http.Cookie;
    set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";");
    set req.http.Cookie = regsuball(req.http.Cookie, ";(language)=", "; \1=");
    set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", "");
    set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");
    if (req.http.cookie ~ "^\s*$") {
    unset req.http.cookie;
    return(pass);
    }
    return(hash);
    }
    }
    sub vcl_hash {
    hash_data(regsub( req.http.Cookie, "^.*language=([^;]*);*.*$", "\1" ));
    }
    Cookie
    cache
    variation

    View full-size slide

  44. THE VMOD ECOSYSTEM
    HTTPS://VARNISH-CACHE.ORG/VMODS/
    HTTPS://DOCS.VARNISH-SOFTWARE.COM/VARNISH-CACHE-PLUS/VMODS/

    View full-size slide

  45. NOT JUST A CACHE
    NOT JUST "TAKE IT OR LEAVE IT"

    View full-size slide

  46. AN HTTP
    LOGIC BOX

    View full-size slide

  47. DECISION MAKING AT THE EDGE

    View full-size slide

  48. import accept;
    sub vcl_init {
    new format = accept.rule("text/plain");
    format.add("text/html");
    format.add("application/json");
    new lang = accept.rule("en");
    lang.add("fr");
    }
    sub vcl_recv {
    set req.http.accept = format.filter(req.http.accept);
    set req.http.accept-language = lang.filter(req.http.accept-language);
    }
    Sanitize
    accept
    headers

    View full-size slide

  49. CACHE VARIATIONS
    VARY: ACCEPT-LANGUAGE VARY: ACCEPT
    ✓ EN
    ✓ FR
    ✓ TEXT/HTML
    ✓ APPLICATION/JSON

    View full-size slide

  50. import crypto;
    import edgestash;
    import xbody;
    sub vcl_backend_response{
    if (beresp.http.Content-Type ~ "html") {
    xbody.regsub("", "<script nonce={{nonce}}>");<br/>edgestash.parse_response();<br/>}<br/>}<br/>sub vcl_deliver {<br/>if (edgestash.is_edgestash()) {<br/>set req.http.X-nonce = crypto.hex_encode(crypto.urandom(16));<br/>set resp.http.Content-Security-Policy = "script-src 'nonce-" +<br/>req.http.X-nonce + "'";<br/>edgestash.add_json({"<br/>{<br/>"nonce":""} + req.http.X-nonce + {""<br/>}<br/>"});<br/>edgestash.execute();<br/>}<br/>}<br/>Personalized<br/>Javascript CSP<br/>nonce<br/>

    View full-size slide

  51. import cookieplus;
    import edgestash;
    import kvstore;
    import xbody;
    sub vcl_init
    {
    new session_json = kvstore.init();
    }
    sub vcl_recv
    {
    unset req.http.X-do-capture;
    unset req.http.X-session;
    if (req.method != "HEAD" && req.method != "GET") {
    return (pass);
    }
    if (!session_json.get(cookieplus.get("session"))) {
    return (pass);
    }
    return (hash);
    }
    CACHE
    PERSONALIZATION

    View full-size slide

  52. sub vcl_pass
    {
    set req.http.X-do-capture = "true";
    }
    sub vcl_backend_response
    {
    if (beresp.http.Content-Type ~ "text") {
    if (bereq.http.X-do-capture) {
    xbody.capture("name", "Hello (\S+)", "\1");
    } else {
    xbody.regsub("Hello \S+", "Hello {{name}}");
    edgestash.parse_response();
    }
    }
    }
    sub vcl_deliver
    {
    if (cookieplus.get("session")) {
    set req.http.X-session = cookieplus.get("session");
    } else if (cookieplus.setcookie_get("session")) {
    set req.http.X-session = cookieplus.setcookie_get("session");
    }
    if (req.http.X-session && xbody.get("name")) {
    session_json.set(req.http.X-session, xbody.get_all(), 24h);
    }
    if (edgestash.is_edgestash()) {
    edgestash.add_json(session_json.get(req.http.X-session));
    edgestash.execute();
    }
    }
    Capture
    value
    Turn value
    into
    placeholder
    Store
    value in K/V
    store
    Parse value
    into
    placeholder

    View full-size slide

  53. import deviceatlas;
    sub vcl_recv {
    set req.http.MobilePhone = "no";
    if (deviceatlas.lookup_int(req.http.User-Agent, "isMobilePhone")) {
    set req.http.MobilePhone = "yes";
    }
    }
    Device
    detection

    View full-size slide

  54. vcl 4.0;
    import mmdb;
    backend default { .host = "192.0.2.11"; .port = "8080"; }
    # create a database object
    sub vcl_init {
    new geodb = mmdb.init("/path/to/db");
    }
    sub vcl_recv {
    # retrieve the name of the request's origin
    set req.http.Country-Name = geodb.country_name(client.ip);
    # if the country doesn't come from Germany or Belgium, deny access
    if (req.http.Country-Name != "Germany" ||
    req.http.Country-Name != "Belgium") {
    return (synth(403, "Sorry, only available in Germany and Belgium"));
    }
    }
    Geo
    blocking

    View full-size slide

  55. vcl 4.0;
    import http;
    backend default {
    .host = "origin";
    .port = "80";
    }
    sub vcl_recv {
    set req.http.X-prefetch = http.varnish_url("/");
    }
    sub vcl_backend_response {
    if (beresp.http.Link ~ "<.+>.*(prefetch|next)") {
    set bereq.http.X-link = regsub(beresp.http.Link, "^.*<([^>]*)>.*$", "\1");
    set bereq.http.X-prefetch = regsub(bereq.http.X-prefetch, "/$", bereq.http.X-link);
    http.init(0);
    http.req_copy_headers(0);
    http.req_set_url(0, bereq.http.X-prefetch);
    http.req_send_and_finish(0);
    }
    }
    Pre-fetching

    View full-size slide

  56. VARNISH DOESN'T JUST
    ACCELERATE WEB PAGES

    View full-size slide

  57. ONLINE VIDEO STREAMING

    View full-size slide

  58. HTTP LIVE STREAMING (HLS)

    View full-size slide

  59. VIDEO.MP4
    STREAM.M3U8
    STREAM_01.TS
    STREAM_02.TS
    STREAM_03.TS ENCODING & PACKAGING
    VIDEO: H264
    AUDIO: AAC

    View full-size slide

  60. #EXTM3U
    #EXT-X-VERSION:3
    #EXT-X-TARGETDURATION:6
    #EXT-X-MEDIA-SEQUENCE:0
    #EXT-X-PLAYLIST-TYPE:VOD
    #EXTINF:6.000000,
    stream_00.ts
    #EXTINF:6.000000,
    stream_01.ts
    #EXTINF:6.000000,
    stream_02.ts
    #EXTINF:6.000000,
    stream_03.ts
    #EXTINF:6.000000,
    stream_04.ts
    #EXTINF:6.000000,
    stream_05.ts
    #EXTINF:6.000000,
    stream_06.ts
    #EXTINF:6.000000,
    stream_07.ts
    #EXTINF:6.000000,
    stream_08.ts
    #EXTINF:6.000000,
    stream_09.ts
    #EXTINF:5.280000,
    stream_010.ts
    #EXT-X-ENDLIST
    STREAM.M3U8

    View full-size slide

  61. MORE VIDEO FEATURES
    ✓ GEOIP
    ✓ DIGITAL RIGHTS MANAGEMENT
    ✓ AUTHENTICATION
    ✓ THROTTLING & RATE LIMITING
    ✓ ADAPTIVE BITRATE FILTERING

    View full-size slide

  62. vcl 4.1;
    import http;
    import std;
    backend default {
    .host = "origin";
    .port = "80";
    }
    sub vcl_recv {
    if (req.url ~ "^/vod/.+\.ts$") {
    http.init(0);
    http.req_set_max_loops(0,1);
    http.req_copy_headers(0);
    http.req_set_method(0, "HEAD");
    set req.http.x-next-url = http.prefetch_next_url();
    std.log("Prefetching " + req.http.x-next-url);
    http.req_set_url(0, req.http.x-next-url);
    http.req_send_and_finish(0);
    }
    }

    View full-size slide

  63. OUR BIGGEST CLIENTS ARE IN BROADCASTING

    View full-size slide

  64. vcl 4.0;
    import cookieplus;
    import redis;
    import synthbackend;
    sub vcl_init
    {
    new db = redis.db(
    location="redis:6379",
    type=master,
    connection_timeout=500,
    shared_connections=false,
    max_connections=1);
    }
    sub vcl_recv
    {
    set req.http.X-Login = "false";
    set req.http.x-session = cookieplus.get("PHPSESSID","guest");
    if(req.http.x-session != "guest") {
    db.command("EXISTS");
    db.push("sf_s"+cookieplus.get("PHPSESSID"));
    db.execute();
    if(db.get_integer_reply() == 1) {
    set req.http.X-Login = "true";
    }
    }
    }
    Synthetic
    HTTP

    View full-size slide

  65. sub vcl_backend_fetch
    {
    if(bereq.url == "/session") {
    if(bereq.http.X-Login != "true") {
    set bereq.backend = synthbackend.from_string("{}");
    return(fetch);
    }
    db.command("EVAL");
    db.push({"
    local session = redis.call('GET', KEYS[1])
    if session == nil then
    return '{}'
    end
    local result = string.gsub(session, '[%c]', '')
    local username = string.gsub(result,'.+Userusername\";s:[0-9]+:\"([^\"]+)\";.+','%1')
    if username == nil then
    return '{}'
    end
    return '{"username":"'.. username ..'"}'
    "});
    db.push(1);
    db.push("sf_s"+cookieplus.get("PHPSESSID"));
    db.execute();
    set bereq.backend = synthbackend.from_string(db.get_string_reply());
    }
    }
    sub vcl_backend_response
    {
    if(bereq.url == "/session") {
    set beresp.http.Content-Type = "application/json; charset=utf-8";
    set beresp.ttl = 3600s;
    set beresp.http.vary = "x-session";
    }
    }
    Synthetic
    HTTP

    View full-size slide

  66. VARNISH ENTERPRISE
    ✓ CLIENT SSL TERMINATION
    ✓ BACKEND SSL CONNECTIONS
    ✓ PARALLEL ESI
    ✓ MASSIVE STORAGE ENGINE
    ✓ ENCRYPTION
    ✓ THROTTLING
    ✓ RATE LIMITING
    ✓ PREFETCHING
    ✓ GEOLOCATION
    ✓ AUTHENTICATION
    ✓ EDGESTASH
    ✓ CUSTOM STATISTICS
    ✓ ADMIN MODULE
    ✓ SUPPORT
    ✓ HIGH AVAILABILITY
    ✓ MOBILE APP

    View full-size slide

  67. HTTPS://GITHUB.COM/THIJSFERYN/EDGESTASHTWIGBUNDLE

    View full-size slide

  68. COMPOSER REQUIRE THIJSFERYN/EDGESTASH-TWIG-BUNDLE

    View full-size slide

  69. {{ edgestash('username','/session') }}
    {{ username | edgestash('username','/session') }}

    View full-size slide

  70. HTTPS://FERYN.EU
    HTTPS://TWITTER.COM/THIJSFERYN
    HTTPS://INSTAGRAM.COM/THIJSFERYN

    View full-size slide