Tutorial: install Varnish + VMODs from sources & build a Paywall

Varnish Bunny

Varnish is well known as a highly effective proxy cache which is able to deal with all the HTTP request/response parts (URLs, headers, cookies, …) using VCL (Varnish Configuration Language). Moreover additional functionalities can also be embedded inside Varnish by using VMODs. This extends the VCL logic, which is kept simple for performance purposes, and so is quickly limited when we want to add some more complex logic in the code (loops, …). I have recently worked on a Paywall, and using Varnish as the server-side part of the Paywall seemed to me a good idea.

I’ve split this post into 2 main parts:

First, I will explain how to install Varnish 3.0.6 (for client’s compatibility reasons) from sources with 3 associated VMODs :

  • libvmod-curl (accesses HTTP and other resources from within VCL using libcurl),
  • libvmod-cookie (handles the content of the Cookie header without complex use of regular expressions)
  • and libvmod-redis (allows synchronous access to Redis from VCL).

I used an Ubuntu Trusty as the OS.

Second, I will explain the way I used all these tools to build a server-side Paywall, without implementing all the real rules obviously. Take this post more like an overall design and a proof of concept. The VCL configuration contains 2 simple rules based on an authorization service and a counter with a threshold. I will provide you throughout this post with a script I wrote to help you have a full environment ready for testing. This script configures Varnish with the rules I will explain below, installs an Apache server + mod PHP with some PHP scripts (providing content and authorization logic) and installs a Redis server. After executing this script on an Ubuntu Trusty (I used Vagrant + VirtualBox as the provider) you will be ready to play the way you want with the VCL and to bring your own logic to the proxy cache.

Varnish Paywall

You may know about the Varnish Paywall product from the .com website. After digging around a bit, I realized that in order to use this module we need to buy a « not so cheap » license.  This license allows us to use a lot of other modules we don’t need. I looked at the way it works, and eventually concluded that it won’t actually do anything more than I can do by myself using VCL and free (check each VMOD license in light of what you are building) modules, as described in this post. So I decided to build a Varnish Paywall from scratch, and I must admit that it was not so difficult in the end.

I built a Varnish 3.0.6 from source files to get headers which are required to build VMODs. Obviously, remember this is a proof of concept: in a production environment you will probably be more careful about not having dev packages on your servers.

Let It Go! 🙂

Take a careful look at the comments in the code snippets. I’ve written a lot of tips in them for you:

  • I’ve noted all the problems (and the solutions :o)) I encountered to help you deal with them, in case you’ve tried an installation by yourself,
  • I’ve explained in the VCL file snippet the configuration I’ve set up and the features it provides to the Paywall and the way you can improve these features.

On a pristine Ubuntu Trusty VM, you can execute all these snippets one after the other or copy/paste them into a single file you’ve made executable. Be sure to be in the home directory of your current user which must not be root but has access to root access using sudo without a password (vagrant in my case).

I started the installation script with a standard init:

#!/bin/bash

# Author:  Frederic FAURE (frederic.faure@ysance.com)
# Company: YSANCE (http://www.ysance.com/)
# Date:    2015-02-25
# Version: 1.0

INSTALL_LOGFILE=~/install.log
echo " " >> $INSTALL_LOGFILE
echo "--------- `date` ----------" >> $INSTALL_LOGFILE
cd
sudo apt-get update

Then I installed Varnish 3.0.6 from sources with all the needed dependencies and tested it to make sure that it works (the result of the test is in the install.log file):

################################
#### Install Varnish
wget http://repo.varnish-cache.org/source/varnish-3.0.6.tar.gz
sudo apt-get install -y autotools-dev automake1.9 libtool autoconf libncurses-dev xsltproc groff-base libpcre3-dev pkg-config libjemalloc-dev libedit-dev
gunzip varnish-3.0.6.tar.gz
tar -xvf varnish-3.0.6.tar
cd varnish-3.0.6
sh autogen.sh
sh configure
make
make check
sudo make install
cd
if [ -f "/usr/local/sbin/varnishd" ]; then echo "varnishd installed in /usr/local/sbin" >> $INSTALL_LOGFILE; else echo "varnishd not installed" >> $INSTALL_LOGFILE; exit -1; fi
if [ -f "/usr/local/etc/varnish/default.vcl" ]; then echo "varnish default configuration installed in /usr/local/etc/varnish" >> $INSTALL_LOGFILE; else echo "varnish default configuration not installed" >> $INSTALL_LOGFILE; exit -1; fi
echo "Varnish version: `varnishd -V 2>&1 | grep varnishd`" >> $INSTALL_LOGFILE

Next I installed Apache and PHP5 as the backend, to serve the content and provide access to a (very simple) service that holds the authorization logic:

################################
#### Install Apache and Php5
sudo apt-get install -y php5
echo "Apache version: `apachectl -v 2>&1 | grep version | cut -d":" -f2`" >> $INSTALL_LOGFILE
echo "PHP version: `php -v 2>&1 | grep "(cli)" | cut -d" " -f2`" >> $INSTALL_LOGFILE

After that I installed Redis to store the user status or session information on server-side. My purpose was to enhance VCL logic capabilities and cache authorization information got from the authorization service to allow faster execution (cache logic is not implemented in the proof of concept):

################################
#### Install Redis
sudo apt-get install -y redis-server
echo "Redis version: `redis-server -v 2>&1 | cut -d" " -f3 | cut -d"=" -f2`" >> $INSTALL_LOGFILE

Following that, I installed the Varnish modules. I began with the VMOD cURL to allow calls to HTTP services to externalize some logic that VCL cannot hold, either because of the VCL limitations (no loops, …), or because data (needed to take decisions) are stored elsewhere:

################################
#### Install VMOD cURL
sudo apt-get install -y git
git clone https://github.com/varnish/libvmod-curl
# When "sh configure" => error: Package requirements (libcurl) were not met: No package 'libcurl' found
# Solution => install "libcurl4-openssl-dev" - development files and documentation for libcurl (OpenSSL flavour)
# When "make" => You need rst2man installed to make dist
# Solution => install "python-docutils" - text processing system for reStructuredText (implemented in Python 2)
sudo apt-get install -y curl libcurl4-openssl-dev python-docutils
cd libvmod-curl
git checkout 3.0
git pull origin 3.0
# When "sh autogen.sh" => required file `./ltmain.sh' not found, Can't open configure
# Reason => http://www.gnu.org/software/automake/manual/html_node/Error-required-file-ltmain_002esh-not-found.html
# Solution => run "sh autogen.sh" twice
sh autogen.sh
sh autogen.sh
sh configure VARNISHSRC=$HOME/varnish-3.0.6
make
# When "make check" => Message from VCC-compiler: Could not load module curl /src/.libs/libvmod_curl.so: cannot open shared object file: No such file or directory
# Reason => variable in code 'import curl from "${vmod_topbuild}/src/.libs/libvmod_curl.so";' is not well replaced (empty): 'import curl from "/src/.libs/libvmod_curl.so";'
# Solution => put the "make check" into comments
# make check
sudo make install
cd
if [ -f "/usr/local/lib/varnish/vmods/libvmod_curl.so" ]; then echo "libvmod_curl installed in /usr/local/lib/varnish/vmods" >> $INSTALL_LOGFILE; else echo "libvmod_curl not installed" >> $INSTALL_LOGFILE; exit -1; fi

Then I needed the VMOD Cookie to deal more efficiently with cookies. Varnish can natively deal with cookies but at the price of complex regexps and I didn’t want to loose the main goal of the VCL logic in verbose side-code:

################################
#### Install VMOD Cookie
git clone https://github.com/lkarsten/libvmod-cookie
cd libvmod-cookie
# When "make" => No rule to make target `@VMODTOOL@', needed by `vcc_if.c'.
# Reason => libvmod-cookie split into 2 branches as of Varnish 4 => branch 4.0 is now the default
# Solution => Get the right Git branch and not try to build module v4 on a Varnish v3 => "git checkout 3.0"
git checkout 3.0
git pull origin 3.0
# Same as VMOD cURL
sh autogen.sh
sh autogen.sh
sh configure VARNISHSRC=$HOME/varnish-3.0.6
make
# Same as VMOD cURL
# make check
sudo make install
cd
if [ -f "/usr/local/lib/varnish/vmods/libvmod_cookie.so" ]; then echo "libvmod_cookie installed in /usr/local/lib/varnish/vmods" >> $INSTALL_LOGFILE; else echo "libvmod_cookie not installed" >> $INSTALL_LOGFILE; exit -1; fi

Finally, in terms of modules, I needed to reach Redis to store browsing information (global or « by topic » view count, …) from users to take decisions or to cache some authorization data got from HTTP services to allow faster execution:

################################
#### Install VMOD Redis
git clone https://github.com/carlosabalde/libvmod-redis
# When "sh configure" => configure: error: libvmod-redis requires libhiredis.
# Solution => install "libhiredis0.10" - minimalistic C client library for Redis
#                     "libhiredis-dev" - minimalistic C client library for Redis (development files)
sudo apt-get install -y libhiredis0.10 libhiredis-dev
cd libvmod-redis
git checkout 3.0
git pull origin 3.0
# Same as VMOD cURL
sh autogen.sh
sh autogen.sh
sh configure VARNISHSRC=$HOME/varnish-3.0.6
make
# Same as VMOD cURL
# make check
sudo make install
cd
if [ -f "/usr/local/lib/varnish/vmods/libvmod_redis.so" ]; then echo "libvmod_redis installed in /usr/local/lib/varnish/vmods" >> $INSTALL_LOGFILE; else echo "libvmod_redis not installed" >> $INSTALL_LOGFILE; exit -1; fi

Next, I created the 2 PHP files. The first one generates a content (full or truncated) depending on the header X-View-Authorized in the request. It simulates the backend, probably a CMS managing your contents. The second file simulates an authorization service. It merely tests if the user in the POST request is « bob »! If yes, access is granted, if not… You are not bob: (hit_for_) « pass » on your way! ;o)

################################
#### Create PHP files

#### Content generator (full or truncated)
cat <<EOF > content.php
<?php
foreach (getallheaders() as $name => $value) {
    if ($name == "X-View-Authorized") {
        if ($value == "1") {
            echo "<p>I am a long<br/>long<br/>long<br/>long<br/>long<br/>long<br/>long<br/>long<br/>text !</p>";
        } else {
            echo "<p>I am a long<br/>long<br/>...</p>";
        }
    }
}
?>
EOF
sudo cp content.php /var/www/html/content.php

#### Authorize to access full content or not
cat <<EOF > authorize.php
<?php
if ($_POST["user"] == "bob") {
    echo "1";
} else {
    echo "0";
}
?>
EOF
sudo cp authorize.php /var/www/html/authorize.php

Now the main dish! I configured the VCL file which deals with HTTP requests, backend responses and cache responses. The VCL contains the logic of the Paywall. Have a close look at the comments: a lot of details are given in them.

################################
#### Create VCL file
cat <<EOF > my_project.vcl
backend default {
    .host = "127.0.0.1";
    .port = "80";
}

import std;
import curl;
import cookie;
import redis;

sub vcl_init {
    # init(STRING tag, STRING location, INT timeout, INT ttl, INT retries, BOOL shared_contexts, INT max_contexts)
    redis.init("main", "127.0.0.1:6379", 500, 0, 0, false, 1);
}

sub vcl_recv {
    # Normalize "Accept-Encoding" to reduce "Vary".
    # Do this only once per request.
    if (req.restarts == 0) {
        if (req.http.Accept-Encoding) {
            # Make sure Internet Explorer 6 doesn't need to deal with compression (it's notoriously bad at it).
            if (req.http.User-Agent ~ "MSIE 6") {
                unset req.http.Accept-Encoding;
            # No point in compressing these.
            } elsif (req.url ~ ".(jpg|png|gif|gz|tgz|bz2|tbz|mp3|ogg)$") {
                unset req.http.Accept-Encoding;
            # If "Accept-Encoding: gzip,deflate" or "Accept-Encoding: deflate,gzip": a preference for gzip,
            # therefore this case first.
            } elsif (req.http.Accept-Encoding ~ "gzip") {
                set req.http.Accept-Encoding = "gzip";
            # Add support for some ancient HTTP clients.
            } elsif (req.http.Accept-Encoding ~ "deflate") {
                set req.http.Accept-Encoding = "deflate";
            } else {
                unset req.http.Accept-Encoding;
            }
        }
    }

    # No access authorized at start, just in case to overwrite client's crafted headers in the request.
    set req.http.X-View-Authorized = 0;
    # Get the cookies from the request.
    cookie.parse(req.http.cookie);

    # Ask an external PHP code for the authorization logic.
    #
    # Obviously in real logic, you will have a token (instead of the username), generated by the external PHP
    # service (which handle the identification and authorization logic), either at log in time for authenticated
    # users or the first time the client with no "token" cookie try to view a content (anonymous client).
    # So this token may or may not be linked to a real user depending if the client is authenticated or not.
    # Once a client is identified with a token, the information "authenticated or not" should be stored in
    # another cookie to avoid asking about authorization for each call:
    #   - if authenticated, the rights could be stored in Redis for faster access:
    #       o try to get rights from Redis,
    #       o if not stored here, ask the external PHP service, and then store it into Redis.
    #   - if not authenticated, then no right, therefore no need to spend time asking the external service or Redis.
    curl.post("http://127.0.0.1:80/authorize.php", "user=" + cookie.get("mp_user"));
    # Set the answer in the request "X-View-Authorized" header
    set req.http.X-View-Authorized = curl.body();
    curl.free();

    # If still not authorized, because the client has no right (when authenticated) or because
    # he is anonymous (not authenticated), he has 10 free views
    if (std.integer(req.http.X-View-Authorized, 0) == 0) {
        # Increments the number stored at key by one. If the key does not exist, it is set to 0 before performing the operation.
        redis.command("INCR");
        redis.server("main");
        redis.push(cookie.get("mp_user")+":view_count");
        redis.execute();
        set req.http.X-View-Count = redis.get_integer_reply();
        # In this case, we count each content access, even if the same content is accessed each time.
        # In a real case, we probably want to leave access to 10 different contents, and moreover with an expiracy (for
        # exemple 10 free contents for a month). Easy with Redis! We can use Sets:
        #   - Store items with "SADD key member": SADD <user/token>:view_count <content_id>. The <content_id> can be sent
        #     in a request header.
        #   - At first test if the content has already been viewed: SISMEMBER <user/token>:view_count <content_id> (returns
        #     the value 1 if the element is a member of the set or 0 if the element is not a member of the set, or if key
        #     does not exist). If you get "1", the content has already been viewed: grant the right.
        #   - Else, test the Set length SCARD <user/token>:view_count (returns the set cardinality - number of elements -
        #     of the set stored at key or 0 if key does not exist). If less than 10: grant the right and set (SADD) the hit
        #     on the content. Moreover if 0 (the key did not exist): set the expiracy (one month from now) with
        #     EXPIRE <user/token>:view_count <seconds> (set a timeout on key: after the timeout has expired, the key will
        #     automatically be deleted).
        if (std.integer(req.http.X-View-Count, 0) < 11) {
            set req.http.X-View-Authorized = 1;
        }
    }
    # Force caching by deleting all cookies
    unset req.http.cookie;
}

