Varnish
The 9 circles of hell
By

04 Dec 2013
Who am i
●
●

●

Luis Ferro
Lead Developer in Rewards and Loyalty
Programs (Metropolis International)
When not coding, you can find me shooting
arrows or cooking, mostly...

http://www.linkedin.com/profile/view?id=73429631
Varnish before Hell Entrance
●

“Varnish is an HTTP accelerator designed for
content-heavy dynamic web sites. Varnish is
focused exclusively on HTTP.” - wikipedia

●

https://www.varnish-cache.org

●

Installing (debian/ubuntu)
sudo apt-get update
sudo apt-get install varnish
st

1 Circle - basics
●

Simple caching

●

Varnish, NGinX and PHP should be working

●

Default configurations apart from the ports
which should be:
Port 80 – Varnish, with backend to 8080
Port 8080 – NGinX, with backend to 9000
Port 9000 – PHP
st

1 – Backend in default.vcl
backend default {
.host = "127.0.0.1";
.port = "8080";
.connect_timeout = 30s;
.first_byte_timeout = 30s;
.between_bytes_timeout = 30s;
}
Check configuration with:
varnishd -C -f default.vcl
st

1 - Checking it in - NGinX
NGinX Test
ab -n 10000 -c 10 http://127.0.0.1:8080/index.php

Requests per second:

253.01 [#/sec] (mean)

Time per request:

39.524 [ms] (mean)

Time per request:

3.952 [ms] (mean, across all concurrent requests)

Transfer rate:

29931.75 [Kbytes/sec] received
st

1 - Checking it in - Varnish
Varnish Test
ab -n 10000 -c 10 http://127.0.0.1:80/index.php

Requests per second:

2624.59 [#/sec] (mean)

Time per request:

3.810 [ms] (mean)

Time per request:

0.381 [ms] (mean, across all concurrent requests)

Transfer rate:

311418.96 [Kbytes/sec] received

10x FASTER
With almost default config
nd

2 Circle – The path to cache
●

Add hit / miss information to all requests
sub vcl_deliver {
if (obj.hits > 0) {
set resp.http.X-Cache = "HIT";
set resp.http.X-Cache-Hits = obj.hits;
} else {
set resp.http.X-Cache = "MISS";
}
return (deliver);
}
nd

2 - Check
rd

3 Circle – Force march
●

Force Refresh
sub vcl_recv { [...]
if (req.http.Cache-Control ~ "no-cache") {
ban_url(req.url);
}
[…] }

●

You may want to protect / restrict who can call
this “ban”
rd

3 - Check
rd

4 Circle – Expired content
●

Add php headers to force content to expire after
a certain quantity of seconds
function headers_for_page_cache($cache_length=600){
$cache_expire_date = gmdate("D, d M Y H:i:s", time() + $cache_length);
header("Expires: $cache_expire_date");
header("Pragma: cache");
header("Cache-Control: max-age=$cache_length, s-maxage=$cache_length");
header("User-Cache-Control: max-age=$cache_length");
}
th

4 Circle - Check
th

5 Circle – All in the bag
●

Ensure cache by delete cookies on unwanted
items (images, css, js)
sub vcl_recv { […]
if (req.url ~ ".(png|gif|jpg)$") {
remove req.http.Cookie;
}
[...]}

●

Varnish doesn't cache anything with a cookie!
th

5 - Check
th

6 Circle – Watch your steps
●

Install GeoIp

●

Get GeoipUsingInlineC from:
https://www.varnish-cache.org/trac/wiki/GeoipUsingInlineC

●

Compile and install it (findable by your OS)
th

6 - Geoip.vcl
C{
#include <dlfcn.h>
#include <stdlib.h>
#include <stdio.h>
static const char* (*get_country_code)(char* ip) = NULL;
__attribute__((constructor)) void
load_module()
{
const char* symbol_name = "get_country_code";
const char* plugin_name = "/usr/lib/geoip_plugin.so";
void* handle = NULL;
handle = dlopen( plugin_name, RTLD_NOW );
if (handle != NULL) {
get_country_code = dlsym( handle, symbol_name );
if (get_country_code == NULL)
fprintf( stderr, "nError: Could not load GeoIP plugin:n%snn", dlerror() );
else
printf( "GeoIP plugin loaded successfully.n");
}
else
fprintf( stderr, "nError: Could not load GeoIP plugin:n%snn", dlerror() );
}
}C
th

6 - Add it to default.vcl
sub vcl_recv { […]
C{
VRT_SetHdr(sp, HDR_REQ, "017X-Country-Code:",
(*get_country_code)( VRT_IP_string(sp, VRT_r_client_ip(sp)) ),
vrt_magic_string_end);
}C
[…] }
th

6 - Check
th

7 Circle – Know your enemies
●

Device Detection

●

Download the devicedetection.vcl from:
http://github.com/varnish/varnish-devicedetect/

●

Add it to default.vcl
sub vcl_recv { […]
call detectdevice;
[…] }
th

7 - Check
th

8 Circle – Tongue in cheek
●

Accepted language

●

Download and create the accept-language.vcl from:
http://github.com/cosimo/varnish-accept-language

●

Include the generated file and call it in default.vcl
sub vcl_recv { […]
C{
vcl_rewrite_accept_language(sp);
}C
[…] }
th

8 - Check
th

9 Circle – Divide and conquer
●

To activate add in your default.vcl
sub vcl_fetch { [...]
if( beresp.http.esi-enabled == "1") {
set beresp.do_esi = true; /* Do ESI processing */
set beresp.ttl = 120s; /* Sets the TTL on the HTML
above */
unset beresp.http.esi-enabled;
} [...]
th

9 - Check

Remember, ESI has issues with non-xml like files. So, the file where you
place the <esi: instructions have to look like a xml file.
More to come... if there was time...
●

Redirects

●

ESI – Sessions / Users

●

Purge

●

SSL / SPDY / Compression

●

Load Balancing

●

Webserver (O.o really?)
Varnish Resources
●

The Book
https://www.varnish-software.com/static/book/

●

Cache Tutorial
http://www.mnot.net/cache_docs/

●

Slides (slides used on the talk)
Slides will be made available later on slideshare

●

Sourcecode (configs, test scripts, etc)
GitHub: https://github.com/lferro9000/varnish-talk
Thank you for listening and...
●

●

●

… Specially to varnish-software and everyone that
somehow contributed directly to enrich varnish.
Always check varnish-software website in case of doubt,
there is an huge amount of little tricks there.
Remember: using varnish, is good for the environment
(less power = win)!
Credits and Thanks
●

Botticelli Chart of Dante's Hell
http://www.flickr.com/photos/distan/3708997567/
by Dina Dubrovskaya (Real Distan)

●

The great people that made Varnish possible

●

PHP London for accepting this talk

PHP London Dec 2013 - Varnish - The 9 circles of hell

  • 1.
    Varnish The 9 circlesof hell By 04 Dec 2013
  • 2.
    Who am i ● ● ● LuisFerro Lead Developer in Rewards and Loyalty Programs (Metropolis International) When not coding, you can find me shooting arrows or cooking, mostly... http://www.linkedin.com/profile/view?id=73429631
  • 3.
    Varnish before HellEntrance ● “Varnish is an HTTP accelerator designed for content-heavy dynamic web sites. Varnish is focused exclusively on HTTP.” - wikipedia ● https://www.varnish-cache.org ● Installing (debian/ubuntu) sudo apt-get update sudo apt-get install varnish
  • 4.
    st 1 Circle -basics ● Simple caching ● Varnish, NGinX and PHP should be working ● Default configurations apart from the ports which should be: Port 80 – Varnish, with backend to 8080 Port 8080 – NGinX, with backend to 9000 Port 9000 – PHP
  • 5.
    st 1 – Backendin default.vcl backend default { .host = "127.0.0.1"; .port = "8080"; .connect_timeout = 30s; .first_byte_timeout = 30s; .between_bytes_timeout = 30s; } Check configuration with: varnishd -C -f default.vcl
  • 6.
    st 1 - Checkingit in - NGinX NGinX Test ab -n 10000 -c 10 http://127.0.0.1:8080/index.php Requests per second: 253.01 [#/sec] (mean) Time per request: 39.524 [ms] (mean) Time per request: 3.952 [ms] (mean, across all concurrent requests) Transfer rate: 29931.75 [Kbytes/sec] received
  • 7.
    st 1 - Checkingit in - Varnish Varnish Test ab -n 10000 -c 10 http://127.0.0.1:80/index.php Requests per second: 2624.59 [#/sec] (mean) Time per request: 3.810 [ms] (mean) Time per request: 0.381 [ms] (mean, across all concurrent requests) Transfer rate: 311418.96 [Kbytes/sec] received 10x FASTER With almost default config
  • 8.
    nd 2 Circle –The path to cache ● Add hit / miss information to all requests sub vcl_deliver { if (obj.hits > 0) { set resp.http.X-Cache = "HIT"; set resp.http.X-Cache-Hits = obj.hits; } else { set resp.http.X-Cache = "MISS"; } return (deliver); }
  • 9.
  • 10.
    rd 3 Circle –Force march ● Force Refresh sub vcl_recv { [...] if (req.http.Cache-Control ~ "no-cache") { ban_url(req.url); } […] } ● You may want to protect / restrict who can call this “ban”
  • 11.
  • 12.
    rd 4 Circle –Expired content ● Add php headers to force content to expire after a certain quantity of seconds function headers_for_page_cache($cache_length=600){ $cache_expire_date = gmdate("D, d M Y H:i:s", time() + $cache_length); header("Expires: $cache_expire_date"); header("Pragma: cache"); header("Cache-Control: max-age=$cache_length, s-maxage=$cache_length"); header("User-Cache-Control: max-age=$cache_length"); }
  • 13.
  • 14.
    th 5 Circle –All in the bag ● Ensure cache by delete cookies on unwanted items (images, css, js) sub vcl_recv { […] if (req.url ~ ".(png|gif|jpg)$") { remove req.http.Cookie; } [...]} ● Varnish doesn't cache anything with a cookie!
  • 15.
  • 16.
    th 6 Circle –Watch your steps ● Install GeoIp ● Get GeoipUsingInlineC from: https://www.varnish-cache.org/trac/wiki/GeoipUsingInlineC ● Compile and install it (findable by your OS)
  • 17.
    th 6 - Geoip.vcl C{ #include<dlfcn.h> #include <stdlib.h> #include <stdio.h> static const char* (*get_country_code)(char* ip) = NULL; __attribute__((constructor)) void load_module() { const char* symbol_name = "get_country_code"; const char* plugin_name = "/usr/lib/geoip_plugin.so"; void* handle = NULL; handle = dlopen( plugin_name, RTLD_NOW ); if (handle != NULL) { get_country_code = dlsym( handle, symbol_name ); if (get_country_code == NULL) fprintf( stderr, "nError: Could not load GeoIP plugin:n%snn", dlerror() ); else printf( "GeoIP plugin loaded successfully.n"); } else fprintf( stderr, "nError: Could not load GeoIP plugin:n%snn", dlerror() ); } }C
  • 18.
    th 6 - Addit to default.vcl sub vcl_recv { […] C{ VRT_SetHdr(sp, HDR_REQ, "017X-Country-Code:", (*get_country_code)( VRT_IP_string(sp, VRT_r_client_ip(sp)) ), vrt_magic_string_end); }C […] }
  • 19.
  • 20.
    th 7 Circle –Know your enemies ● Device Detection ● Download the devicedetection.vcl from: http://github.com/varnish/varnish-devicedetect/ ● Add it to default.vcl sub vcl_recv { […] call detectdevice; […] }
  • 21.
  • 22.
    th 8 Circle –Tongue in cheek ● Accepted language ● Download and create the accept-language.vcl from: http://github.com/cosimo/varnish-accept-language ● Include the generated file and call it in default.vcl sub vcl_recv { […] C{ vcl_rewrite_accept_language(sp); }C […] }
  • 23.
  • 24.
    th 9 Circle –Divide and conquer ● To activate add in your default.vcl sub vcl_fetch { [...] if( beresp.http.esi-enabled == "1") { set beresp.do_esi = true; /* Do ESI processing */ set beresp.ttl = 120s; /* Sets the TTL on the HTML above */ unset beresp.http.esi-enabled; } [...]
  • 25.
    th 9 - Check Remember,ESI has issues with non-xml like files. So, the file where you place the <esi: instructions have to look like a xml file.
  • 26.
    More to come...if there was time... ● Redirects ● ESI – Sessions / Users ● Purge ● SSL / SPDY / Compression ● Load Balancing ● Webserver (O.o really?)
  • 27.
    Varnish Resources ● The Book https://www.varnish-software.com/static/book/ ● CacheTutorial http://www.mnot.net/cache_docs/ ● Slides (slides used on the talk) Slides will be made available later on slideshare ● Sourcecode (configs, test scripts, etc) GitHub: https://github.com/lferro9000/varnish-talk
  • 28.
    Thank you forlistening and... ● ● ● … Specially to varnish-software and everyone that somehow contributed directly to enrich varnish. Always check varnish-software website in case of doubt, there is an huge amount of little tricks there. Remember: using varnish, is good for the environment (less power = win)!
  • 29.
    Credits and Thanks ● BotticelliChart of Dante's Hell http://www.flickr.com/photos/distan/3708997567/ by Dina Dubrovskaya (Real Distan) ● The great people that made Varnish possible ● PHP London for accepting this talk