Dejte lidem data a nechte je tvořit

… aneb jak v Liftagu dělá BI 2/3 firmy.

V Liftagu aktuálně hledáme třetího BI specialistu. Přitom pár měsíců dozadu BI v Liftagu prakticky neexistovalo. Možná někoho zaujme, nebo třeba i inspiruje, příběh tohoto boostu.

Známá poučka říká, že bychom lidem neměli dávat ryby, ale raděj je naučit ryby chytat. Naše myšlenka byla taková, že místo statických dat či reportů, bychom mohli lidem dát hřiště na hraní.

V mladých firmách je autonomnost a schopnost rozhodování naprosto zásadní aspekt úspěchu. Neustále totiž hledáte nový směr. Podpůrnou roli v průkopničení hrají data.

Reporting

V Liftagu používáme Tableau. Každé ráno v něm naši datoví nadšenci najdou počet jízd za předchozí den, konverzi, nové pasažéry a všechny vymazlené metriky, které jsme za ta léta vyrobili. Prostě reporting.

Dlouho jsme si tak vystačili s následujícím stackem:

Primární data - Mongo, MySQL -> processing tool -> visu tool

V jistou chvíli nás ale začaly limitovat umělé hranice, které jsme si do celého flow zakomponovali. To bude předmětem tohoto článku.

Liftago není jen appka, ve skutečnosti je v engineeringu jen jedna třetina celé firmy. Zbytek zajišťuje řidiče, pasažéry, business klienty. S oblibou kandidátům říkám, že je to end-to-end business. Naprogramovat věci není lehké, ale často je složitější věci vymyslet a vyladit.

Naše kultura je taková, že chceme, aby na našem produktu participoval každý. Nápadů máme plný backlog již léta, prioritizace je často oříškem. Představte si, že jako zběhlý engineer máte podezření, že vás napadla krásná nová featura. Ověřit si ideu v datech je základ, protože nechcete mít prázdné ruce, ale naopak držet argumenty.

Klasický přístup ověření spočívá v kontaktování BI týmu, aby vám začal do Tableau počítat novou meriku či vytvořil nějaký board, jenž by vám pomohl s argumentací. To často narazí na dva problémy:

  • BI je typicky zahlcené mnoha prioritami, které měly být již včera hotové
  • protože vy jste owner nápadu, nikdo vám moc nevěří a tak v tom často nevidí prioritu

A tak celý nápad putuje do backlogu.

Ohh wait. A co kdybyste si ty metriky do Tableau vyrobili sami? 
To by zkrátilo cyklus od nápadu k realizaci na minimum.
No dobře, pojďme se podívat, co by to obnášelo, a jak by to bylo složité.

Bariéry a kameny úrazu

Celý tenhle příběh bariér jsme si začali uvědomovat dva roky dozadu, kdy jsme měli GoodData, náš předchůdce Tableau. Je to jistě skvělý kus softwaru. Bohužel postavený nad svým vlastním vnitřním modelem. Ten model zajišťuje hladší práci při visualizaci.

Jenže tenhle doménově-datový model přidává další vrstvu složitosti. Implementují vám ho lidé z GoodData. Jeho změnu určitě dokážete udělat i vy, ale je to velmi složité. Naprosto to odporuje potřebě startupu a rychlých změn nad daty. Nakonec to byl hlavní důvod proč zavedené věci měnit.

Muset kvůli změně struktury dat volat konzultanty jsme si nemohli dovolit.

Nově použité Tableau sice žádný doménový model nemá, ale instalovat si pro změny v reportech speciální desktopovou aplikaci, není také žádná hitparáda. V době SaaS aplikací? Kor když chcete dělat jednoduché věci. Časem šly určité změny dělat i na webu, ale přirozená křivka odporu byla již tehdy veliká.

To je první velká bariera. Ikdyž byste tohle překonali, nemáte vyhráno.

V dalším kroku potřebujete přesvědčit Keboolu, abyste do té visualizační tooly ty data dostali. Keboolu máme všichni rádi. Zjednodušuje věci.