sub vcl_fetch {
    # Vary the content on 2 axis: encoding and authorization. Thanks to the normalization of the "Accept-Encoding" header,
    # we only have a few different "versions" of the same cached object: "full and gziped", "full and deflated",
    # "truncated and gziped", ...
    set beresp.http.Vary = "Accept-Encoding, X-View-Authorized";
    # Set the cache TTL to 1 minute. If you modify the content returned (in "content.php" file), it will take up to 1 minute
    # for a client's call to get the change.
    set beresp.ttl = 60 s;
    # If the "X-View-Count" header is set in the request and the right to access the content has been granted (based on this
    # information), we send it back to the client to tell him how many free contents it remains.
    # Be careful: this information is cached with the page, so it is shared for all the client's request during 1 minute...
    # See "vcl_deliver" for the trick.
    # Tip: use "X-Varnish" response header to know if you got the page from cache or not (and check the behaviour you are
    # waiting for). For a cache hit, "X-Varnish" will contain both the ID of the current request and the ID of the request
    # which populated the cache. Therefore:
    #   - X-Varnish:752368328 752368327 => got the page from cache,
    #   - X-Varnish:752368328           => got the page from backend.
    if (req.http.X-View-Count && std.integer(req.http.X-View-Authorized, 0) == 1) {
        set beresp.http.X-View-Count = req.http.X-View-Count;
    }
}

sub vcl_deliver {
    # Unset this dynamic header and set it to the right value if needed before sending the cached page to the client.
    unset resp.http.X-View-Count;
    if (req.http.X-View-Count && std.integer(req.http.X-View-Authorized, 0) == 1) {
        set resp.http.X-View-Count = req.http.X-View-Count;
    }
}
EOF
sudo cp my_project.vcl /usr/local/etc/varnish/my_project.vcl

Final steps. I checked that the Apache and Redis services have started up properly to give a response to Varnish:

################################
#### Check Apache and Redis Servers are started
#### Should be because they are managed by services
if [ -z "`ps aux | grep "/usr/sbin/apache2" | grep -v grep`" ]; then echo "Apache Server NOT started" >> $INSTALL_LOGFILE; exit -1; else echo "Apache Server started" >> $INSTALL_LOGFILE; fi
if [ -z "`ps aux | grep "/usr/bin/redis-server" | grep -v grep`" ]; then echo "Redis Server NOT started" >> $INSTALL_LOGFILE; exit -1; else echo "Redis Server started" >> $INSTALL_LOGFILE; fi

And last of all, I started the Varnish server itself. Here you are:

################################
#### Start Varnish Server
sudo varnishd -f /usr/local/etc/varnish/my_project.vcl -s malloc,128M -T 127.0.0.1:2000 -a 0.0.0.0:8080
if [ -z "`ps aux | grep "varnishd" | grep -v grep`" ]; then echo "Varnish Server NOT started" >> $INSTALL_LOGFILE; exit -1; else echo "Varnish Server started" >> $INSTALL_LOGFILE; fi

You’re now able to use the installed components to run some tests:

  • Have a look at the install.log file at the same level as the install script: you have to find a « Varnish Server started » at the end.
  • Open a browser and set a cookie called mp_user (it is required) using a plugin like EditThisCookie (for Chrome). Set « bob » as the value if you want to always be authorized (or anything else if you don’t), set the IP of your VM as the domain, and set ‘/’ as the path.
  • Use the URL http://192.168.33.10:8080/content.php to access the content through Varnish (update the IP with the IP of your VM):
    • if you are bob, you always have access to the full content,
    • if you are another user, 10 accesses to the full content are granted to you. Please note that the number of already-viewed contents is in the response headers for a display if needed (for example a bar with « You have read 5 of 10 free articles this month. »), even if the content is cached. Switch the value in mp_user cookie from one to another and see what you get: you always get the right number of already-accessed contents, even if the content comes from the cache (60s TTL). When the threshold is exceeded, you get the truncated content.

As you can imagine, there are a few more « trickier » rules in the real Paywall configuration I set up: I can deal with all of them with this installation (plus just a little bit of cliend-side JavaScript and the libvmod-dns to double check the bots), but it is not the intention of this post to present them all and anyway the rules depend on the specific needs of the client.

I hope this tutorial will help you take your first steps into the world of Varnish and Paywalls!

Frédéric FAURE @Twitter @Ysance

Thanks to Online syntax highlighter like TextMate for the syntax highlighting.

Cinématique Paywall & Profiling de l’internaute

Halte Péage

Un Paywall est un système permettant de décider d’afficher ou non le contenu d’un site soumis à souscription : les exemples classiques sont les sites de presse, les distributeurs de vidéos à la demande, … L’objectif est de bloquer l’accès aux internautes sans les autorisations nécessaires et souhaitant accéder à du contenu payant ou bien d’offrir un peu de ce contenu pour servir de teasing puis d’afficher le paywall au moment opportun pour inciter à l’achat. Il faut ensuite proposer la ou les offres adaptées, fonction du contenu et de l’internaute. On peut avoir :

  • du paiement par accès à un contenu,
  • un accès illimité à l’ensemble du site après un paiement unique,
  • une location sur une durée,
  • la souscription à un thème/pack sur une période donnée,
  • ou bien encore un système de pass.

Concernant l’internaute, il y a 2 grands cas :

  • soit l’internaute est authentifié sur le site et on parle plutôt d’utilisateur et la décision d’affichage ou non du paywall est plus simple car on a à disposition un ensemble d’informations sur lui que l’on a associées à son identifiant,
  • soit il ne l’est pas et il faut réussir à identifier le visiteur en l’affectant à un ou plusieurs segments afin de le qualifier. N’oublions pas qu’un visiteur peut également être un utilisateur qui ne s’est pas connecté à son compte.

Le cas de l’internaute non authentifié est fréquent pour des sites de presse payants par exemple. Cela est moins le cas pour de la vidéo à la demande où il faut au préalable s’inscrire au service (ou juste après avoir visionné un trailer).

Plusieurs produits, une cinématique

J’ai regardé un certain nombre de solutions de Paywall du marché (mais je suis loin d’en avoir fait le tour) et je n’en ai pas trouvé qui inclut la partie en amont d’identification (notamment le profiling) de l’internaute : elles sont orientées sur l’aval de la cinématique à savoir l’affichage du paywall lui-même/déblocage du contenu et la gestion du catalogue des offres de souscription libérant le contenu.

A noter que ces produits sont plutôt orientés sur la délivrance de contenus de type vidéo (au niveau de l’imbrication du paywall dans le contenu et des offres proposées). Pour les sites de type presse, le choix est encore plus restreint et on se dirige facilement sur du sur-mesure.

En revanche, la cinématique de gestion du paywall est, elle, standard. Voici ci-dessous une description de cette cinématique présentant les 3 phases :

  1. identification de l’internaute (utilisateur ou visiteur) via authentification et/ou profiling,
  2. autorisation,
  3. affichage (+ recommandations).

Paywall - Cinématique : Identification / Autorisation / Affichage

Focus sur le Profiling

DMP Ysance

Le profiling, ou la capacité à rapprocher un internaute d’un ou plusieurs segments afin de lui proposer la « meilleure » expérience de navigation, peut s’appuyer sur :

  • des données de navigation « live », classiquement le referrer, des cookies, … présentes dans la page,
  • des données issues de produits externes type DMP (Data Management Platform), comme la Digital Data Factory, qui va collecter les données de navigation des internautes, les réconcilier et les mettre à disposition pour création de segments dans lesquels les internautes seront insérés afin de les qualifier.

La deuxième source de données permet (par rapport à la première) de capter et d’agréger toutes les données traduisant le comportement d’un visiteur/utilisateur sur différents canaux tels que le web (fixe/mobile) et les applications mobiles et sur différents domaines qui peuvent correspondre à plusieurs marques d’un même groupe. Le croisement d’informations issues de plusieurs domaines (ma-marque1.com, ma-marque2.com, …) étant possible suivant les limites d’usage de la donnée : par exemple dans le cadre de la Digital Data Factory, le partage des informations est limité aux domaines appartenant à un compte client donné (pas de rapprochement de la donnée inter-clients). Ces données permettent ainsi de qualifier des visiteurs « anonymes » via affectation à un ou plusieurs segments et donc de lui proposer une navigation sur-mesure. Il est également possible, comme précisé précédemment, d’utiliser ce même profiling sur des utilisateurs (authentifiés donc) et de compléter les données du SI interne (personnelles, de souscription, comportementales prédéfinies).

Pour apporter un éclaircissement sur la manière de rapprocher des données de navigation qui sont captées par la DMP de celles du SI interne, il faut partager un identifiant. Pour ce faire, dans le cadre de la Digital Data Factory en tout cas, 2 variables sont valorisées par vous-même dans le code JS du tracker (le tag JS inséré dans les pages de votre site) quand l’internaute est authentifié (ou par rebond email également) : pm_id (profil match ID) et pm_src (profil match Source). Elles correspondent à l’identifiant de l’utilisateur pour une source donnée du SI, par exemple 123456 (pm_id) dans le CRM (pm_src). C’est notamment par ce biais que le rapprochement est possible entre les données de navigation et les données du SI. Il y a d’autres moyens, mais cela pourra faire l’objet d’un article complet sur le sujet.

Frédéric FAURE @Twitter @Ysance

Big Data avec Hadoop : comparatif Hive, Pig, Impala, Shark & Spark

Hive Integration in Shark

Hive Integration in Shark

En matière de Big Data en général, et avec Hadoop en particulier, ce ne sont pas les noms de produits qui manquent : Hive, Pig, Impala, Shark, Spark, … Difficile de s’y retrouver dans ce zoo rempli de bêtes étranges. Après avoir creusé le sujet et effectué quelques tests, notamment sur EMR (Elastic Map Reduce d’AWS), voici un retour d’expérience sur quelques produits phares de l’écosystème Hadoop. Je vous propose donc un bref comparatif des technologies et quelques use-cases associés afin de vous donner les bases pour investiguer sur les produits qui vous intéressent.

Le but de cet article n’est pas d’expliquer l’architecture de Hadoop (1 & 2) ou de HDFS ou bien le fonctionnement de MapReduce. Il s’agit simplement de mettre en exergue les différences entre les produits précédemment cités afin que vous puissiez avancer dans la bonne direction en fonction de votre cas d’utilisation.

J’ai mis dans le tableau un certain nombre de critères qui me paraissent intéressants.

Hive Pig Impala Shark/Spark
Langage des requêtes HQL (HiveQL) Pig Latin HQL : HiveQL Spark – Scala / Python 

Shark – HQL (HiveQL)

Moteur M/R : MapReduce M/R : MapReduce MPP : Massively Parallel Processing RDD : Resilient Distributed Dataset
Compatibilité Hadoop 1 (car utilise M/R) & 2 1 (car utilise M/R) & 2 2 uniquement car utilise  MPP et à donc besoin de la nouvelle architecture « YARN » permettant d’utiliser le moteur de son choix à l’intérieur de l’ordonnanceur 1 & 2 : utilise RDD, cependant Spark remplace totalement la partie ordonnancement de Hadoop. Il peut donc fonctionner sur 1 également.
Tendance Disque Vs Mémoire Disque Disque Mémoire Mémoire (disque possible, mais avec de moins bonnes performances évidemment, mais toujours mieux que les produits basés sur MR)
UDF/UDAF : User Defined Function / User Defined Aggregate Function Oui en Java Oui en Java + Piggy Bank => dépôt contributif de UD(A)Fs écrites en Java 

Oui en Python, JavaScript, Ruby and Groovy => cependant le support est limité et la Piggy Bank n’est pas disponible

Plus d’infos sur Pig 0.13.0 Documentation > User Defined Functions

Impala 1.1 : oui et non => exécution via le Hive shell 

Depuis Impala 1.2 : oui => C++ et Java (vous pouvez réutiliser celles écrites pour Hive)

Plus d’infos sur Cloudera Impala > User-Defined Functions (UDFs)

Spark – Il est possible de coder des actions / transformations RDD en Java/Scala/Python (More on RDD Operations). 

Shark – Oui en Java (vous pouvez réutiliser celles écrites pour Hive)

