Speak Softly And Carry A Big Stack


How to leverage open source projects (nginx, haproxy, redis, varnish, etc.) to turbocharge your php application stack

Austin Morris / @austinsmorris
Slides: https://austinsmorris.github.io/speak-softly-and-carry-a-big-stack

First the bad news...

No PHP

No DB

(In the traditional sense...)

Now the good news...

High Performance is not hard

High Performance = High Traffic

High Performance = High Availability

php

Problem: My PHP is too slow!

Use the right version!

Use the newest version!

(Or at least PHP 5.5)

Try HHVM?

  • HHVM is an open-source virtual machine designed for executing programs written in Hack and PHP.
  • HHVM uses a just-in-time (JIT) compilation approach to achieve superior performance while maintaining the development flexibility that PHP provides.
  • Wicked fast!

PHP-NG (Next Generation)


  • Based on a refactored Zend Engine, PHP-NG is already merged into master and will be the basis for the major PHP release.
  • PHP-NG is already twice as fast as PHP 5.6.

Use bytecode cache!

  • OPcache for >= PHP 5.5
  • APC for <= PHP 5.4

php.ini settings:


        opcache.enable=1
      
  • Turn it on.

          opcache.enable_cli=1
        
  • If you want it on the CLI.

        opcache.memory_consumption=128
      
  • How much memory opcache will use (MB).
  • Check your usage with opcache_get_status().

        opcache.interned_strings_buffer=8
      
  • How much memory will be used to share immutable strings between processes (MB).

        opcache.max_accelerated_files=4000
      
  • How many files (scripts) are cached.
  • How big is your code base?

        opcache.fast_shutdown=1
      
  • Turn it on to speed up deconstruction (freeing memory) at the end of each request.

        opcache.revalidate_freq=60
      
  • How often code files are checked for updates (s).
  • Ignore if you use...

        opcache.validate_timestamps=0
      
  • Never check for updates.
  • Do this if your deployment (cap?) supports it.
  • Much safer then mixing old and new code for 60s.

Use php-fpm with...

nginx

Problem: My web server is too slow!

nginx + php-fpm = rad!

  • apache + mod_php is so 2000-late.
  • nginx is fast and efficient.
  • php-fpm can be scaled to maximize hardware capability.

nginx.conf


        user austin admin;
        worker_processes 1;

        events {
          multi_accept on;
          worker_connections 1024;
        }
      