Jenže když požádáte vašeho BI kolegu, aby vám ukázal váš graf transformací, zaručuju vám, že vás ten obrázek k smrti vyděsí. Jako děti čert. Já si na ten moment přesně vzpomínám. Ztratil jsem všechny iluze, že tu změnu dokážu udělat já sám v krátkém čase.

Naštěstí tenhle problém není neřešitelný. Věci je potřeba refaktorovat. Ať je to kód nebo transformace. Nicméně vrásky na čele vám to přidá.

Poslední kámen úrazu na vás čeká ve vašich datech – primárním zdroji dat. S ním nechcete dělat vůbec nic, pouze mu dobře porozumět. V případě monga je skrytá komplexita jinde než byste čekali. Mongo nevyžaduje z principu žádné pevné schéma, takže set fieldů vašich entit se historicky vesele měnil. Vytáhnout vše najednou je nemožné. Proto musíte reinkarnovat vývojáře, kteří na tom historicky pracovali, a zjistit jak se věci měly. Potom se vám podaří vytáhnout všechno.

A teď si představte, že nejste engineer, ale méně technicky zdatný člověk. Máme jich 2/3 firmy. Ten nemá vůbec žádnou šanci něco změnit, něco ověřit.

Hledali jsme cestu jak z toho ven.

MapD z pytle ven!

Byly časy, jasně si na ně vzpomínám, kdy jsem se snažil prorazit výše uvedenou cestu. Měli jsme už Snowflake. Ten splňuje vše co žádám: velmi snadno dosáhnete svého. V té době jsem si v zoufalství stavěl reporting nad Snowflakem tak, že jsem agregovaná data tahal do excelu a tam si dělal grafy. Šlo to totiž udělat velmi rychle.

Jednoho dne ale přišel náš kolega, říkejme mu třeba Tibor, s localhost instancí nástroje MapD. Teď se jmenuje OmniSci.

Tenhle moment způsobil datovou revoluci v Liftagu.

MapD totiž umožňovalo naprosto zásadní pokrok. Prerekvizitou pro něj byl jen jeden krok:

  1. Nahrát do něj CSV soubor s daty, miliony řádků, klidně 200 sloupců jako máme u objednávek my

Nic víc nepotřebujete. V MapD totiž umí něco skvělého.

Za real-time si visuelně nad těmi všemi daty vytváříte vlastní reporty. Přidáváte grafy, pie, histogramy. Počítáte metriky SQLkem. Visualizace je radost.

A teď si představte, že výsledky vaši hypotéz vidíte instantně teď hned!

Měli jste nápad? Sem s ním. Chcete vědět, co se dělo včera ve večerní špičce?

Vezměte si histogram požadavků, omezte si ho na včerejší čas. Máte podezření, že bylo v ostravském centru málo řidičů? Naklikejte si jejich počet do grafu vedle. Jaká byla struktura jejich chování? Hodtě si vedle pie chart.

Měli jste podezření? Za deset minut máte jasno. Visuelně.

Je to dobrý, jo?

Je to skvělý!

  • Během pár minut víte výsledek, sami
  • Vše děláte visuelně, takže nepotřebujete umět programovat – zvládnou to i ty 2/3 firmy
  • MapD zvládá miliony řádků, takže neřešíte performance, indexy atp.
  • V grafech můžete, ale nemusíte, přidávat SQL – je to vhodné i pro složitější věci

V MapD si doteď v Liftagu naklikalo report už hodně lidí. Od té doby slyším po kuchyňkách, ne-engineeringových kanclech a často i hospodách, lidi mluvit jak věci řeší a hledají v datech. Že chtějí příští sprint něco vyrobit, protože data.

Předtím jsme si říkali, jak chceme být data driven. Opravdový přístup k datům mělo jen pár nadšenců. Teď ho mají všichni. Teď konečně fungujeme  data-driven.

Protože jsem se dost rozepsal, v navazujícím článku ukážu jak přesně to děláme.

Http and TCP Load Balancing with Kubernetes