Mapping direct d’un fichier S3 en tant que Table (sans avoir à le recopier sur HDFS) Oui Oui Non Oui pour Shark en tant que table 

Oui pour Spark en tant que fichier

Quelques remarques et précisions complémentaires :

  • Impala et Shark/Spark ont la bonne idée de pouvoir réutiliser les éléments constituants de Hive que vous avez déjà mis en place : Hive Metastore (description de la structure et des propriétés des tables), SerDes (Serializer/Deserializer) et UD(A)Fs.
  • Tous ces produits se basent sur HDFS.
  • A noter que Spark SQL est maintenant un élément à part entière du projet Spark et va remplacer Shark dans le futur proche (le motif invoqué pour ce changement est l’héritage lourd du code Hive utilisé par Shark) :

At the Spark Summit today, we announced that we are ending development of Shark and will focus our resources towards Spark SQL, which will provide a superset of Shark’s features for existing Shark users to move forward.

In particular, Spark SQL will provide both a seamless upgrade path from Shark 0.9 server and new features such as integration with general Spark programs.

And for organizations with legacy Hive deployments, Hive on Spark (NDLA : Spark as an alternative execution engine to Hive) will provide them a clear path to Spark.

A noter que dans la lignée de Shark et Impala :

In particular, like Shark, Spark SQL supports all existing Hive data formats, user-defined functions (UDF), and the Hive metastore.

Evolution de Shark vers Spark SQL

Evolution de Shark vers Spark SQL

  • Les UDFs correspondent à des fonctions scalaires c’est-à-dire qu’elles retournent une valeur unique pour chaque ligne de résultat et les UDAFs retournent une valeur basée sur un ensemble de lignes (agrégat : SUM, …).
  • Une interface graphique pratique, Hue, qui vous permet notamment
    • d’interroger vos jeux de données Hadoop via un éditeur de requêtes pour Hive, Pig et Impala,
    • de naviguer dans vos données et les gérer (visualiser les metastores, naviguer dans HBase, créer des jobs d’import/export de données avec Sqoop, …),
    • de planifier des workflows (avec Oozie).
  • Puisque je parle de Sqoop et de Oozie :
    • Apache Sqoop est un outil qui permet de transférer des données en mode bulk entre Hadoop et des bases de données structurées comme des bases de données relationnelles.
    • Apache Oozie, quant à lui, est un ordonnanceur de workflows qui permet de gérer des tâches Hadoop comme MR, Streaming MR, Pig, Hive, Sqoop, DistCp, … ou bien des tâches spécifiques comme des programmes Java ou bien des scripts Shell.
  • Et puisque j’ai introduit encore un nouveau nom, DistCp (distributed copy) est un outil basé sur MapReduce pour copier de gros volumes de données entre clusters ou à l’intérieur d’un même cluster : bash$ hadoop distcp2 hdfs://nn1:8020/foo/bar hdfs://nn2:8020/bar/foo.

Use-cases :

  • Dans des cas d’utilisation en mode interactif sur des requêtes ad hoc, les outils basés sur MR comme Pig et Hive ne sont pas adaptés car ils génèrent un programme Java à partir de la requête SQL que vous avez saisie avant de l’exécuter. Cela engendre une inertie non négligeable qui ne s’intègre pas bien avec un mode interactif. En revanche, des outils comme Impala et Shark/Spark (ou de Spark SQL/Spark ;o)) sont très intéressants pour ce use-case.
  • Pour l’exécution de tâches en mode batch sur de gros volumes de données, l’utilisation d’outils comme Hive ou Pig semble plus adaptée car ils n’ont pas une approche aussi agressive en mémoire que Impala ou Shark/Spark et auront moins de risque de rencontrer les limites mémoire de la machine. De plus sur de gros traitement, l’inertie liée à la génération de l’exécutable ne représente qu’une infime partie du temps de traitement. A voir cependant ce que donne l’utilisation de Shark/Spark sur disque sur de gros jeux de données qui ne peuvent siéger aisément en mémoire.
  • Concernant l’utilisation d’outils de reporting comme Tableau ou JasperSoft, outre la problématique de certains produits qui ne sont pas adaptés au mode interactif, il n’en demeure pas moins que le HQL diffère en termes de possibilités en comparaison de ce que l’on pourrait appeler du « true » SQL. Ils ne permettront pas de répondre aux fonctionnalités de ces outils. Il est donc indispensable (encore à l’heure actuelle) de connecter ces outils à une base relationnelle ou bien à un DWH/DM (Data WareHouse/ Data Mart).

Frédéric FAURE @Twitter @Ysance

Décryptage CoreOS, Mesos & Kubernetes

Logo CoreOS Mesos

Bonjour à tous !

On me pose pas mal de questions en ce moment sur CoreOSApache MesosKubernetes et Docker et leurs relations : « C’est la même chose ? », « On peut les utiliser ensemble ? ». J’en profite donc pour faire un petit article de décryptage pour tout le monde sur ces différents produits si vous vous posez les même questions ! ;ob

Mesos et CoreOS

Apache Mesos et CoreOS remplissent des fonctionnalités équivalentes : ils permettent de manager un pool de ressources (serveurs) pour exécuter des tâches dessus de manière à voir ce pool comme si il s’agissait d’un serveur unique. On peut voir Mesos ou CoreOS comme des OS distribués.

On trouve donc des équivalences avec les OS (Linux) classiques :

Le init system (distribué)

Marathon pour Mesos

« Marathon is an Apache Mesos framework for long-running applications. Given that you have Mesos running as the kernel for your datacenter, Marathon is the init or upstart daemon. »

Fleet (basé sur systemd & etcd) pour CoreOS

« With fleet, you can treat your CoreOS cluster as if it shared a single init system. »

« fleet ties together systemd and etcd into a distributed init system. Think of it as an extension of systemd that operates at the cluster level instead of the machine level. »

Le scheduler/cron (distribué)

Chronos pour Mesos

« Chronos is Airbnb’s replacement for cron. It is a distributed and fault-tolerant scheduler that runs on top of Apache Mesos. You can use it to orchestrate jobs. »

La fonctionnalité Timer units de systemd pour CoreOS

« Systemd is capable of taking on a significant subset of the functionality of Cron »

Kubernetes

Et Kubernetes dans tout ça ?

Maintenant que les ressources sont à disposition, il faut exécuter les tâches ou plus précisément mettre à disposition les services. On peut dire que Kubernetes prend en charge « une couche au dessus » de CoreOS et de Mesos. Ces services vont s’exécuter dans des containers Docker.

A noter une différence entre CoreOS et Mesos : CoreOS est (à l’heure actuelle) concentré sur l’exécution de containers Docker, alors que Mesos permet à plusieurs frameworks (voire plusieurs « copies » du même framework), comme Hadoop, Spark, … et depuis peu Kubernetes, de s’exécuter dans un même cluster « côte à côte ». Mesos est donc plus modulaire : Software projects built on Mesos.

Vous pouvez lire cette page Kubernetes Design Overview qui décrit le concept général du framework, mais pour résumer Kubernetes permet de définir les services métiers à un niveau macro ainsi que leurs attributs, je m’explique :

Le paradigme dans lequel ces outils évoluent est celui des microservices. A ce sujet je vous conseille vivement de lire ces 2 articles Microservices – Not A Free Lunch! (« individual services are very simple, a lot of complexity exists at a higher level ») et Idées fausses sur Docker. A noter que concernant le second, il faut prendre en compte la dynamique de l’évolution autour de Docker (« Actuellement, des besoins à grandes échelles sont nécessaires pour que les avantages de Docker surpasse sa complexité. Cependant, à la vitesse à laquelle évolue le projet, cela pourra rapidement ne plus être le cas. ») et je vois également un intérêt notable actuellement pour les environnements (Dev, Build & Int) pris en charge par la CI (Continuous Integration) : Retour d’Expérience d’une Intégration Continue avec Docker/Gitlab/Jenkins.

Kubernetes permet donc de déclarer des services métiers constitués d’un ensemble de microservices, chaque microservice étant hébergé sur un container. Ce service métier est pris en charge par la notion de Pods qui définit les ressources partagées par les microservices et qui sert d’unité de déploiement et de scaling horizontal/replication. Il y a ensuite la notion de Labels associé à chaque Pod qui permet de les identifier puis de les sélectionner depuis un autre service métier (Pod) pour communiquer avec, de manière faiblement couplée donc (on s’adresse à une déclaration de service métier et non directement aux containers le servant).

J’espère que ce petit décryptage vous sera utile ! 🙂

Frédéric FAURE @Twitter @Ysance

Retour d’Expérience d’une Intégration Continue avec Docker/Gitlab/Jenkins

Logo Docker

J’ai récemment monté pour un client une Intégration Continue basée sur Docker, Gitlab et Jenkins. L’objectif était de proposer une Intégration Continue ayant pour couverture les postes de développement, la plateforme de build permettant de passer les tests unitaires, ainsi que la plateforme d’intégration à disposition du client et des responsables du projet à des fins de recette. La plateforme de production quant à elle a été montée sur des VMs (par opposition aux containers) dédiées en utilisant évidemment le contenu des Dockerfiles qui nous servait de modèle de configuration comme pourrait le faire un descripteur Puppet.

Gitlab et Jenkins ont été choisis car ce sont des valeurs sûres qui font bien le travail qui leur revient : gestion des sources avec une interface graphique efficace pour Gitlab (en fait c’est Github @home) et Jenkins pour l’ordonnancement des builds/tests unitaires et des déploiements. Le pari était plus sur l’utilisation de Docker et la manière de l’intégrer dans l’Intégration Continue afin d’en tirer le maximum d’avantages. Plusieurs objectifs ont donc été ciblés (et atteints) via l’utilisation de Docker, mais des difficultés ont également été rencontrées.

C’est ce que je vais vous présenter dans la suite de cet article.

Docker

Pour résumer, Docker est basé sur la technologie LXC (« LXC is often considered as something in the middle between a chroot on steroids and a full fledged virtual machine. The goal of LXC is to create an environment as close as possible as a standard Linux installation but without the need for a separate kernel. ») qui va vous permettre d’exécuter des containers isolés à l’intérieur d’une VM, de limiter les ressources (bande passante, CPU, …) auxquelles ils ont accès et de les mettre en relation sur un réseau interne et externe via un bridge. De plus, la performance est une idée sous-jacente de Docker, basé sur AUFS (AnotherUnionFS), qui permet de démarrer un container à partir d’une image en quelques millisecondes.

Utiliser Docker : philosophie et objectifs

Docker Filesystems Multilayer

Docker Filesystems Multilayer

Tout d’abord j’utilise les containers Docker comme des lightweight VMs plutôt que comme des single-process/multi-process microservices. Je vous invite à lire ces commentaires intéressants sur le sujet suite à un post concernant l’utilisation ou non de SSHd dans images Docker. A ce sujet j’ai dû ré-installer/re-configurer un certain nombre de services implicites sur une distribution classique comme les CRONs, le logger, … J’aurais pu utiliser cette image directement, mais ne l’ai pas fait car je n’avais pas encore rencontré les problèmes auxquels nous avons été confrontés. A refaire, avec l’expérience, je prendrai en compte sérieusement cette alternative (baseimage-docker).

Les objectifs concernant l’utilisation de Docker sont :

  • Avoir des composants homogènes (version, configuration, …) entre postes de DEV, plateforme de build et plateforme d’intégration.
  • Déployer des composants transverses (servant à l’amélioration de la qualité du code et ce sur tous les environnements, y compris les postes de Dev) dans des containers dédiés ou bien intégrés aux containers : actuellement en matière de container dédié j’en ai déployé un contenant un serveur de logs (Kibana) et j’envisageais aussi d’en déployer un autre avec un outil de graphing comme Graphite pour récupérer les métriques systèmes des containers et voir quel composant consomme le plus de ressources (mais je n’ai pas eu le temps), et en matière de composants intégrés j’ai ajouté du XDebug/Webgrind (pour Php) pour du debugging et du profiling de code, par exemple.
  • Simuler la topologie réseau, en effet les containers communiquent entre eux via IPs, donc pas de configuration en 127.0.0.1 où tous les composants d’une plateforme sont installés sur la même VM et communiquent en local, ce qui en général aboutit à des problèmes au moment du passage en environnement cible distribué.
  • Possibilité de simuler la répartition d’une fonctionnalité sur plusieurs serveurs, même et surtout sur le poste de DEV : stockage des données shardées sur plusieurs Redis par exemple.
  • Centralisation des configurations, qui seront appliquées à l’identique sur la Production (à l’exception des configurations liées aux ressources disponibles sur le serveur : CPU, RAM, …), dans les Dockerfiles et autres fichiers de configuration des services embarqués dans les images. Ces configurations sont elles-même stockées dans Gitlab.

Architecture générale

L’intégration Continue

Un schéma étant plus parlant qu’un long discours, vous trouverez ci-dessous le schéma (vous pouvez cliquer dessus pour agrandir la vue) de l’architecture de l’Intégration Continue.

Architecture CI

Architecture CI

A noter que les ports des containers exposant les services de base de données sont bindés sur la VM afin de pouvoir y accéder en remote avec des clients graphiques (ou non ;)) dans les environnements où cela fait sens.

L’architecture se décompose en 4 environnements distincts :

CI Management

Ce serveur contient Gitlab, Jenkins et la Docker Private Registry. Pour information, la Private Registry est installée via une image Docker packagée qui est exécutée en tant que container. La commande de lancement est la suivante :

sudo docker run -d -p 5000:5000 -v /registry:/tmp/registry -name registry registry:0.6.5

Les 3 composants sont installés derrière un reverse proxy NGinx qui assure le SSL offloading ainsi que l’authentification pour la Private Registry Docker.

