From LXC to docker-machine and cloudery

Attention: this post provides a very quick and simplistic (but functional) vision of the promised title.

 

In the beginning

Linux is a fantastic OS, it has more than we imagine and it still manages to get better. There is a feature called cgroups:

which provides a mechanism for easily managing and monitoring system resources, by partitioning things like cpu time, system memory, disk and network bandwidth, into groups, then assigning tasks to those groups

Let’s say we created a cgroup with: 50% of cpu, 20% memory, 2% of disk and a virtual network with 100% of bandwidth, now we can run our application under that cgroups restrictions.

Another cool feature of Linux is LXC (linux-containers):

which combines kernel’s cgroups and support for isolated namespaces to provide an isolated environment for applications

Now we’re able to provide a Linux machine capable of running multiple applications that run in isolation (like if there was an isolated OS for each application). This sounds like something we achieved with virtualization (app-level, os-level, cpu-level and so on) but faster and cheaper and without the overhead of running multiple kernels.

31-containers-vs-traditional-virtualization

Docker

Docker is:

an open-source project that automates the deployment of applications inside software containers, by providing an additional layer of abstraction and automation of operating-system-level virtualization on Linux. This is what Docker is but remember, it is not perfect.

The highlighted part is very interesting, docker will provide you a layer of abstraction that allows you to create and deploy your application within a container (an isolated, resource managed place to run processes) in a standardized way.

Docker machine, compose and so on

Life almost always get easier with abstractions, we (developers) don’t worry about how disks works (drivers) or even how a package left your pc and hit another one (we should know how this works :P). Our productivity had increased a lot since we relied on these abstractions.

And this is the same for the docker ecosystem, as we start to use it more often. We create best practices, solve issues with workarounds and etc, some of these will become part of the docker solution.

  • docker-machine: An application needs a machine to run regardless if it’s local, physical, virtual or in the cloud.
  • docker-compose: An application needs a way to declare its dependencies, either packages or distinct services like datastore.

Step 0: get ready

  1. If you’re on MacOS/Windows you’ll need to install VirtualBox or VMWare
  2. If you’re on MacOS/Windows install docker toolbox otherwise apt-get them all

Step 1: create the app

Let’s say we’ll create a rails 4 application with mongo.

Step 2: declare the app and its dependencies

We declare our dependencies by using two files: docker-compose.yaml and Dockerfile. In the Dockerfile we’ll describe how our machine should be (aka: all need packages and stuffs).

Then we can move to its broad services dependencies, like database or even web server. We’ll use mongo as datastore and nginx as the web server.

Step 3: deploy it locally

We need to create a machine for it and then we need to run it.

Step 4: deploy in the cloud

The same way we created a machine to run our app locally ,we can create any number of machines to run this application, even in cloud environment such as digitalocean, aws, azure, google and etc.

That’s it :) for a more explained rails app docker workflow read this great post or yet a fresh new example of docker-compose.yaml.

// TODO: some things

Let’s suppose we just created a staging environment and another developer come to help us, it seems that there is no an official way to share our created machine (amazon, google app engine, azure, digital ocean…) with team members. There are some workarounds but it’ll be nice to see this becoming a feature.

Troubleshooting

  • Useful commands to troubleshooting, exploration and debug:
    • To enter on a machine: $ docker-machine ssh staging (either local or cloud)
    • To enter on a container: $ docker-compose run db bash (either local or cloud)
    • To list files within a container: $ docker-compose run db ls -lah data/db
    • To edit/add/remove data on mongo: $ mongo –host DOCKER_IP
  • If you face any error like E: Failed to fetch … during the docker-compose build try it again
  • If you face any error like “Error creating machine: Error running provisioning: Unable to verify the Docker daemon is listening: Maximum number of retries (10) exceeded” during any deployment, try to download docker-toolbox again and install it.

Google is your friend.

3 tips to make you a better developer

four20puzzle

introduction

I’m sorry for the clickbait headline, I didn’t have a better idea/name for it.

We (developers) occasionally produce lazy/messy code and from time to time we need to remember the most important rule: “We do code to solve problems but also for human being be able: to use, to maintain and to evolute”. 

TLDR; (a unit can be a: function, var, method, class, parameter and etc)

  1. Naming your units with care and meaning;
  2. Try to see your code as a series of transformation;
  3. When possible make yours units generic.

Keep in mind that these tips are just my opinions and at the best they were based on: excellent books (Refactoring, DDD, Clean Coder and etc ), articles & blog posts,  excellent people I’ve worked/paired with,  presentations,  tweets and experiences.

naming is hard

Name your units with care and meaning. Your code should be easy to understand.

Although naming things is really hard, it is also extremely important. Let’s a see a snippet of code:

Let’s discuss about this code above:

  • the function topComments receives an id but is it the id from the comment, user, article? Let’s say it’s form the user, therefore userId should vanish this doubt.
  • the name of the function is topComments but it looks like it’s getting the top 10 latest comments only thus we could call it top10LatestCommentsFrom.
  • the ajax function accept two callbacks one in case of success (succCB) and otherwise an error (errCB), I believe we can call them: onSuccess and onError for better understanding.
  • all the arguments are using short names and we can have less confusing names just by using the entire name.
  • you got the ideia, naming things to let the code clear!

Although we still have so many problems in this code, now it’s easier to understand and we only named things properly.

For sure there are some cases when short names are just okay, for example: when you’re developing an emulator or virtual machine you often use short names like sp (stack pointer) and pc (program counter) or even doing a very generic unit.

filter -> union -> compact -> kick

Try to see and fit your code as transformations, one after another.

Some say that in computer science almost all the problems can be reduced to only two major problems: sort and count things (plus doing these in a distributed environment), anyway the point is: we usually are coding to make transformation over data.

For instance our function top10LatestCommentsFrom could be summarized in these steps:

  1. fetch comments (all)
  2. sort them (by date)
  3. filter them (only top)
  4. select the first 10

Which are just transformations over an initial list, we can make our function top10LatestCommentsFrom much better with that mindset.

 

By the way this could lead you to easily understand the new kid on the block sometimes referred as Functional Reactive Programming.

<be generic>

Work to make your units generic.

Let’s imagine you are in an interview process and your first task is to code a function which prints the numbers 1, 2 and 3 concatenated with “Hello, “. It should print: “Hello, 1” and then “Hello, 2″…

Now they ask you to print also the letters: “D”, “K” and “C”.

It was the first step toward the “generic”, now the interviewers say you have also to print a list of person’s name but now it’ll be a list of objects [{name: “person”},…].

Things start to get specific again and the interviewers want to test you. They ask you to print a list of car’s brand [{brand: “Ferrari”}, ..] plus a list of game consoles with their architecture [{name: “PS4”, arch: “x86-64”}, …]