Kubernetes allows to manage large clusters which are composed of docker containers. And where is large computation power there is large amount of data throughput. However, there is still a need to spread the data throughput so it can reach and utilize particular docker container(s). This is called load balancingKubernetes supports two basic forms of load balancing. Internal among docker containers themselves and external for clients which call you application from outside environment – just users.

Internal Load Balancing with Kubernetes

Usual approach during the modeling of an application in kubernetes is to provide domain models for pods, replications controllers and services. Look at the great documentation if you are not aware of all these principles.

For simplification, pod is a set of docker containers which are always located on the same node. Replication controller allows and guarantees scalability. The last but not least is the service which hides all instances of pods – created by replication controller – behind a facade.

The concept of services brings very powerful system of internal load balancing.

Look at the picture above. Service A uses Service B. Common pattern is to provide some service discovery so some service A can look up some instance of service B’ endpoint and use it for later communication. However, service B has it’s own lifecycle and can fail forever or service B can have multiple running instance. Thus, a collection of instances of service B is changing during the time.

You need some boilerplate code to handle the algorithm describe above. Ohh, wait. Kubernetes already contains similar system.

Let say, service B has 3 pods – running docker containers. Kubernetes allows you to introduce one instance of service B to hide these three pods. In advance, it gives you one virtual IP address. Service A than can use this virtual IP address and kubernetes proxying system does the routing to a specific pod. Cool isn’t it?

The routing supports sticky session or round robin algorithm. It depends. The last thing which is up to you is to have clever client for your service, e.g. implemented with cooperation of hystrix, so it does re-trying or handle timeouts correctly.

External Load Balancing using Kubernetes

This type of load balancing is about routing the traffic from source clients to target proper pods. You have multiple options here.

Buy AWS/GCE Load Balancer

The load balancing and the networking itself is difficult system. Sharing public IPs, protocols to resolve hostnames to IPs, to resolve IPs to mac addresses, HA of LB and so on. It makes sense to buy load balancer as a service if you deploy your app in some well known cloud provider.

Thus, the services support load balancing type. Kubernetes then uses cloud provider specific API to configure the load balancer.

This is the simplest way. But what are your other options if you are not so lucky and you can’t do that? You have to deploy your own load balancer. Let’s review existing load balancers which can be deployed on premise.

Kube-proxy as external load balancer

I’ve already described the mechanism how kubernetes supports internal load balancing. Yeah! Is it possible to use it for external load balancing? Yes but there are some limitations.

Kube-proxy does exactly what we want but it’s very simple tcp load balancer – L4. I’ve detected some failures during some performance testing from external clients. The proxy just sometimes refused the connection or did not know target address of particular host and so on. However, there is no way how to configure kube-proxy, e.g. to setup some timeouts. Or there is also no way – as it’s TCP load balancer – how to do re-tries on http layer.

Kube-proxy also suffers by performance issues but Kubernetes 1.1 supports native IP tables so huge progress has been made during the recent time.

These all are reasons why I recommended to have clever service client within the internal load balancing section at the beginning. So I would still recommend to use kube-proxy as an internal load balancer but use some solid external load balancer implementation for external load balancing.

Kubernetes Ingress

New version 1.1 of the system introduced ingress feature. However, it’s still beta.

Ingress is a new version of kubernetes resource. It’s API which allows you to define certain rules where to send a traffic. For example you can re-route a traffic from path http://host/example to a service called example. It’s pretty simple.

Ingress itself is not load balancer. It’s just a domain model and a tool to describe the problem. Thus there must be a service which absorbs this model and which does the load balancing but Ingress just helps you as a abstraction. Kubernetes team already developed ha-proxy implementation for this load balancing service.

Service Load Balancer

There is a project which exist from earlier kubernetes times called service load balancer. It’s under development for a year. It’s probably predecessor of Ingress. The load balancer also uses two levels of abstraction and any existing load balancer can stand as an implementation, ha-proxy is the only current implementation.

Vulcand

Vulcand is a new load balancer. It’s technology is compatible as the base configuration is stored within etcd – this allows to utilize watch command. Note that vulcand is http-only load balancer, tcp belongs among requested features. It’s also not integrated with kubernetes but here is a simple tutorial.