5 images différentes ont été générées puis stockées dans la Private Registry : reverse proxy (NGinx), serveur Web Php (NGinx + Php-fpm), Redis, MySQL, serveur de logs (Kibana). Pour simplifier le schéma, je ne représente pour chaque environnement (ils sont tous identiques) qu’une seule instance (container) de chaque image car en réalité il y a 2 containers Redis (sharding) et 2 containers MySQL (séparation métier des données : base opérationnelle / base BI).

Intégration

Ce serveur contient uniquement l’agent Jenkins pour le déploiement/build des sources et le client Docker. Les containers sont running de manière continue, permettant ainsi aux responsables du projet et au client de tester la dernière version du produit poussé manuellement à partir de Jenkins.

Logo Jenkins

CI Slave One

Ce serveur contient uniquement l’agent Jenkins pour le déploiement/build des sources et le client Docker. En revanche, les containers sont running uniquement au moment d’un push d’un ou plusieurs commit au niveau de Gitlab qui déclenche via un hook la tâche de build et de passage des TUs au niveau de Jenkins. Dès qu’une modification du code est donc poussée sur le repository, il y a vérification que l’ensemble fonctionne toujours.

Attention, nous ne construisons pas les images à ce moment là : elles sont déjà construites et stockées dans la Private Registry. Les images sont simplement rafraîchies au cas où il y aurait eu un changement, puis le code source est monté dans le container serveur Web Php via un volume pour exécution des TUs.

A noter également que la plateforme de build (CI Slave One) ne démarre pas le reverse proxy car il n’y a pas besoin d’accéder ni au serveur Web Php ni à Kibana de l’extérieur (il est simplement démarré pour s’assurer que l’intégralité de la plateforme démarre bien et pourrait également faire l’objet de TUs pour vérifier que les logs circulent bien jusqu’au stockage sur ElasticSearch). Les ports des bases de données ne sont pas non plus bindés sur l’extérieur pour accès via un client en remote. En effet, tout cet environnement est temporaire pour le temps d’exécution des TUs.

Une particularité pour le lancement de cet environnement :

sudo docker run -a stdout -a stderr -p 80:80 -v /apps/www/web-site/public:/apps/www/web-site/public:rw -v /apps/www/web-site/static:/apps/www/web-site/static:ro -v /apps/www/web-site/extra_vh_conf:/conf/extra_vh_conf:ro -v /apps/www/web-site/init_db:/conf/init_db:ro -v /apps/www/web-site/cron_files:/conf/cron_files:rw -link kibana:kibana -link mysql1:mysql1 -link mysql2:mysql2 -link redis1:redis1 -link redis2:redis2 -name webserver_php docker.monprojet.com:443/webserver_php –docker-env=tu –init-db=true

On redirige les sorties (-a stdout -a stderr) afin que Jenkins puisse les afficher et on fait bien attention à ce que l’entrypoint (cmd.sh) du container propage bien le code de résultat des TUs afin que Jenkins passe en échec ou en succès. On passe également en paramètres de l’entrypoint l’environnement d’exécution (–docker-env=tu) et le fait que l’on initialise les données à chaque fois (–init-db=true), en effet, elles ne sont pas persistées entre 2 builds.

Développement

Logo Gitlab

Nous avons X postes de DEV qui exécutent les containers en permanence et modifient le code via IDE dans le répertoire projet monté dans le container Serveur Web Php (à noter que des outils de développement sont installés dans ce container tel que XDebug pour le debugging et le profiling, … Cf. objectif « composants transverses » ). Les scripts d’initialisation des données, les fichiers contenant les rewrite rules des Virtual Hosts, les fichiers contenant les CRONs, … sont pris en compte/exécutés au lancement des containers, les fichiers étant nommés selon une norme dans l’arborescence du projet.

La Production

La Production quant à elle est exécutée sur des VMs sans utiliser Docker. Nous avons fait ce choix en premier lieu pour des questions de stabilité de Docker que nous ne jugions pas encore Prod-ready.

Nous n’avions ensuite que peu d’intérêt en termes de rationalisation des ressources car nous utilisons pour chaque service une instance « dédiée » (pas au sens « dédié » des EC2, mais simplement que le service est seul sur son instance) de « taille respectable » (no pun intended :)) dont les ressources sont convenablement utilisées.

Utiliser Docker : problèmes et solutions

Ne vous y trompez pas, j’ai apprécié de travailler avec Docker et le résultat obtenu. A refaire, je mettrai en place exactement la même solution car j’ai obtenu les objectifs recherchés avec cette combinaison d’outils. Cependant, je dois avouer que j’ai quand même dû sortir les rames car j’ai rencontré un certain nombre de problèmes qui m’ont rendu la tâche difficile, ou tout du moins plus dure que prévue. Je vous donnerai les moyens que j’ai employés pour les résoudre (quand cela a été possible). Pour information, j’ai construit l’Intégration Continue sur la version 8.0 de Docker sur une Ubuntu Precise (LTS) montées sur des instances EC2 (AWS). Les postes de Dev quant à eux sont des VMs Ubuntu Precise Desktop montées sur VirtualBox.

Philosophie

La philosophie première de Docker est d’exécuter un processus dans un container et la conception générale est orientée dans cette direction de servir des single/multi-process microservices. Cependant, un nombre certain d’utilisateurs, dont je fais partie, se dirigent vers une utilisation des containers Docker comme des lightweight VMs en embarquant à l’intérieur tout le nécessaire pour faire fonctionner un service complet qui est une partie d’une plateforme distribuée plus complexe. Pour rappel, un article représentatif que j’ai cité plus haut Why you don’t need to run sshd in your Docker containers et ces commentaires intéressants associés. Dans les cas présentés dans l’article, les solutions envisagées à base de volumes reviendrait à introduire un effort de design supplémentaire et une pénibilité à maintenir ce modèle sur une grande quantité de containers pour arriver à un résultat équivalent en se connectant tout bêtement en SSH.

Stabilité

Un des points principaux qui me fait dire que Docker, en tout cas cette version (et les quelques suivantes aussi), manque de stabilité est une certaine légèreté sur le traitement de remontés orientées stabilité justement.

Un exemple :

Je me suis fais spammé par un message du style « aufs <process> <directory> is overlapped ». En cherchant sur Internet je trouve que mon cas a déjà été remonté : aufs test_add:246:docker[973] /var/lib/docker/ is overlapped #3668. La conclusion du thread est :

As far as I’ve seen, that warning had no side effect and it’s also being printed on my system running Docker on AUFS.

I’ll close this issue now. Please report back if you run into problems and you think they might be somehow associated with those warnings.

Cela me gêne de fermer une issue parce qu’il ne semble pas y avoir d’impact, alors que quand je lis la documentation de AUFS :

Aufs rejects the branch which is an ancestor or a descendant of anther branch. It is called overlapped. When the branch is loopback-mounted directory, aufs also checks the source fs-image file of loopback device. If the source file is a descendant of another branch, it will be rejected too.

After mounting aufs or adding a branch, if you move a branch under another branch and make it descendant of anther branch, aufs will not work correctly. »

Ce message ne me semble pas être uniquement cosmétique.

De manière générale, on sent bien une évolution de Docker au travers des versions pilotée en premier lieu par l’apport de nouvelles features, cependant je m’attache à ce genre de « détails » orientés industrialisation.

Rétro-compatibilité

Les fonctionnalités évoluent vite et mettre à jour la CLI Docker représente un risque non négligeable : par exemple, j’ai essayé de passer de la version 8.0 à la version 8.1 pour bénéficier d’un correctif. Résultat : l’authentification ne fonctionnait plus avec la Private Registry. Le « problème » est que sur un projet, on doit prioriser les tâches à valeur ajoutées et si upgrader la version de Docker pour régler un problème induit de devoir revoir une autre partie à côté… Et bien je suis resté en 8.0.

Networking

La partie Networking aurait pu aussi bien se retrouver dans la partie philosophie que dans la partie rétro-compatibilité : au moment où j’ai débuté la mise en place de l’Intégration Continue, il y avait pas moins de 5 méthodes pour mettre en relation les containers entre eux. Le problème est qu’il est difficile de prédire dans l’avenir quelle solution sera privilégiée et donc quelle est celle à choisir pour pouvoir mettre à jour Docker sans avoir à repenser tout le design par la suite.

Au moment de Docker 8.0, il y avait :

  1. le link statique avec le IPs « en dur » (déconseillé par Docker et pour cause… C’est statique),
  2. la récupération de l’IP du container qui vient de démarrer via « inspection » (docker inspect CONTAINER) pour réinjection en tant que paramètre au container dépendant (du scripting à prévoir pour le lancement de la plateforme complète et des paramètres « techniques » à prévoir pour l’entrypoint en plus des paramètres « métiers » au lancement des containers : peut être plus simple),
  3. Ambassador Containers (j’ai déjà assez de containers quand ma plateforme complète est démarrée ! :o)),
  4. Pipework, Software-Defined Networking for Linux Containers (là j’avoue que j’ai lâché l’affaire, trop compliqué),
  5. Et finalement le Container Linking utilisant les options –name et –link lors d’un docker run. C’est cette option que j’ai privilégiée en nommant mes containers puis en établissant un link entre eux : les informations (et notamment l’IP) du container linké sont récupérées dans les variables d’environnement du container utilisant les services du container linké (REDIS1_PORT_6379_TCP_ADDR=172.17.0.5) via le script cmd.sh paramétré comme entrypoint (ENTRYPOINT [« /apps/cmd.sh »]) qui met à jour les fichiers de configuration (sed -i « s/REDIS1_PORT_6379_TCP_ADDR/$REDIS1_PORT_6379_TCP_ADDR/g » /etc/php5/conf.d/redis.ini) avant de lancer les services.

En utilisant la dernière méthode on peut ainsi démarrer un container redis en le nommant :

sudo docker run -d –expose=22 -p 6379:6379 -v /data/redis1:/data/redis:rw -name redis1 docker.monprojet.com:443/redis

Attention : soit vous exposez (–expose=6379) simplement le port de chaque container redis au niveau du réseau interne Docker (bridge), soit, si vous voulez binder les ports sur la VM, il faudra penser à effectuer une translation (-p 6380:6379-p 6381:6379, …) pour les autres containers. :o)

Ou bien un le container Kibana :

sudo docker run -d –expose=22 –expose=80 –expose=9200 –expose=udp/5140 -v /data/kibana:/var/lib/elasticsearch:rw -name kibana docker.monprojet.com:443/kibana

Puis les linker avec le container serveur Web Php pour qu’il puisse accéder à leurs services :

sudo docker run -d –expose=80 –expose=22 -v /apps/www/web-site/public:/apps/www/web-site/public:rw -v /apps/www/web-site/static:/apps/www/web-site/static:ro -v /apps/www/extra_vh_conf:/conf/extra_vh_conf:ro -v /apps/www/init_db:/conf/init_db:ro -v /apps/www/cron_files:/conf/cron_files:rw -link kibana:kibana -link mysql1:mysql1 -link mysql2:mysql2 -link redis1:redis1 -link redis2:redis2 -name webserver_php docker.monprojet.com:443/webserver_php –docker-env=int –init-db=$INITDB

On notera les paramètres passés en fin de ligne à l’entrypoint pour paramétrer le lancement fonction de l’environnement et du besoin de réinitialiser les données.

Droits des Volumes

Dans le cas de volumes montés en écriture, étant donnée que l’hôte et les containers ne partagent pas les mêmes utilisateurs/groupes ou tout du moins ils n’ont pas les même ids, je me suis retrouvé sur mon hôte avec des fichiers et répertoires initialement en root:root passés en messagebus:messagebus ou nobody:nobody. Là j’avoue j’ai un peu tiqué… Je ne sais pas trop quoi en penser ou quel workaround appliquer. Vos suggestions sont les bienvenues ! :o)

Private Registry

La Private Registry nous permet de stocker les images construites et mises à disposition des autres plateformes utilisant Docker. Nous utilisons la version 0.6.5 de la registry (registry:0.6.5).

Authentification

Une partie ou j’ai un peu souffert… Il n’y a pas de gestion d’authentification par défaut sur la Private Registry. Il faut donc la mettre en place au niveau de NGinx avec une basic_auth.

Le premier problème a été de trouver des informations sur le sujet : l’authentification à une Private Registry était un sujet abordé par quelques pionniers qui ont donné de leur temps.

Le second problème est que l’authentification est plus que simpliste. Pas moyen de positionner 2 niveaux de droits, un pour la lecture (GET) et un pour l’écriture (POST) des images. Même avec des directives telles que :

limit_except GET {
}

On pourrait laisser la lecture libre et l’écriture soumise à authentification. Mais rien de bien plus précis comme par exemple avoir un système d’authentification avec des rôles associés (puller les images, pusher les images, …). A part en réaliser un nous-même en front, rien de build-in.

Au final, ma configuration est celle-ci (avec un NGinx <1.3.9 installé avec nginx-extras pour bénéficier du chunking nécessaire) :

# Requires nginx >=1.3.9
# FYI: Chunking requires nginx-extras package on Debian Wheezy and some Ubuntu versions
# See chunking http://wiki.nginx.org/HttpChunkinModule

upstream docker-registry {

server localhost:5000;

}

