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. View Slide

  2. Slow websites
    SUCK

    View Slide

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

    View Slide

  4. SLOW ~ DOWN

    View Slide

  5. View Slide

  6. View Slide

  7. View Slide

  8. THROWING
    SERVERS
    AT THE PROBLEM

    View Slide

  9. MO' MONEY
    MO' SERVERS
    MO' PROBLEMS

    View Slide

  10. IDENTIFY SLOWEST PARTS

    View Slide

  11. OPTIMIZE

    View Slide

  12. AFTER A WHILE YOU HIT THE LIMITS

    View Slide

  13. CACHE

    View Slide

  14. HI, I'M THIJS

    View Slide

  15. I'M AN
    EVANGELIST
    AT

    View Slide

  16. View Slide

  17. View Slide

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

    View Slide

  19. View Slide

  20. I'M @THIJSFERYN

    View Slide

  21. View Slide

  22. BEYOND
    BASIC
    WEB
    ACCELERATION?

    View Slide

  23. BASIC

    View Slide

  24. CONTENT DELIVERY CHALLENGES

    View Slide

  25. REDUCE LATENCY

    View Slide

  26. IMPACT ON YOUR SERVERS

    View Slide

  27. COMPUTING POWER

    View Slide

  28. NETWORK
    CAPACITY

    View Slide

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

    View Slide

  30. REVERSE CACHING PROXY

    View Slide

  31. NORMALLY
    USER SERVER

    View Slide

  32. WITH REVERSE CACHING PROXY
    USER PROXY SERVER

    View Slide

  33. View Slide

  34. View Slide

  35. WHY NOT
    USE A CDN?

    View Slide

  36. VARNISH IS CDN SOFTWARE!

    View Slide

  37. ORIGIN SHIELD

    View Slide

  38. VARNISH CAN BE
    YOUR CDN

    View Slide

  39. WHY IS VARNISH
    SO POWERFUL?

    View Slide

  40. 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 Slide

  41. ORIGIN
    VARNISH
    REQUEST
    COALESCING

    View Slide

  42. POWER
    THE REASON WHY PEOPLE TRUST VARNISH

    View Slide

  43. HTTP
    THE REASON WHY PEOPLE USE VARNISH

    View Slide

  44. 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 Slide

  45. CACHE VARIATIONS
    Vary: Accept-Language

    View Slide

  46. 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 Slide

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

    View Slide

  48. STATE

    View Slide

  49. COOKIES

    View Slide

  50. IN AN IDEAL WORLD

    View Slide

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

    View Slide

  52. REALITY
    SUCKS

    View Slide

  53. View Slide

  54. View Slide

  55. TIME TO LIVE

    View Slide

  56. CACHE VARIATIONS

    View Slide

  57. LEGACY

    View Slide

  58. VCL
    THE REASON WHY PEOPLE ❤ VARNISH

    View Slide

  59. View Slide

  60. View Slide

  61. 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 Slide

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

    View Slide

  63. 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 Slide

  64. 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 Slide

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

    View Slide

  66. NEXT LEVEL

    View Slide

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

    View Slide

  68. AN HTTP
    LOGIC BOX

    View Slide

  69. DECISION MAKING AT THE EDGE

    View Slide

  70. 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 Slide

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

    View Slide

  72. 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 Slide

  73. 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 Slide

  74. 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 Slide

  75. 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 Slide

  76. 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 Slide

  77. 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 Slide

  78. VARNISH DOESN'T JUST
    ACCELERATE WEB PAGES

    View Slide

  79. ONLINE VIDEO STREAMING

    View Slide

  80. View Slide

  81. PACKAGING

    View Slide

  82. HTTP LIVE STREAMING (HLS)

    View Slide

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

    View Slide

  84. #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 Slide






  85. setup='{}'>





    View Slide

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

    View Slide

  87. 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 Slide

  88. OUR BIGGEST CLIENTS ARE IN BROADCASTING

    View Slide

  89. 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 Slide

  90. 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 Slide

  91. View Slide

  92. 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 Slide

  93. View Slide

  94. View Slide

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

    View Slide

  96. COMPOSER REQUIRE THIJSFERYN/EDGESTASH-TWIG-BUNDLE

    View Slide

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

    View Slide

  98. View Slide

  99. View Slide

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

    View Slide