nginx.conf (cont.)


        http {
          sendfile on;
          tcp_nopush on;
          keepalive_timeout 65;
          server_tokens off;
          include mime.types;
          default_type application/octet-stream;
          access_log /usr/local/var/log/nginx/access.log;
          error_log /usr/local/var/log/nginx/error.log;

          upstream php-fpm-socket {
            server unix:/tmp/php-fpm.sock;
          }

          include /usr/local/etc/nginx/sites-enabled/*;
        }
      

sites-enabled/mysite


        server {
          listen 80;
          server_name mysite.dev;
          root /path/to/public/dir;
          index index.php;

          location / {
            try_files $uri $uri/ /index.php?$query_string;
          }

          location ~ \.php$ {
            fastcgi_pass php-fpm-socket;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include fastcgi_params;
          }
        }
      

php-fpm.conf


        [dev]
        listen = /tmp/php-fpm.sock
        pm = dynamic
        pm.max_children = 100
        pm.start_servers = 10
        pm.min_spare_servers = 5
        pm.max_spare_servers = 15
        pm.max_requests = 1000
        pm.status_path = /php_status
      

haproxy

Problem: I only have one web server!

haproxy is sweet!

  • Very fast and reliable.
  • Modest hardware can handle a large load.
  • Capable of layer 4 and layer 7 proxying.
  • De-facto standard open source load balancer.

haprox.cfg


        global
          maxconn 50000
          user haproxy
          group haproxy
          stats socket /tmp/haproxy
          node lb1
          nbproc 1
          daemon

        defaults
          log global
          retries 3
          timeout connect 5000ms
          timeout client 5000ms
          timeout server 5000ms
          maxconn 50000
      

haprox.cfg (cont.)


        frontend tcp_proxy
          bind *:80
          mode tcp
          default_backend my-backend

        backend my-backend
          mode tcp
          balance roundrobin
          # option httpchk HEAD / HTTP/1.1\r\nHost:\ example.com
          server my-server-1 10.0.2.2 check port 80 inter 1000
          server my-server-2 10.0.2.3 check port 80 inter 1000
      

haprox.cfg (cont.)


        listen stats *:1936
          mode http
          stats enable
          stats uri /
          stats hide-version
          stats auth Username:Password
      

More tips...

  • Use keepalived to enable backup LBs.
  • Use DNS to point to multiple LBs.
  • Tune your OS (Linux):
    • More connections and ports.
    • Tweak TCP time wait.
    • Remove soft and hard file limits.

redis

Problem: I have more than one web server!

Redis is awesome!

  • In-memory key value store
  • Persist-to-disk
  • Easy setup and configuration
  • Excellent documentation

Share your sessions

  • Sticky sessions cause hot spots
  • Use the phpredis extension with igbinary

        extension=igbinary.so
        extension=redis.so
      

redis.conf



(demo for full effect)

php.ini


        session.save_handler = redis
        session.save_path = "tcp://192.168.0.100:6379"
      

But wait, there's more!

Userland cache

  • phpredis provides native access
  • Doctrine driver (using phpredis) for:
    • Metadata cache
    • Query cache
    • Results cache

Redis to the Resque!

  • Redis is the engine driving resque
  • Use a job queue for anything that is not part of the response
  • php-resque lets your write jobs in php

varnish

Problem: I want more!

Web Application Accelerator

a.k.a Caching HTTP Reverse Proxy

  1. Send an HTTP Requst.
  2. Varnish will fetch and return a response.
  3. Resend the HTTP Request
  4. Varnish will return a cached response.

Pros

  • In-memory (mostly) HTTP cache.
  • Wicked fast static content.
  • Can act as a reverse proxy.
  • Varnish Configuration Language

Cons

  • Must read HTTP (no SSL).
  • No persistence (with experimentation).
  • Tricky debugging.
  • Varnish Configuration Language

/etc/default/varnish


        # Should we start varnishd at boot?  Set to "no" to disable.
        START=yes

        # Maximum number of open files (for ulimit -n)
        NFILES=131072

        # Maximum locked memory size (for ulimit -l)
        MEMLOCK=82000

        DAEMON_OPTS="-a :80 \
          -T localhost:6082 \
          -S /etc/varnish/secret \
          -f /etc/varnish/my.vcl \
          -s malloc,256m"
      

        -s malloc,256m
      
  • Use malloc if all of your cache will fit in memory.

        -s file,/tmp/varnish,500G
      
  • Use file if it won't.

/etc/varnish/my.vcl


        vcl 4.0;

        backend server1 {
          .host = "192.168.0.100";
          .port = "80";
        }

        backend server1 {
          .host = "192.168.0.200";
          .port = "80";
        }

        acl purge {
          "192.168.0.200";
          "192.168.0.100";
        }

        sub vcl_init {
          new backends = directors.round_robin();
          backends.add_backend(server1);
          backends.add_backend(server2);
        }
      

/etc/varnish/my.vcl (cont.)


        sub vcl_recv {

          if (req.method == "PURGE") {
            if (!client.ip ~ purge) {
              return (synth(405, "Not allowed"));
            }

            return (purge);
          }

          if (req.http.Cache-Control ~ "no-cache") {
            return (pass);
          }

          return (hash);
        }
      

/etc/varnish/my.vcl (cont.)


        sub vcl_backend_response {
          set beresp.ttl = 120s
        }
      

Useful Tools

  • varnish cli
  • varnishadm
  • varnishhist
  • varnishlog
  • varnishncsa
  • varnishreplay
  • varnishsizes
  • varnishstat
  • varnishtest
  • varnishtop

tl;dr

  • Breakdown your architecture into components.
  • Pick components that are really good at what they do.
  • Step 3: Profit!

Questions?