server {

listen 443 ssl;
server_name docker.monprojet.com;

ssl on;
ssl_certificate /etc/nginx/star_monprojet_com_combined.crt;
ssl_certificate_key /etc/nginx/monprojet.key;

proxy_set_header Host $http_host; # required for docker client’s sake
proxy_set_header X-Real-IP $remote_addr; # pass on real client’s IP
proxy_set_header Authorization «  »; # see https://github.com/dotcloud/docker-registry/issues/170

access_log /var/log/nginx/docker_access.log;
error_log /var/log/nginx/docker_error.log;

client_max_body_size 0; # disable any limits to avoid HTTP 413 for large image uploads

# required to avoid HTTP 411: see Issue #1486 (https://github.com/dotcloud/docker/issues/1486)
chunkin on;
error_page 411 = @my_411_error;
location @my_411_error {

chunkin_resume;

}

location / {

auth_basic « Restricted »;
auth_basic_user_file /etc/nginx/.docker-htpasswd;

proxy_pass http://docker-registry;

proxy_set_header Host $host;
proxy_read_timeout 900;

}

location /_ping {

auth_basic off;

proxy_pass http://docker-registry;

}

location /v1/_ping {

auth_basic off;

proxy_pass http://docker-registry;

}

}

A noter que les messages en retour en cas d’erreur de l’authentification étaient incompréhensibles. J’ai eu beaucoup de mal à troubleshooter quand l’authentification ne fonctionnait pas au début.

Tagging

Il n’est pas possible de versionner dans une Private Registry comme on peut le faire sur la version publique et le tag contient le host permettant d’atteindre le NGinx qui reverse-proxyfie (c’est un verbe ?!?) vers la Private Registry. C’est en se basant sur celui-ci que la CLI Docker détermine si on essaie d’accéder à la registry publique ou à une private. De ce fait, le path réseau (par exemple docker.monprojet.com:443) utilisé pour enregistrer (pusher) l’image est stocké et il n’est pas possible de récupérer (puller) l’image par un autre path réseau :

docker.monprojet.com:443/kibana

Et oui parce que j’avais bien pensé pour le point précédent (authentification) passer en direct sur la Private Registry (sans passer par le reverse proxy) pour enregistrer et bloquer les requêtes POST sur le reverse proxy pour ne laisser que les requêtes GET (récupération des images) soumises à basic_auth, mais à ce moment le chemin changeant…

docker.monprojet.com:5000/kibana

… Le nom aussi ! Et il n’était plus possible de la récupérer de l’extérieur via le reverse proxy !

Nettoyage

Là aussi gros mal de tête : pas moyen de supprimer des images pushées dans la registry, donc au fil des modifications sur les images pushées : accumulation de layers ! Et on se retrouve depuis un client (poste de Dev, plateforme d’intégration, …) à charger une image obèse (et je pèse mes mots) constituée d’une arborescence de layers (de taille respectable ;b) qui met du temps à être téléchargée. Il faut dans ce cas nettoyer la registry régulièrement : arrêt du container registry, suppression de toutes les données du répertoire contenant les images sur le host (/registry), relance du container puis reconstruction + push des images. Vraiment pas pratique. Et là on en arrive au point suivant…

Gestion des packages

Qui dit reconstruction complète de toutes les images dit repassage du « apt-get update » et prise en compte des nouveaux packages qui peuvent induire des modifications de comportement ! Ca nous est déjà arrivé, dur pour un simple nettoyage.

Pas de secret, pour gérer l’aspect packages je ne vois pas d’autre solution que de mettre en place un repo miroir dont je contrôle les mises à jour et sur lequel je pointe. Si vous avez une autre idée (autre qu’avoir downloadé tous les packages en local à un instant « t » pour les inclure dans l’image par add au build… hein !) je suis preneur.

Pour limiter la prise de poids des images de la registry (et donc éviter le rebuild complet et la mise à jour des packages associée) accédée par les plateformes (dev, int, …), une solution aurait été d’avoir une deuxième registry que l’on pourrait nommer staging. Elle permettrait de supporter les constructions successives pour affinage d’une image avant de pusher la version validée sur la registry accédée par toutes les autres plateformes. Mais cela ne règle pas le problème de fond.

Certificats

La CLI Docker n’acceptant pas les certificats auto-signés (au début du projet nous n’avions pas de certificat valide pour installer sur le reverse proxy NGinx de la CI Management – cf. schéma), il a fallu ruser et créer notre propre CA pour signer un certificat, puis importer notre CA sur les  postes utilisant la CLI Docker dans les autorités de certifications reconnues via update-ca-certificates.

Conclusion

En conclusion on peut dire que l’Intégration Continue mise en place avec Docker, Gitlab et Jenkins a atteint les objectifs fixés. Plus particulièrement au sujet de Docker, la tâche n’a pas toujours été aisée, principalement à cause d’une lacune en termes d’industrialisation et des solutions proposées qui ont tendances à suivre une philosophie (single/multi-process microservices) qui n’est pas complètement en accord avec un usage lightweight VMs que je pratique, tout comme un nombre certain d’utilisateurs de Docker à en croire mes lectures.

Pour ma part, ce que je regarde avec attention dans l’évolution de Docker (qui a déjà sorti la version 1.0 à l’heure où j’écris ces lignes) sont les aspects de stabilité et de rétro-compatibilité pour assurer une maintenance et une évolutivité plus simple du produit. Ensuite au niveau de la Private Registry, une authentification un peu plus poussée et en built-in, un tagging plus souple (n’incluant pas le path réseau) et complet et finalement une possibilité de nettoyage et de manipulation de manière plus générale des images stockées.

Dans tout les cas, Docker (et LXC) est un produit à suivre, pas seulement parce que c’est à la mode, mais parce qu’il répond réellement à des problèmes comme ceux de l’Intégration Continue ou bien de la construction de plateformes PaaS en rationalisant l’utilisation des machines tout en assurant performance et isolation.

Frédéric FAURE @Twitter @Ysance

AWS re:Invent 2013 (2.3/2) : Use Cases – Concept du monitoring à grande échelle avec S3 et EMR par Netflix

Logo Netflix

Cet article fait suite à la synthèse des sorties annoncées lors du summit AWS re:Invent 2013 qui s’est déroulé cette année du 12 au 15 Novembre à Las Vegas. Nous allons aborder dans ce dernier article de la série l’utilisation que Netflix fait de EMR et de S3 pour sa télémétrie, comprendre la collecte des métriques de ses infrastructures World Wide en un endroit centralisé pour les utiliser dans le cadre d’un monitoring.

Cet article se base sur une présentation très intéressante de Roy Rapoport (Netflix) qui nous a fait partager l’architecture de Atlas, leur outil maison de monitoring mis en place pour traiter le volume colossale d’informations générées par leur infrastructure, autant au niveau de la collecte que de l’exploitation (analyse). L’un des points cruciaux est de pouvoir avoir un accès rapide à ces millions de métriques, à savoir une faible latence entre la génération de la métrique dans l’infrastructure distribuée et sa mise à disposition sur de multiples dimensions dans l’outil de monitoring centralisé.

Vous pourrez retrouvez également les autres articles de cette série consacrée au retour d’expérience du summit AWS en suivant ces liens Optimisation de son cluster EMR (Elastic MapReduce)Créer son NAS sur AWS : NFS, CIFS & GFS (GlusterFS) ou bien encore Accélérer la délivrance de son contenu via CloudFront et Route 53. C’est parti pour l’article 2.3 de la série ! :o)

Concept du monitoring à grande échelle avec S3 et EMR par Netflix

Netflix possède une infrastructure World Wide construite sur un modèle SOA et déployée sur AWS. L’objectif premier de Netflix en construisant son infrastructure sur AWS n’était pas de réduire les coûts mais bien d’avoir à disposition un environnement scalable pour ne pas être limité.

Un des gros chantiers dans le traitement d’un tel volume de métriques à destination du monitoring est d’une part le stockage, mais également la vitesse de mise à disposition des métriques à destination des consommateurs, qu’ils soient Ops ou bien Business oriented. Nous allons voir comment Netflix a dû faire évoluer son architecture au fil du temps avec la quantité croissante de données collectées dans leur infrastructure.

Cet article sera articulé dans un mode lessons learned.

Culture à Netflix :

  • L’optimisation de la rapidité d’innovation est privilégiée par rapport aux coûts engendrés : « Ca coûtera ce que ça coûtera ».
  • Intimement lié au principe précédent, ils embauchent des gens brillants (et expérimentés) qui sont autonomes et qui apporteront des expériences et des façons d’aborder les problèmes différentes de celles qui peuvent être « acquises » chez Netflix.
  • Une sélection naturelle des processus est effectuée : seuls ceux qui sont utiles sont adoptés par les équipes et appliqués dans le temps.
  • Décentralisation de la responsabilité des opérations : les développeurs sont responsables de leur code. Les tâches suivantes sont donc de leur responsabilité : Build, Test, Deploy, Set up alerting and monitoring &… Wake up at 2AM ! ;ob

Ancien système de monitoring :

  • Système mis en place par un admin système sur son temps disponible.
  • Télémétrie gérée dans un système RRD. Pour plus d’informations sur RRDTool et ses RRAs, vous pouvez lire cet article et celui-ci.
  • La tenue à la charge était correcte tant que la quantité de métriques n’a pas explosé. Il a montré ses limites lors des passages successifs de différents paliers : 5/11 => 2 millions de métriques, 9/11 => 10 millions de métriques, 4/12 => 18 millions de métriques.
Netflix Atlas Metric Format

Netflix Atlas Metric Format

Nouveau système de monitoring :

La métrique

  • Atlas (nouveau système) va prendre le relai de Epic (ancien système). CloudWatch fournit également des métriques. Une UI (User Interface) multi-systèmes va permettre de prendre en compte les 3 sources de métriques afin d’assurer la continuité de l’existant.
  • Les métriques restent dans la région (au sens AWS) où elles sont générées pour maintenir une isolation régionale. Un endpoint par région permet de récupérer les métriques localisées et un endpoint global permet de requêter sur toutes les métriques en mode multi-régions.
  • Contrairement à avant où l’identification de l’origine du métrique était réalisée de manière centralisée, c’est à présent chaque agent (client) qui envoie l’identité du serveur/application sur lequel il est installé.
  • Evolution du nom des métriques : passage d’un nom de métrique (contenant de nombreuses meta-data) d’un style vecteur a.b.c.d.e.f à une liste de paires clé-valeur [(ka=va), (kb=vb), (kc=vc), (kd=vd), (ke=ve), (kf=vf)] (voir photo ci-contre). C’est l’agent qui s’occupe de générer cette liste et de l’adresser au collecteur.
  • Les requêtes sur les données collectées sont exécutées via un outil maison puissant qui rend les requêtes complexes possibles, mais… les simples un peu difficiles ! :o)
Netflix Atlas Overall Architecture

Netflix Atlas Overall Architecture

Utilisation des métriques

  • Toutes ces métriques sont utilisées dans des graphiques, dashboards, pour l’alerting, …
  • Elles ont besoin d’être remontées rapidement, pas seulement pour le monitoring Ops, mais aussi pour les besoins analytiques de la BI.
  • Durant les pics d’utilisation de la donnée, des batchs ou autres peuvent faire monter le nombre de data points requêtés à 2.8G (million) data points/s.

Architecture de Atlas v1

  • L’agent sur le serveur/l’application publie la métrique sur le publish cluster (cluster de publication).
  • Le poller cluster réalise régulièrement des health checks/snmp requests sur le serveur/l’application et publie également la métrique sur le publish cluster.
  • La métrique est ensuite envoyée par le publish cluster au backend (shardé sur plusieurs instances) de la région et agrégée en parallèle par blocs dans des fichiers par ce même publish cluster pour être envoyée sur S3.
  • Le backend est accessible via un regional endpoint et également via un global endpoint.
  • Cependant le système n’est pas assez véloce.
Netflix Atlas Memory Approach

Netflix Atlas Memory Approach

Architecture de Atlas v2

  • Passer le backend sur Cassandra avec du SSD en lieu et place des disques durs… Cher et techniquement pas sûr que ce soit une bonne solution pour répondre au besoin.
  • La mémoire ! C’est une bonne solution : tout en mémoire dans des instances m2.4xlarge ! … Beaucoup trop cher compte tenu du volume de données à stocker…
  • Essayons de réduire la donnée stockée (la métrique) avant de s’attaquer à l’architecture/infrastructure.
  • La métrique est composée de 2 éléments : tags (le nombre d’informations qui joue sur la taille unitaire d’une métrique) & time (la fréquence, c’est à dire la répétition de cette taille).
  • Les instances ont une durée de vie courte (un ou deux jours) : il n’est pas nécessaire pour la plupart des métriques de conserver l’identifiant de l’instance, car c’est un événement utilisateur qui nous intéresse et pas un événement utilisateur qui s’est produit sur cette instance particulière.
Netflix Atlas Reductive Approach

Netflix Atlas Reductive Approach

  • Approche par réduction des séries de données : on conserve uniquement pour une série les informations agrégées minimum, maximum, total et count => 3, 5, 9, 14, 20 (une prise par minute, soit 5min) devient min 3, max 20, total 51, count 55. On passe de 5 à 4 data points… Mais si on augmente la taille du time bucket réduit en prenant par exemple une heure (au lieu de 5min) : on passe de 60 data points à 4. Le average n’est pas calculé car il ne permet pas de réductions ultérieures simples, contrairement au couple total & count.
  • Mise en place d’un policy driven system exécuté par EMR et proposant 4 actions : preserve (conservation de la donnée intacte), drop (éviction de la donnée), consolidate (réduction de la série de données du time bucket), rollup (cumulatif, somme des valeurs pour un tag donné).
  • On obtient donc 4 clusters (voir schéma ci-dessous) dont le premier est alimenté directement par le publish cluster et contient les données intactes des 6 dernières heures. Les suivants sont alimentés à partir des données, stockées dans S3, qui sont passées par EMR pour être réduites (pour rappel, c’est le publish cluster qui dépose les blocs de données agrégées dans S3). Plus on « va vers la droite », plus la réduction est importante.