Yikes, I suppose you’re not proud of that code and probably your interviewers will be little concerned about your skills with development, let’s list some of the problems with this approach.

  • Naming (we’re calling a person of an item)
  • High coupling (the function print knows too much about each printable)
  • Lots of (inner) conditionals :( it’s really hard to read/maintain/evolute this code

What we can do?! Well, it seems that all we need to do is to iterate through an array and prints an item but each item will require a different way of printing.

 

I said naming is important but when you make something very generic you should also make the abstract names not tied to any concrete concept. In fact, in Haskell (let’s pretend I know Haskell) when a concrete type of something may vary we use single letters to take their place.

Bonus round

  1. Make your units of execution to perform a single task.
  2. Use dispatch/pattern matching/protocol something instead of conditionals.
  3. Enforce DRY as much as you can.

presentation – Live Video Platform for FIFA World Cup


In this talk, we will describe globo.com’s live video stream architecture, which was used to broadcast events such as the FIFA World Cup (with peak of 500K concurrent users), Brazilian election debates (27 simultaneous streams) and BBB (10 cameras streaming 24/7 for 3 months) .

NGINX is one of the main components of our platform, as we use it for content distribution, caching, authentication, and dynamic content. Besides our architecture, we will also discuss the Nginx and Operational System tuning that was required for a 19Gbps throughput in each node, the open source Cassandra driver for Nginx that we developed, and our recent efforts to migrate to nginx-rtmp.

How To Optimize Nginx Configuration for HTTP/2 TLS (SSL)

TLDR;

http/2 over tls with nginx is already a reality, how can we achieve the best performance of it? check the example configuration.

Introduction

We all know that http/2 is right here and although it doesn’t impose the TLS usage, the major browsers already took their side (a.k.a only supporting http/2 over TLS).

The support for http/2 was released with nginx 1.9.5 (except for “Server Push”). But isn’t HTTPS a lot slower than good old HTTP? Well, this is not easy to answer but we can fine tune nginx to do much better than the default configuration.

I really believe that the biggest fight is against latency not CPU load, the tips you’ll see here are mostly about reducing RTT in order to decrease latency.

tls ssl does not need cpu

Before we move on to the practical tips, let’s revise the simple tasks you must to do first:

  1. upgrade to the latest kernel (3.7+)
  2. upgrade to the latest openssl (1.0.1j)
  3. upgrade to the latest nginx (1.9.5)

These tips above will get you a lot of improvements but let’s go to the optimization tips:

TLS session resumption

What?

When you want to use HTTPS, your browser needs to negotiate the session (certificate, cipher, hash algorithm, tls version, key …), in a very simplistic way it does follow the steps:

  1. Establish a TCP connection (SYN, SYN/ACK, ACK)
  2. Negotiate and establish the TLS session

When you leave the site and come back later, the browser will need to renegotiate the session. TLS session resumption is the technique to partially skip this negotiation by persisting the session for later usage.

The left graph represents an over simplified version of a full TLS handshake (skipping TCP handshake) and on the right side you can see how TLS resumption works, the point is to skip RTT.

tls negotiation  tls negotiation resumption

Why?

If we skip part of the session negotiation we’ll delivery fast content.

How?

We do have two ways of solving this issue: saving the session (TLS) on server (session cache) or preferable on client (session ticket).

session cache

ssl_session_cache   shared:SSL:10m;
ssl_session_timeout 1h;

In this case when client tries to reconnect, the server will try to recovery past persisted session skipping partially the negotiation. With this shared session (of 10m), nginx will be able to handle 10 x 4000 sessions and the sessions will be valid for 1 hour.

However, there are problems with this approach:

  1. sessions are stored on the server;
  2. the “shared session” is saved in each server, so multiple nginx’s will not share the same session;

For the second problem, the great project openresty is about to release a new feature (ssl_session_store_by_lua) which will enable us to save these sessions in a “central” repository (like redis).

session ticket

# $> openssl rand 48 > file.key
ssl_session_tickets on; 
ssl_session_ticket_key file.key;

In this case, the server will create a ticket and send it to the client, when the client tries to connect again it’ll use the ticket and the server will just resume the session.

Nginx comes with session tickets enabled by default but if you will deploy your application in more than one box (bare metal, cloud, virtual machines, containers …) you’ll also need to specify the same key (used to create the tickets), you should rotate this key often.

Although this approach is much better than session cache, not all browsers support this so you might need to offer both solutions.

TLS false start

What?

How about to have the same benefit (skipping RTT) as in TLS resumption but when the browser first negotiate with the server?! This is possible by using and enforcing the forward secrecy.

Instead of waiting the last handshake step from server, the browser will already send the data (request) and the server will reply with the data (response). This technique is known as TLS false start.

tls negotiation  tls false start

Why?

Less RTT means faster site/video/image/data to final users.

How?

This is possible because we can extend the tls protocol by instrument it with specific ciphers.

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';

OCSP stapling and certificate chain

What?

Creating trust is a hard task to do and part of the responsibilities of TLS is to enforce it. In order to establish this trust the browser (or you OS) needs to at least have one point to trust.

In a very simplistic way, your browser believes that http://www.example.com is which it claims to be based on a chain of trust, it checks by:

  1. looking at its certificate and then checking if its signature is valid (checking all the certificates until the ROOT)
  2. looking if the certificate is not revoked by either searching it in CRL (certificate revocation list) or issuing a new request OCSP (Online Certificate Status Protocol)

tls chain

Both steps might force your browser to do more RTT, if your server doesn’t provide the intermediate certificates the browser will need to download them and it might even request an OSCP (requiring more RTT: DNS, TCP_HANDSHAKE, TLS_HANDSHAKE).

Why?

Less RTT means faster site/video/image/data to final users 2.

How?

You can concatenate your certificate with its chain (except ROOT, it’s not necessary, in fact the browser won’t trust your ROOT) then avoiding extra RTT to download the missing certificates.

$ cat mysite.cert ca1.cert > full.cert
...
ssl_certificate /path/to/full.cert

You can set up nginx to avoid OCSP by stapling (this “staple” is digitally signed which makes possible for your browser to check its authenticity) the OCSP response on your server then you will avoid extra RTT for OCSP.

ssl_stapling on; 
ssl_stapling_verify on; 
ssl_trusted_certificate /path/to/cas.pem

TLS record size optimization

What?

Although the maximum size of a TCP packet is 64K, a new TCP connection starts with much less than this maximum.

And each TLS record can hold at maximum 16K (which is the default size for nginx), summing up this size plus the headers of tcp and ip the server might need to make 2 RTT to serve the first bytes. And that’s not cool.

TCP is great but it has limitations, it is not ideal for all kinds of applications and there is even “quic” efforts to make web faster with experimentations using UDP instead of TCP.

Since we’ve reached our current speed limits, light speed, (who knows what “quantum” can do) we’re moving to avoid extra RTT.

*you can’t use QUIC on nginx yet.

Why?

Less RTT means faster site/video/image/data to final users. :) Again!!!

How?

There is a tradeoff here, you can either chose throughput (TLS record size to max) or latency (a small record size). It would be great if nginx could offer an adaptive option, starting small (4K, to speed up the first bytes) and after 1 minute or 1MB it increases for 16K.