Standalone HAProxy

In fact, I have finally ended with developing of my own load balancer component which is based on existing system. It’s pretty easy.

There is a kubernetes java client from fabric8, you can use it to watch the kubernetes API and it’s then up to you which existing load balancer you want to leverage and which kind of config file you will generate. However, as it’s simple there are already some projects which do the same:

Standalone Nginx

Nginx is similar technology to HAProxy so it’s easy to develop a component to configure Nginx load balancer as well. Nginx plus seems to support directly kubernetes but it’s a service.

Validating nginx config file using docker approach

I try to setup nginx as a load balancer. The configuration is just a file with a lot of constrains so I need a validation. There is no online validation service, as e.g. CoreOS has, and I don’t want to install nginx on my laptop as I work on a distributed app.

Docker is right approach for me. Let say I have following config:

https://gist.github.com/MartinPodval/851b574da259abb33163.js
In short, I’m going to pass nginx config to running nginx instance and look to the logs.

Put you nginx.config to the temp and start the docker image:

sudo docker run –name nginx -v /tmp/nginx.config:/etc/nginx/nginx.conf:ro -d nginx

It uses volume mapping so the command just starts a new docker container and mounts a local /tmp/nginx.config to the given in-container path. You can obviously change the volume path to your personal path. Is it working or not? Look at logs.

sudo docker logs nginx

If there is no entry, your file is fine. In the case of an error, you can see something like this:

2016/01/08 11:37:31 [emerg] 1#1: unexpected “}” in /etc/nginx/nginx.conf:44
nginx: [emerg] unexpected “}” in /etc/nginx/nginx.conf:44

Note that if you want to validate the file again just stop, remove and then start container again, e.g.:

sudo docker stop nginx
sudo docker rm nginx

Building go-lang project /Kubernetes Ingress/ from scratch with no go experience

I have been working with Kubernetes and I wanted to build it’s contrib yesterday. However, nginx implementation of Kubernetes’ Ingress is written in go-lang. Even though I needed to change some const string, it required recompilation.

Go is not java, and go building system is not maven. Setting up the environment was not straightforward. I was facing couple troubles but I’m going to take it from the beginning. My laptop uses ubuntu 15.04 – well 15.10 since 9pm 🙂 – and I have never installed go lang yet.

Go lang installation on Ubuntu

First of all, you need to install go lang. You can use official repo, but it contains older version 1.3. However, do not install it using apt-get as kubernetes or it’s dependencies require higher version of go-lang. Of course, I originally installed version 1.3  but some fatal error occurred later. It forced me do to the manual installation anyway.

Here is simple tutorial.

https://gist.github.com/MartinPodval/948818a0635ba0759324.js
Last two lines affect only the current terminal. You should update /etc/environment in the case you consider to use go-lang in the future.

Then, you can try to get version of go via:

$ go version
go version go1.5.1 linux/amd64

Go-lang compilation and building

Now, you can pull your git project, e.g.:

git clone https://github.com/kubernetes/contrib


There is a build command in go-lang. You can type it into you terminal:

https://gist.github.com/MartinPodval/998be55bf77f45b4703a.js
What’s wrong? We have not setup GOPATH yet. This directory is something like maven cache. It’s pretty simple, you can just point the variable to your home, e.g.

export GOPATH=/home/martin/.go

Try to build it again. There are no errors, but nothing is happening. I found in some stackoverflow discussion that there is a get command as well. Unfortunately, build is consist of get anyway. Then I found -v as verbose.

https://gist.github.com/MartinPodval/cbe92056b842356d1b35.js
Actually, we are on right track. The fetching of dependencies takes a time so it seemed that go build or go get did not work but that’s not true.

That’s all, now building should work:

https://gist.github.com/MartinPodval/d10c864185bc2ce91ffe.js

Go-lang 1.3 vs 1.5

I just wanted to discuss why to prefer manual installation over ubuntu repository. For the record. Building kubernetes project using go-lang 1.3 from ubuntu official repo failed:

https://gist.github.com/MartinPodval/6b60130ae64dfbfceb32.js