Netflix Atlas EMR Architecture

Netflix Atlas EMR Architecture

Netflix Atlas Reduction Policy

Netflix Atlas Reduction Policy

  • Il est possible d’ajouter des policies sur demande.
  • Ces policies sont paramétrées dans l’EMR Driver.
  • On obtient un stockage indéfini sur S3.
  • Note personnelle : c’est le même principe que l’on retrouve dans RRDTool cité précédemment, mais à l’échelle BigData.
  • On peut donc, grâce aux policies, cacher des métriques et les réactiver sur demande, définir des granularités spéciales pour des jours spéciaux et se prévenir de voir les clusters d’historisation pollués par une explosion de métriques (un développeur qui ajoute par mégarde une génération de métrique volumineuse) en ajoutant une policy qui drop la métrique avant qu’elle ne soit propagée.

Gain

Netflix Atlas Reduction Efficiency

Netflix Atlas Reduction Efficiency

Le gain est présenté ci-contre, avec les informations suivantes :

  1. Le Time Horizon correspond à la période de rétention de la donnée.
  2. La Size correspond au nombre d’instances (de type m2.4xlarge) nécessaires pour chaque cluster du backend.
  3. Le ratio Instances Per Hour correspond au nombre d’instances nécessaires pour le cluster pour traiter 1h de données. On notera un petit souci sur les arrondis (à l’inférieur) qui amène des résultats surprenants ! ;ob
  4. Le % Reduction indique le gain par rapport au premier cluster (6H) non réduit.

What’s Next :

Plusieurs pistes sont à l’étude pour améliorer encore l’existant :

  • Utilisation des nouveaux types d’instances qui sont apparues entre temps pour réduire les coûts.
  • Les utilisateurs des données sont très demandeurs de métriques mais ne demandent pas beaucoup de métriques différentes : l’idée serait de migrer les données les moins demandées sur des stockages moins performants, donc de réduire les coûts.
  • Actuellement le délai de remontée d’une alerte par le système de monitoring est de 5 minutes. L’idée serait de mettre en place un nouveau canal dédié à la remontée des alertes pour arriver à un délai de 1 minute pour être prévenu d’un incident sur l’infrastructure World Wide.

Frédéric FAURE @Twitter @Ysance

AWS re:Invent 2013 (2.2/2) : Use Cases – Accélérer la délivrance de son contenu via CloudFront et Route 53

Waterfall Graphs

Cet article fait suite à la synthèse des sorties annoncées lors du summit AWS re:Invent 2013 qui s’est déroulé cette année du 12 au 15 Novembre à Las Vegas. Nous allons aborder dans cet article l’accélération de la délivrance du contenu d’un site via CloudFront et Route 53. Cet article va expliquer les mécanismes mis en place au niveau de CloudFront pour optimiser la vitesse de récupération du contenu depuis la source et de distribution vers le navigateur, voire même comment améliorer l’envoi de données depuis un formulaire vers votre serveur Web en passant par CloudFront qui sert de relai. Nous verrons également quels contenus sont éligibles à être cachés.

Cet article se base sur une présentation très intéressante de Kalyanaraman Prasad (AWS) et Parviz Deyhim (AWS), de même que les illustrations qui en sont tirées. Je diverge des avis présentés durant la session sur 2 points que je signalerai dans l’article.

Vous pourrez retrouvez également les autres articles de cette série consacrée au retour d’expérience du summit AWS en suivant ces liens Optimisation de son cluster EMR (Elastic MapReduce)Créer son NAS sur AWS : NFS, CIFS & GFS (GlusterFS) ou bien encore Concept du monitoring à grande échelle avec S3 et EMR par Netflix. C’est parti pour l’article 2.2 de la série ! :o)

Accélérer la délivrance de son contenu via CloudFront et Route 53

Même si on passe beaucoup de temps à optimiser les backends, le ressenti utilisateur provient avant tout du frontend et de la manière de construire la page et de délivrer le contenu au browser. Le CDN (Content Delivery Network) est donc un outil très important de notre architecture pour apporter le contenu au plus près de l’utilisateur final.

Cet article sera articulé dans un mode lessons learned.

Les types de contenus :

  • Static – CSS/JS/images/… – High TTLs – URL type cdn.example.com pointant vers CDN ayant pour origine un storage comme S3 ou un serveur Web.
  • Re-Usable – Contenu personnalisé/API Calls/… – Low TTLs – URL type cdn.example.com pointant vers CDN ayant pour origine un serveur Web.
  • Dynamic/Unique – index.jsp – Zero TTL – URL type http://www.example.com pointant vers un serveur Web.

Cacher des URLs avec des paramètres comme des APIs Calls (Re-Usable) est une bonne idée, même pour quelques secondes et même si chaque jeu (combinaison) d’arguments génère un item caché différent pour un call sur un type de ressource : /maressource?a=1&b=2 est un item différent de /maressource?a=1&b=3. En effet, il faut prendre en compte le nombre de RPS (Requêtes Par Seconde) pour évaluer le bénéfice : on décharge l’origine du nombre de RPS x TTL (donc économie de ressources – bande passante, CPU, …) et on donne aux utilisateurs un meilleur rendu à partir du moment où le contenu est mis en cache pour la période (TTL), donc pour ceux après le premier utilisateur de chaque période.

Et pourquoi ne pas tout mettre derrière le CDN avec une URL type http://www.example.com pointant vers le CDN ayant pour origine un serveur Web ou un storage ? Y compris les contenus dynamiques générés à la volée par le serveur Web comme le point d’entrée du site (index.jsp, index.php, …). Nous allons voir cela.

Avantages de CloudFront :

  • Edge Locations permettant de rapprocher le contenu à délivrer du end-user.
  • Optimisations TCP/IP au niveau réseau via connexions persistantes (Keep-Alive).
  • Terminaison SSL (SSL offloading).
  • Prise en charge des verbes POST/PUT pour optimiser l’upload de données.
  • Latency Based Routing (via Route 53, dans le cas d’un déploiement multi-régions).
Waterfall Graphs

Waterfall Graphs

Overall Response Time

Overall Response Time

Analyse du temps de chargement :

  • Utilisation de Waterfall Graphs (extension standard dans les navigateurs) pour déterminer les étapes de chargement d’une ressource dans une page : DNS Lookup => TCP Connection => Time to First Byte => Content Download.

Trouver quels contenus cacher en priorité et détecter des comportements utilisateurs inattendus :

  1. collecter les access logs du serveur web,
  2. stocker les logs dans un storage adapté fonction de la volumétrie : par exemple, si on est sur la plateforme AWS, on peut utiliser RDS, DynamoDB, Redshift ou encore S3 (le stockage par bloc de logs peut être une bonne solution en cas de forte volumétrie),
  3. analyser les logs par divers moyens : requête SQL sur RDS, EMR à partir de données dans DynamoDB, Redshift ou S3,
  4. détecter des top queries.

Optimisation de la délivrance du contenu, même dynamique :

TCP Handshake

TCP Handshake

Improve DNS Time

  • Route 53… Je ne pense pas que Route 53 soit le principal facteur d’optimisation du DNS Time. Même si je ne doute pas de l’efficacité du service, ni de l’intérêt de l’utiliser en termes d’intégration avec CloudFront, ce dont je parle après, c’est avant tout le fait qu’il soit distribué dans les Edges Locations d’AWS qui devrait être mis en avant. Maintenant si vous utilisez un DNS encore plus près de vos end-users, cela fera de la distance en moins à parcourir à la requête de résolution « hostname => IP » et donc améliorera le DNS Time. A noter que le DNS Time dépend également de ce que vous avez renseigné au niveau du DNS (si vous avez configuré un plat de spaghetti au niveau des records, cela peut ralentir la résolution) et des TTLs configurés sur vos records. Maintenant Route 53 fait bien sont office, alors pourquoi ne pas profiter de ce service.

 

Improve TCP Connection

  • CloudFront avec les connexions persistantes (Keep-Alive) entre CloudFront et l’origine => TCP handshake (cf. image ci-contre) récurrent limité au dialogue entre end-user et CloudFront (donc sur de plus courtes distances).
  • Dans le cas d’une connection SSL, CloudFront prend en charge le SSL offloading avec son certificat ou le vôtre que vous pouvez importer : l’idée est encore d’effectuer ces opérations au plus près du end-user et de décharger l’origine des traitements récurrents comme le SSL offloading sur chaque connexion utilisateur. A noter qu’il est possible post CloudFront de continuer sur du HTTPS jusqu’à l’origine (si nécessaire pour du contenu sensible) ou bien de switcher en HTTP, dans le cas où le HTTPS en pré CloudFront avec été positionné uniquement pour éviter les erreurs du type « la page contient des éléments non sécurisés » dans une page délivrée en HTTPS.
TCP Slow Start

TCP Slow Start

Improve Time To First Byte

  • Autrement connu sous le nom de TTFB, c’est un bon indicateur qui inclut la latence aller/retour de la requête HTTP et le temps de génération de la page (dans le cas d’un contenu dynamique). Par rapport à l’explication fournie dans la présentation, mon avis est différent : pour moi, c’est le fait pour un contenu de pouvoir être caché (type Static et Re-Usable) qui améliore énormément le TTFB (moins de distance physique, sans parler de tous les hops au niveau réseau) et non la gestion du Keep-Alive TCP post CloudFront car le calcul du TTFB commence après l’établissement de la connexion TCP (optimisée), à l’envoi de la requête HTTP. Eventuellement le fait de décharger l’origine de la mise à disposition du contenu cachable peut permettre au serveur de concentrer ses ressources sur la génération du contenu dynamique (et donc d’améliorer la partie génération de la réponse du TTFB).

Improve Content Download Time

  • L’impact du TCP Slow Start (cf. image ci-contre) est minimisé du fait des connexions persistantes entre CloudFront et l’origine.
  • A noter dans ce point l’intérêt du Latency Based Routing (attention : dans ce cas le déploiement de votre site doit être multi-régions, car c’est la latence des services AWS au niveau de la région qui est testée et non celle de votre service), ou autres DNS offrant des capacités similaires, notamment pour les requêtes de contenu dynamique qui reviennent à chaque fois à l’origine.

Intérêts de Route 53

2 intérêts, orientés intégration avec les autres services AWS, que je vois d’utiliser Route 53 plutôt qu’un autre DNS (mais il y en a sûrement d’autres ;ob) :

  • Dans le cadre de la disponibilité plus que de la performance, l’intégration de health checks au niveau de Route 53 sur les points d’entrée (origines) de votre service qui sont couplés avec CloudFront. Le health check n’est pas soumis à une obligation de multi-régions puisque c’est le point d’entrée de votre service qui est testé et non les services (localisés) AWS qui le sous-tendent.
  • Les records ALIAS to A et ALIAS to AAAA qui peuvent remplacer les records A et AAAA et permettant de pointer sur l’alias d’un ELB en entrée de votre service puisqu’il ne faut évidemment pas pointer sur son IP qui change régulièrement.

Optimisation des requêtes PUT/POST :

  • Les données ne sont pas cachées et CloudFront relaie les données uploadées vers l’origine.
  • Les mêmes optimisations que celles pour le contenu dynamique s’appliquent (TCP Handshake & Slow Start entre CloudFront et origine via Keep-Alive et terminaison SSL proche du end-user).

Notes personnelles :

  • Il est important de vérifier, avant d’opter pour un CDN, les fonctions de demande d’éviction d’objets en cache avant la fin du TTL : c’est possible dans CloudFront, cependant il n’y a pas de prise en compte de Regex. Donc il faudra lister tous les objets explicitement : je crois que donner un « répertoire » (un ensemble d’objets avec le même préfixe) dans S3 est possible, mais pas de wildcard.
  • Penser également à versionner vos objets pour les montées de version lors des déploiements pour pouvoir faire cohabiter 2 versions et rollbacker le déploiement en cas de souci.
  • Concernant les prix, Amazon affirme que utiliser CloudFront n’induit pas de surcoût par rapport à la solution d’accéder tout le temps à l’origine. Au vu du système de facturation, cela peut être effectivement le cas, mais il vaut mieux toujours faire le calcul en fonction de votre use-case personnel et si c’est plus cher, voir si le surcoût est acceptable vis-à-vis du gain en expérience utilisateur (comprendre meilleur taux de transformation, …).

Frédéric FAURE @Twitter @Ysance

AWS re:Invent 2013 (2.1/2) : Use Cases – Créer son NAS sur AWS : NFS, CIFS & GFS (GlusterFS)

NAS sur AWS : NFS sous Linux

Cet article fait suite à la synthèse des sorties annoncées lors du summit AWS re:Invent 2013 qui s’est déroulé cette année du 12 au 15 Novembre à Las Vegas. Nous allons aborder dans cet article la mise en place d’un NAS (Network Attached Storage) sur une infrastructure AWS (VPC ou EC2 standalone) afin de partager des fichiers via le réseau selon les protocoles NFS (Network File System) pour Unix et CIFS (Common Internet File System) pour Windows. Nous mettrons dans ce cadre un cluster inter-AZs (Availability Zone/Zone d’Accessibilité) basé sur PaceMaker et DRBD. Nous aborderons une alternative basée sur GFS (GlusterFS) afin de mettre à disposition des instances EC2 de votre infrastructure vos fichiers de manière transparente et distribuée. Cet article se base sur une présentation très intéressante de Craig Carl (AWS).

Vous pourrez retrouvez également les autres articles de cette série consacrée au retour d’expérience du summit AWS en suivant ces liens Optimisation de son cluster EMR (Elastic MapReduce)Accélérer la délivrance de son contenu via CloudFront et Route 53 ou bien encore Concept du monitoring à grande échelle avec S3 et EMR par Netflix. C’est parti pour l’article 2.1 de la série ! :o)