ssl_buffer_size 16k; #for throughput, video applications
#ssl_buffer_size 4k; for quick first byte delivery

HSTS (HTTP Strict Transport Security)

What?

HSTS “converts” your site to a strict HTTPS-only, it eliminates unnecessary HTTP-to-HTTPS redirects by shifting this responsibility to the client, most of the browsers support it. Even if you forgot to change http for https the browser will do that for you.

Why?

Redirects means more RTT, yeah I know it’s getting repetitive but it’s to reduce latency.

How?

A simple http header to instruct the browser.

add_header Strict-Transport-Security "max-age=31536000; includeSubdomains"; 
#'max-age' values specifies how long browser should follow this rule.

Summary

It’s all about making web faster avoiding RTT (later I’ll post tips specific to http/2), so here’s a check list:

  • Upgrade to the latest: kernel, openssl and nginx.
  • Use TLS resumption and TLS false start
  • `cat` your certificate with the intermediates
  • Think about the best size (hard) for you TLS record
  • Enforce HSTS ;)

Here’s the full config example:

# command to generate dhparams.pen
# openssl dhparam -out /etc/nginx/conf.d/dhparams.pem 2048

limit_conn_zone $binary_remote_addr zone=conn_limit_per_ip:10m;
limit_req_zone $binary_remote_addr zone=req_limit_per_ip:10m rate=5r/s;
limit_req_status 444;
limit_conn_status 503;

proxy_cache_path /var/lib/nginx/proxy levels=1:2 keys_zone=backcache:8m max_size=50m;
proxy_cache_key "$scheme$request_method$host$request_uri$is_args$args";
proxy_cache_valid 404 1m;

upstream app_server {
  server unix:/tmp/unicorn.myserver.sock fail_timeout=0;
}

server {
  listen 80;
  server_name *.example.com;
  limit_conn conn_limit_per_ip 10;
  limit_req zone=req_limit_per_ip burst=10 nodelay;
  return 301 https://$host$request_uri$is_args$args;
}

server {
  listen 443;
  server_name _;

  limit_conn conn_limit_per_ip 10;
  limit_req zone=req_limit_per_ip burst=10 nodelay;

  ssl on;

  ssl_stapling on;
  ssl_stapling_verify on;
  ssl_trusted_certificate /etc/nginx/conf.d/ca.pem;

  ssl_certificate /etc/nginx/conf.d/ssl-unified.crt;
  ssl_certificate_key /etc/nginx/conf.d/private.key;
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
  ssl_dhparam /etc/nginx/conf.d/dhparams.pem;
  ssl_prefer_server_ciphers on;
  ssl_session_cache shared:SSL:10m;
  ssl_session_timeout 10m;

  root /home/deployer/apps/example.com/current/public;

  gzip_static on;
  gzip_http_version   1.1;
  gzip_proxied        expired no-cache no-store private auth;
  gzip_disable        "MSIE [1-6]\.";
  gzip_vary           on;

  client_body_buffer_size 8K;
  client_max_body_size 20m;
  client_body_timeout 10s;
  client_header_buffer_size 1k;
  large_client_header_buffers 2 16k;
  client_header_timeout 5s;

  add_header Strict-Transport-Security "max-age=31536000; includeSubdomains"; 

  keepalive_timeout 40;

  location ~ \.(aspx|php|jsp|cgi)$ {
    return 404;
  }

  location ~* ^/assets/ {
    root /home/deployer/apps/example.com/current/public;
    # Per RFC2616 - 1 year maximum expiry
    # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
    expires 1y;
    add_header Cache-Control public;
    access_log  off;
    log_not_found off;

    # Some browsers still send conditional-GET requests if there's a
    # Last-Modified header or an ETag header even if they haven't
    # reached the expiry date sent in the Expires header.
    add_header Last-Modified "";
    add_header ETag "";
    break;
  }

  try_files $uri $uri/index.html $uri.html @app;

  location @app {
    proxy_set_header X-Url-Scheme $scheme;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    # enable this if you forward HTTPS traffic to unicorn,
    # this helps Rack set the proper URL scheme for doing redirects:
    proxy_set_header X-Forwarded-For-Forwarded-Proto $https;

    proxy_set_header Host $host;
    proxy_redirect off;
    proxy_pass http://app_server;
  }

  error_page 500 502 503 504 /500.html;
  location = /500.html {
    root /home/deployer/apps/example.com/current/public;
  }
}

With this configuration I was able to get an A+ at SSLlabs, this is a useful tool where you can check what you need to do to make your SSL site better, it gives you specific tips (apache, nginx, IIS).

tls a+
I strongly recommend you:

Great sources of info

Standing on the shoulders of giants

And for sure HN and SO.