UPDATE : Une erreur s’est glissée dans mon article. Suite à une remarque pertinente d’un lecteur, il s’avère que GFS != GlusterFS. GFS est le filesystem cluster de RedHat, tandis que GlusterFS (qui appartient aussi à RedHat) est un FS distribué. Je pensais que GFS était un acronyme condensé de GlusterFS et j’avais tort. Cet article traite donc bien de GlusterFS.

Créer son NAS sur AWS : NFS, CIFS & GFS (GlusterFS)

Tout d’abord avant d’entrer dans le vif du sujet, une question revient régulièrement : pourquoi AWS ne propose-t-il pas un NAS en standard dans leur offre ? La réponse des intéressés est que S3 peut répondre à 90% des use-cases en termes de partage de fichiers. Il reste néanmoins les 10% restants, que je croise régulièrement, qui utilisent des composants qui ne supportent pas en natif le dialogue avec S3, qui ne veulent pas investir dans le développement d’un plugin S3 spécifique pour leurs outils et qui ne veulent pas non plus utiliser en production des « émulations » de systèmes de fichiers de type s3fs basé sur FUSE pour des questions de performances et de stabilité (notamment dans les cas où les applications doivent modifier les fichiers directement sur le disque, ce qui n’est effectivement pas le cas « idéal » pour l’utilisation d’un object storage en backend). Il reste donc un certain nombre de cas où le NAS devient indispensable.

Les solutions proposées ci-dessous pour la mise en place d’un NAS ne résolvent pas pour autant un des problèmes inhérents au NAS et qu’il est bon de rappeler : la gestion des petits fichiers, plus petits que la taille des blocs utilisés. En effet, ils vont consommer de l’espace inutilement sur le disque, ne vont pas bénéficier de l’accélération due au striping car ne pourront être écrits/lus en parallèle sur plusieurs disques, … Donc c’est toujours le même problème.

Maintenant que les présentations sont effectuées, passons au vif du sujet ! :o)

Interfaces réseau et bande passante

Le premier élément à prendre en compte dans la mise en place d’un NAS est la bande passante des interfaces réseau de l’instance EC2 qui va supporter le partage des fichiers : il faut choisir un type d’instance qui va assurer un débit suffisant au niveau des interfaces IP publique et EBS.

NAS sur AWS : débit sur interface IP publique

NAS sur AWS : débit sur interface IP publique

NAS sur AWS : débit sur interface EBS

NAS sur AWS : débit sur interface EBS

Les images ci-contre indiquent les caractéristiques des différentes catégories d’instances en termes de bande passante :

  • Concernant l’interface publique, le pré-requis est de prendre au minimum une famille proposant des performances « moderate ».
  • Au niveau de l’interface avec les EBS, la famille doit avoir l’option EBS Optimized disponible : attention, cette option est au niveau de l’EC2 et est différente de la notion de Provisioned IOPS volumes (EBS). Il est d’ailleurs conseillé d’utiliser les 2 en même temps (EBS Optimized pour les instances EC2 & Provisioned IOPS volumes pour les volumes EBS).

EBS-optimized Instances

For a low, additional, hourly fee, you can launch selected Amazon EC2 instances types as EBS-optimized instances. EBS-optimized instances deliver dedicated throughput between Amazon EC2 and Amazon EBS, with options for 500 Mbps and 1000 Mbps depending on the instance type used. We recommend using Provisioned IOPS volumes with EBS-optimized instances or instances that support cluster networking for applications with high storage I/O requirements.

A noter que les instances, placées dans un même groupe de placement et supportant le Cluster Networking, peuvent profiter d’une bande passante importante de 10Gigabit, qui est partagée dans ce cas entre l’interface IP publique et l’interface EBS.

Pour plus d’informations sur les différents types d’instances EC2 disponibles, leurs caractéristiques et leurs options, vous pouvez suivre ce lien.

EBS Vs Ephemeral

Grand classique :

  • Ephemeral disks optimisés pour les sequential IOs,
  • Volumes EBS optimisés pour les random IOs.

De plus, les temps de latence au niveau des Ephemeral disks sont meilleurs. On en arrive à la conclusion qu’il faut stocker les données sur les disques éphémères. Il reste alors à ajouter une réplication DRBD asynchrone entre les disques éphémères et les volumes EBS pour assurer la durabilité des données. Les disques éphémères jouent alors le rôle d’un cache.

Les instances de type hs1, et également les nouvelles i2, sont tout à fait adaptées pour ce genre de use-cases avec leurs disques éphémères SSD et leur capacité en IOPs.

Comme précisé dans l’article précédent, il est à noter quelques tips utiles concernant les instances optimisées en IOPs et en particulier construites sur des disques SSD. Il s’agit de tips pour les hs1, mais qui ne manqueront pas de s’appliquer aux i2 :

  • Instance Store with HS1 Instances,
  • Disk Initialization (zero-fill),
  • Setting the Memory Limit (Linux-based Amazon Machine Images => CONFIG_XEN_MAX_DOMAIN_MEMORY),
  • Setting the User Limit (ulimit) (Linux-based Amazon Machine Images => default ulimit of 1024 to 2048).

Stars and Stripes

NAS sur AWS : Ephemeral Disks backed with DRBD to EBS

NAS sur AWS : Ephemeral Disks backed with DRBD to EBS

Maintenant pour améliorer les performances en IOPs, il faut créer un array de disques ou RAID striping de type RAID0, c’est à dire sans calcul de parité comme en RAID5, ni mirroring comme en RAID1. En effet, la durabilité des volumes EBS est déjà assurée par réplication réseau (3 fois) donc par du RAID1.

A noter que le striping doit être appliqué autant au niveau des disques éphémères que des volumes EBS pour bénéficier des avantages à tous les niveaux.

Cela va permettre :

  1. de pouvoir écrire et lire la donnée éclatée sur plusieurs disques en parallèle (donc des accès à la donnée plus rapides),
  2. d’avoir une meilleure bande passante générale « instance EC2 – volumes EBS », pour peu que l’on ait accordé la somme des bandes passantes des volumes EBS avec la bande passante de l’interface EBS de l’instance EC2,
  3. d’avoir plus de stockage disponible (si besoin au niveau des volumes EBS).

Pour agréger les volumes EBS, il a été proposé mdadm. Personnellement j’utilise LVM2 (Logical Volume Manager… 2). Tout est possible ! :o)

Précision concernant le snapshot d’un array de disques : il faut évidemment arrêter les écritures le temps d’initialiser le snapshot et, ce, sur tous les volumes EBS en même temps (sous peine d’avoir une incohérence au niveau des données sauvegardées). A noter que le fait d’effectuer un snapshot, même lorsqu’il est initialisé et que les volumes EBS sont à nouveau accessibles en écriture, ralentit les performances sur les accès : il vaut mieux donc effectuer les backups lors de « périodes creuses ».

Seconde précision concernant le nombre de volumes EBS à intégrer au RAID0 : cela dépend du type d’instance, mais on peut envisager 8 disques, voire 16 (au max) pour les plus grosses instances.

Multi-AZs

NAS sur AWS : NFS sous Linux

NAS sur AWS : NFS sous Linux

Il nous reste maintenant à prendre en compte la résilience de notre NAS au niveau d’une région en utilisant plusieurs AZs (Availability Zone ou Zone d’Accessibilité). Cela va être rendu possible à nouveau via DRBD pour la synchronisation des données, mais cette fois-ci de manière synchrone.

Pour plus d’informations sur les différentes options de mirroring de DRBD, vous pouvez suivre ce lien.

Il reste maintenant qu’en cas de défaillance du noeud principal, il faut que le noeud passif en hot standby prenne le relai ! C’est le rôle de PaceMaker que de monitorer l’état de santé du noeud primaire et de promouvoir le secondaire en cas de problème. Pour ce faire, il récupère l’ENI (Elastic Network Interfaces) de l’instance down et l’attache à celle qui conserve la donnée répliquée, dans le cas d’un VPC. Dans le cas d’une instance EC2 dans le réseau classique (en fait je dirais plutôt ancien, car maintenant le standard c’est le VPC), PaceMaker récupère l’IP publique (EIP) pour l’attacher à l’instance promue ! Pour customiser PaceMaker de la sorte, il faut ajouter un script dans les « ressources externe » de Pacemaker afin de définir l’action à réaliser, spécifique à la plateforme AWS. Ce script « doit être » ou « est déjà » mis à disposition par AWS suite au summit : surveillez les mises à jour de leurs ressources ! :o)

A noter que la réplication synchrone inter-AZs risque d’induire une latence, à surveiller donc pour valider que le NAS fonctionne bien de manière optimale.

NAS sur AWS : CIFS sous Windows

NAS sur AWS : CIFS sous Windows

Windows et DFS

Nous avons abordé pour l’instant le cas des environnements UNIX. Il est également possible de réaliser le même travail sous Windows avec DFS. Cela n’est apparemment possible qu’à partir de la version Windows Server 2012, même si il me semble que ce protocole était disponible dans les précédentes versions. Mais il doit bien y avoir une raison et cela dépasse mes compétences en MS.

GlusterFS

Configuration GFS

Configuration GFS

Reste maintenant l’option GFS, un système de fichiers distribué. Toute la « magie » de GFS réside dans le client : stateless, c’est lui qui va identifier les serveurs avec qui il doit dialoguer et rendre ainsi transparent pour l’utilisateur les échanges avec le cluster. Il est évidemment possible, lors de la définition des réplicas, de faire pointer vers des instances dans des AZs différentes pour assurer une résilience du système. Vous pourrez trouvez ci-contre quelques lignes de configuration pour la partie cliente et pour la partie serveur. Cette configuration est tirée de la présentation AWS citée en début d’article, comme le reste des illustrations.

Si la mise en place d’un GlusterFS ou d’une des solutions précédemment exposées vous semble trop complexe ou trop coûteuse par rapport à votre besoin, des éditeurs de solutions proposent des services managés basés sur ces technos, comme SoftNAS qui utilise plutôt les techniques de réplication DRBD/PaceMaker ou bien Zadara Storage qui propose une solution basée sur GFS (indications techniques données par notre présentateur, je n’ai pas eu le temps d’étudier les solutions en détail :o)). Une liste plus complète de fournisseurs de solutions NAS est disponible dans les slides linkés en début d’article. Cela peut être une piste si vous préférez ne pas avoir à gérer vous même votre NAS. Vous pouvez aussi aller voir directement les solutions des ISVs (Independent Software Vendors) disponibles dans la AWS Marketplace > Rubrique Software Infrastructure pour chercher ce qui vous intéresse.

Conclusion

Voilà donc un tour d’horizon des solutions possibles en termes de NAS. Il est vrai que, quand cela est possible, il est intéressant d’utiliser les services managés proposés par AWS, comme S3 pour ne pas le citer, et de bénéficier des avantages en termes de disponibilité, durabilité, … Mais quand la solution que vous construisez nécessite la mise à disposition de fichiers partagés entre vos instances et qu’elle n’est pas compatibles avec S3, ni avec du FUSE over S3, il faut utiliser un NAS : soit avec la bonne vieille méthode du DIY (Do It Yourself) et en acceptant les coûts induits en termes d’instances EC2/volumes EBS et de maintenance, soit en confiant la mise en place et l’exploitation du NAS à d’autres sociétés via des services managés basés sur ceux d’AWS.

Frédéric FAURE @Twitter @Ysance

AWS re:Invent 2013 (2.0/2) : Use Cases – Optimisation de son cluster EMR

Logo Hadoop

Cet article fait suite à la synthèse des sorties annoncées lors du summit AWS re:Invent 2013 qui s’est déroulé cette année du 12 au 15 Novembre à Las Vegas. Je me propose, dans cet article, de vous exposer quelques bonnes pratiques et retours intéressants glanés durant les sessions et échanges au niveau des stands du Central : ces retours supplémentaires portent sur des sujets bien précis comme Créer son NAS sur AWS : NFS, CIFS & GFS (GlusterFS)Optimisation de son cluster EMR (Elastic MapReduce)Accélérer la délivrance de son contenu via CloudFront et Route 53 ou bien encore Concept du monitoring à grande échelle avec S3 et EMR par Netflix.

Comme bien souvent, j’ai été un peu optimiste de vouloir faire un seul et unique article sur ces sujets. Comme il y a pas mal de choses à dire, je vais faire un article pour chacun des 4 retours afin de les exposer plus en détail, ce sera d’autant plus intéressant… Enfin je l’espère ! :o)

Optimisation de son cluster EMR (Elastic MapReduce)

2 présentations intéressantes de l’équipe Amazon suivies de quelques questions au stand Big Data AWS du Central me permettent de vous proposer cette synthèse.

Tout d’abord le cluster EMR se décompose en 3 couches distinctes :

  • le Name Node, point central (et accessoirement SPOF) du système HDFS, qui ne contient pas de données lui-même mais sait où elles sont stockées dans le système distribué.
  • Les Core Nodes qui stockent réellement les données dans le système HDFS.
  • les Worker ou Task Nodes qui effectuent les traitements distribués.

Optimisation des coûts

Architecture EMR : réduction des coûts (Spot instances)

Architecture EMR : réduction des coûts (Spot instances)

Architecture EMR : réduction des coûts (Reserved instances)

Architecture EMR : réduction des coûts (Reserved instances)

Tout d’abord, il faut décider si son cluster EMR est transient (temporaire) ou bien long running (permanent). Cela dépend de 2 facteurs :

  • Est-ce que la quantité de traitements à effectuer permet d’alimenter en permanence le cluster ? Auquel cas on peut envisager du long running, pour peu que la charge soit suffisamment lisse ou bien que l’on ne soit pas regardant sur la durée du traitement en cas de pics de traitements à effectuer.
  • Doit-on être capable d’effectuer des requêtes interactives (avec PigHive, …), donc d’interroger le système HDFS ? Auquel cas il faut également un cluster long running pour maintenir le système HDFS up.

A partir de ce moment on peut envisager :

  • Pour le cas du long running cluster, de prendre des Reserved instances pour la totalité du cluster si la quantité de travail à effectuer est constante ou bien uniquement pour les Name/Core Nodes et éventuellement une partie des Worker Nodes pour la charge de base avec un complément en Spot instances pour les pics.
  • Pour le cas du cluster transient, on peut se diriger sur des On Demand instances (voire Reserved fonction du % d’utilisation du cluster sur l’année) pour les Name/Core Nodes et des Spot instances pour les Worker Nodes avec éventuellement une base en On Demand/Reserved (toujours fonction du % d’utilisation du cluster sur l’année) si on souhaite conserver une capacité de traitement minimale durant l’exécution du cluster transient.
Architecture EMR : choix des types d'instance

Architecture EMR : choix des types d'instance

Optimisation des performances

L’optimisation des performances passe tout d’abord par le choix des instances : on préférera des instances optimisées en CPU ou en RAM pour les Worker Nodes fonction de traitement à effectuer, alors que l’on prendra des instances optimisées en IOPs (hs1, voire la nouvelle famille d’instances i2 qui fera un très bon office) pour les Core Nodes afin de répondre aux besoins du HDFS.

Ensuite, Amazon déconseille fortement d’utiliser un EBS pour stocker les données des Cores Nodes dans le système HDFS. En effet, les EBS sont orientés random IOs contrairement aux Ephemeral disks orientés sequential IOs. Les EBSs ne sont donc pas efficaces pour le stockage HDFS, quand bien même ils assureraient la durabilité des données. Ce sont donc les Ephemeral disks qui sont conseillés (surtout quand on bénéficie des SSDs d’une hs1 ou i2).

Comme précisé dans l’article précédent, il est à noter quelques tips utiles concernant les instances optimisées en IOPs et en particulier construites sur des disques SSD. Il s’agit de tips pour les hs1, mais qui ne manqueront pas de s’appliquer aux i2 :

  • Instance Store with HS1 Instances,
  • Disk Initialization (zero-fill),
  • Setting the Memory Limit (Linux-based Amazon Machine Images => CONFIG_XEN_MAX_DOMAIN_MEMORY),
  • Setting the User Limit (ulimit) (Linux-based Amazon Machine Images => default ulimit of 1024 to 2048).
Architecture EMR : utilisation de S3 et de HDFS

Architecture EMR : utilisation de S3 et de HDFS

S3 et/ou HDFS

Comme évoqué précédemment, Amazon déconseille d’utiliser un EBS pour stocker les données des Cores Nodes dans le système HDFS. La préconisation est donc de sauvegarder régulièrement les données stockées sur HDFS sur S3 à la fin du traitement ou bien lors d’un checkpoint entre 2 traitements. Il sera donc possible de repartir du résultat stocké sur S3 pour un traitement ultérieur.

Pour effectuer des requêtes interactives avec Pig ou Hive, il est nécessaire d’avoir les Core Nodes up and ready pour utiliser le système HDFS. Pour ceux qui ne connaissent pas ces 2 outils, Hive (via un langage HiveQL ressemblant au SQL) et Pig via un langage plus procédural/fonctionnel, vont extraire les données de HDFS et générer les jobs MapReduce correspondant à votre expression, puis lancer le traitement. C’est une surcouche vous permettant d’exprimer les traitements MapReduce souhaités dans un langage qui vous est plus familier.

On peut garder à l’esprit le service AWS Data Pipeline pour le déplacement des données en entrée et en sortie de EMR. Il vous permettra de déplacer les données entre différents services AWS (S3, RDS, DynamoDB et EMR) et également bases de données on-premise (sur site) via JDBC par exemple. On peut considérer Data Pipeline comme un équivalent de Amazon Simple Workflow Service (SWF, service d’orchestration de tâches et de gestion d’états entre applications), mais pour les données.

Customisation

Finalement,  il est possible de customiser son cluster EMR via une tâche de bootstrap disponible. Elle permet d’installer les outils qui vous sont nécessaires, voire même de charger une distribution MapReduce différente de celles proposées par le service EMR. On perd un peu de la simplicité du service managé, mais cela permet de pouvoir adresser plus de cas.

Frédéric FAURE @Twitter @Ysance

AWS re:Invent 2013 (1/2) : News

AWS re:Invent 2013

Le summit AWS re:Invent 2013 s’est déroulé cette année du 12 au 15 Novembre à Las Vegas. Ces 4 jours ont été l’occasion d’assister à la mise à disposition de plusieurs nouveaux services (Amazon Kinesis, Amazon CloudTrail, Amazon WorkSpaces et Amazon AppStream), de nouvelles fonctionnalités majeures dans des services existants (support du moteur PostgreSQL dans RDS, Cross Region Read-replicas pour RDS, Global Secondary Indexes dans DynamoDB, Identity Federation avec SAML 2.0 et Redshift Snapshot Copy) et pour finir de nouveaux types d’instances EC2 (c3 dans la gamme super computing et i2 dans la gamme disques hautes performances).

Le summit a été également l’occasion d’assister à de nombreuses sessions proposées par les équipes AWS, par des clients ou bien par des partenaires présentant leurs solutions basées sur AWS. Il a également été possible d’échanger sur les stands du Central avec les éditeurs de solutions et intégrateurs gravitant autour de l’univers AWS.

Je me propose, dans cet article, de revenir sur les nouveautés dévoilées durant ce summit AWS re:Invent 2013. Je proposerai, dans un ou plusieurs articles ultérieurs, quelques bonnes pratiques et retours intéressants glanés durant les sessions et échanges au niveau des stands du Central : ces retours supplémentaires porteront sur des sujets bien précis comme Créer son NAS sur AWS : NFS, CIFS & GFS (GlusterFS), Optimisation de son cluster EMR (Elastic MapReduce)Accélérer la délivrance de son contenu via CloudFront et Route 53 ou bien encore Concept du monitoring à grande échelle avec S3 et EMR par Netflix.

Les sorties de Noël

Les services

  1. Amazon CloudTrail propose un composant de sécurité supplémentaire afin de tracker l’activité des utilisateurs du compte au niveau des appels d’APIs qu’ils effectuent. Accédez ici à une explication plus détaillée du service et de ses fonctionnalités.
  2. Amazon WorkSpaces offre la possibilité d’accéder un environnement de travail virtuel stocké dans le Cloud et accessible par de multiple devices via un client léger. Accédez ici à une explication plus détaillée du service et de ses fonctionnalités.
  3. Amazon AppStream cible les développeurs d’applications mobiles pour faciliter le portage de leurs jeux, applications graphiques HD, … en les streamant depuis le Cloud vers les devices des utilisateurs. Ce nouveau service s’appuie sur la récente famille d’instances g2 mise à disposition par Amazon. Accédez ici à une explication plus détaillée du service et de ses fonctionnalités.
  4. Amazon Kinesis, voir en détail ci-après. :o)

Les fonctionnalités

  1. Support du moteur PostgreSQL dans RDS.
  2. Cross Region Read-replicas pour RDS.
  3. Global Secondary Indexes dans DynamoDB.
  4. Identity Federation avec SAML 2.0. A noter que la fédération d’identité est la possibilité de gérer des identités dans son propre référentiel (LDAP, Active Directory, …) et de générer des tokens temporaires (autorisant un accès granulaire aux services/ressources AWS) avec les droits découlant de notre référentiel en utilisant le Security Token Service (STS). Une fédération d’identité est également possible avec les identity providers externes suivant : Amazon.com, Facebook ou Google.
  5. Redshift Snapshot Copy.
AWS Trusted Advisor

AWS Trusted Advisor

Les nouvelles familles d’instances

  1. c3 dans la gamme super computing.
  2. i2 dans la gamme disques hautes performances (IOPs). A noter quelques tips utiles concernant les instances optimisées en IOPs et en particulier construites sur des disques SSD. Il s’agit de tips pour les hs1, mais qui ne manqueront pas de s’appliquer aux i2 :
  • Instance Store with HS1 Instances,
  • Disk Initialization (zero-fill),
  • Setting the Memory Limit (Linux-based Amazon Machine Images => CONFIG_XEN_MAX_DOMAIN_MEMORY),
  • Setting the User Limit (ulimit) (Linux-based Amazon Machine Images => default ulimit of 1024 to 2048).

Focus

Un focus particulier a été porté sur :

  • Les partenaires, voir le point dédié ci-après.
  • Le souci de la relation client, notamment avec le AWS Trusted Advisor qui a été updaté pour l’occasion et qui continue de fournir de précieuses informations au client pour qu’il puisse améliorer la gestion de son compte, que ce soit en termes d’optimisation des coûts, de sécurité, de tolérance à la panne ou bien de performance.

Kinesis

Amazon Kinesis

Amazon Kinesis

Kinesis est un nouveau service proposé par Amazon qui met à disposition un système de messages distribué à haut débit de type publish/subscribe. Le concept général est de poster des messages dans une stream afin de pouvoir les récupérer de manière asynchrone au niveau d’un pool de workers (par exemple des instances EC2 placées dans un auto-scaling group) afin de les consommer et de les traiter. Le service est distribué dans 3 AZs (Availibility Zones ou Zones d’Accessibilité) pour assurer la disponibilité du service ainsi que la durabilité des messages. Kinesis propose un concept similaire à Apache Kafka, mais sous la forme d’un service managé et non pas d’un produit à installer/configurer/exploiter soi-même. Vous trouverez de nombreux détails techniques sur Kinesis sur le site Amazon Web Services Blog.

Cinématique

Voici la cinématique d’utilisation de Kinesis :

  1. On crée une stream et on définit le nombre de shards associés (plus une stream aura de shards, plus elle pourra prendre en compte de messages en termes de débit).
  2. On (producer) adresse un message au service en lui indiquant la stream, la clé de partition et le contenu du message (data blob). La clé de partition est hashée et le résultat utilisé pour sélectionner le shard à utiliser.
  3. Un numéro de séquence est généré lors de l’ingestion du message.
  4. On (consumer) peut ensuite récupérer le message du shard de la stream en récupérant un itérateur (GetShardIterator) en lui indiquant son point de départ (AT_SEQUENCE_NUMBER, AFTER_SEQUENCE_NUMBER, TRIM_HORIZON ou LATEST) et… en itérant dessus (GetNextRecords).

A noter qu’il est possible de spliter/merger les shards d’une stream (en cours d’utilisation) pour en augmenter/réduire le débit.

Kinesis Vs SQS

La première question que je me suis posée au niveau de ce nouveau service est « quelle différence avec SQS (Simple Queue Service) qui semble couvrir le même besoin ? ». J’ai déjà utilisé SQS de la sorte : publication des messages dans SQS et consommation/traitement de manière asynchrone par un pool de workers qui, éventuellement, peuvent être intégrés dans un auto-scaling group déclenchant la scaling policy quand le CPU du groupe remonté par CloudWatch dépasse un certain seuil.

Persistance des messages

La première différence notable est la persistance des messages qui sont ingérés par Kinesis. La consommation du message par un worker n’entraîne pas sa disparition. Le message est supprimé au bout d’une période de rétention de 24 heures. Les messages peuvent donc être traités plusieurs fois, par différents worker ou même par le même worker. Cela est rendu possible via le numéro de séquence du message.

Attribution d’une séquence

Un numéro de séquence est attribué au message lorsqu’il est ingéré, garantissant son ordre de délivrance au(x) consumer(s). Cela permet également de décider pour chaque consumer à partir de quel message on souhaite itérer.

Pricing

Le prix est basé sur un coût horaire par shard (capacité de la stream) et sur le nombre de PUTs.

Fault Tolerance Support in KCL (Kinesis Client Library)

La librairie cliente

La librairie cliente (JAVA uniquement) fournie par Amazon est conseillée plutôt que d’appeler directement l’API car elle prend en compte un nombre certain de fonctionnalités : fail-over, recovery, load balancing, … A noter que la librairie crée et utilise une table de locks dans DynamoDB pour gérer les aspects précédemment cités.

Concernant le langage actuel de la librairie, un certain nombre de demandes pour l’équivalent Python et Ruby ont déjà été émises lors des sessions ! ;o)

Il est conseillé pour une question de disponibilité de positionner les workers, sur lesquels va être installée la librairie, dans plusieurs AZs (via un auto-scaling group par exemple).

Les Partenaires

Les partenaires ne sont pas en reste sur ce summit ! On note la mise en avant de 3 éléments importants :

AWS Solutions Architect Associate

AWS Solutions Architect Associate

  • Un focus a été porté sur l’APN (AWS Partner Network) qui a été créé « assez récemment » et qui permet d’accéder à un ensemble de fonctionnalités permettant de gérer la relation Amazon – partenaire. Il comprend notamment une partie formation et certification. J’en profite au passage pour présenter le nouveau logo que je viens de recevoir ! ;o)
  • La mise en avant de la Marketplace et son intégration dans la console AWS.
  • La mise en avant de AWS Test Drives permettant de mettre à disposition des labs respectant un formalisme bien défini avec les ressources associées (vidéo, AMI, …) qui seront exécutées sur le compte du partenaire (donc gratuit pour l’utilisateur de la démo qui aura uniquement à saisir ses informations de contact). L’événement a été l’occasion d’annoncer l’ajout de nombreux labs pour mettre en avant les produits/services des éditeurs partenaires et/ou le savoir faire des intégrateurs partenaires.

Frédéric FAURE @Twitter @Ysance