maanantai 11. heinäkuuta 2016

Monitor your applications and servers with Telegraf, InfluxDB and Grafana

Grafana with graphs from cpu, memory, mongodb

Telegraf

I stumbled upon a nice go project Telegraf which saves metrics from various sources to InfluxDB among others. I already had InfluxDB running so I gave it a shot.

There are two servers, Babylon, the internet facing and an other one named Black Pearl. Latter one is the Kubernetes master for a Raspberry Pi cluster. Installing Telegraf is easy enough.

Installation instructions from packages: https://github.com/influxdata/telegraf

I want to install this on docker images too so I did it like this:
sudo mkdir -p /work
cd /work
sudo curl -LO https://dl.influxdata.com/telegraf/releases/telegraf-1.0.0-beta2_linux_armhf.tar.gz
sudo tar xvzf telegraf-1.0.0-beta2_linux_armhf.tar.gz
cd telegraf
sudo cp etc/logrotate.d/* /etc/logrotate.d/
sudo mkdir -p /var/log/telegraf
sudo cp usr/bin/telegraf /usr/bin
sudo bash -c "telegraf -sample-config -input-filter cpu:mem:net:swap:mongodb -output-filter influxdb > 
/etc/telegraf/telegraf.conf"
sudo mkdir -p /etc/telegraf
sudo bash -c "nohup telegraf > /var/log/telegraf/telegraf.log 2>1 &"

On the raspberry pi, I also had to change the config line a bit,
cpu:mem:net:swap:mongodb -> cpu:mem:net:swap
since there is no mongodb on that instance.

Grafana

Grafana is a tool for making graph dashboards. Installing Grafana is easy. Adding a datasource is done straight from the UI, no need for config handling.
Adding a graph to a dashboard is easy too.
The main tab adding the graph is Metrics. You add the query there.

With Telegraf it's easy to get all metrics to a database and from there on using Grafana is a breeze.

Adding InfluxDB datasource to Grafana: http://docs.grafana.org/datasources/influxdb/
Telegraf: https://github.com/influxdata/telegraf
InfluxDB: https://influxdata.com/time-series-platform/influxdb/

sunnuntai 19. kesäkuuta 2016

Cache on the background with golang

Caching based on requests

Usually caches on the web are made so that the cache is filled with requests coming in from the browser. Positive about this is that you cache the most used requests and not the ones which are not used. The downside is that in case of errors or timeouts, you really have to think what to do. And what about expiration and the size of the cache?

Caching on the background

If you know what to fetch and you know the requests what to get before hand, you can easily schedule the cache fetching. Although I'm not using a popular cache like LRU here, it's certainly possible.

A simple example

This is a simple example for a cache. You can get, add and remove items from the cache.
package main

import (
    "github.com/streamrail/concurrent-map"
)

var cache cmap.ConcurrentMap

func init() {
    cache = cmap.New()
}

func AddItemToCache(key string, value string) {
    cache.Set(key, value)
}

func GetItemFromCache(key string) string {
    if cache.Has(key) {
        if tmp, ok := cache.Get(key); ok {
            return tmp.(string)
        }
    }
    return ""
}

func removeItem(key string) {
    cache.Remove(key)
}

Let’s get weather information on the background.
func fetchWeather() *Weather {
    var w Weather
    resp, err := client.Get(weatherURL)
    if err != nil {
        fmt.Println(err)
        return nil
    }
    defer resp.Body.Close()
    buf, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Println(err)
        return nil
    }
    if err = json.Unmarshal(buf, &w); err != nil {
        fmt.Println(err)
    }
    return &w
}

Marshal it to json string
func weatherToCache() {
    fmt.Println("fetching weather")
    if w := fetchWeather(); w != nil {
        j, err := json.Marshal(&w.Query.Results.Channel.Item)
        if err != nil {
            fmt.Println(err)
        } else {
            AddItemToCache(weatherURL, string(j))
        }
    }
}

and schedule fetching it
func weatherSchedule() {
    for range time.Tick(time.Hour * 1) {
        weatherToCache()
    }
}

In main function We use iris to get the cached weather information after we put the weather scheduling to run on the background.
func main() {
    weatherToCache()
    go weatherSchedule()
    iris.Get("/weather", func(c *iris.Context) {
        c.Text(200, GetItemFromCache(weatherURL))
    })
    iris.Listen(":8080")
}

Used or mentioned dependencies
iris-go.com
Concurrent map
LRU cache

See the whole project at https://github.com/jelinden/go-background-cache-example

sunnuntai 22. toukokuuta 2016

Testing with Golang

Coming from a Java background, testing with Go has been a struggle from the beginning for me. For simple unit testing, there are simple enough guides for doing it, but what about the more complex cases? What if I use a library like Echo, should I test what a middleware function returns? How do I mock data needed by the function?

And there’s of course the simplicity of Go, everything is so small and nice, should I even test the smallest functions? I’ll share some of my experiences and thoughts about testing in Go.




Unit testing

Here's a function which drops everything that is not a letter or a number:
func validate(param string) string {
 r, err := regexp.Compile("[a-zåäöA-ZÅÄÖ0-9]+")
 if err != nil {
  fmt.Fatal(err)
 }
 return string(r.ReplaceAll([]byte(param), []byte(""))[:])
}

Testing this is easy enough:
func TestValidateSuccess(t *testing.T) {
    value := validate("foo bar ! 1 2[]()><")
    if value != "foobar12" {
        t.Errorf("Validate result was not 'foobar'")
    }
}

One thing to notice is that if you make all functions to return values, it's really easy to test. If you modify values without returning them, testing is a bit harder job to do (At least for me).

There are extensions to testing package also. For example testify (https://github.com/stretchr/testify).
With it you can easily do assertions with a nice syntax. It also supports mocking.
func TestValidateSuccess(t *testing.T) {
    assert := assert.New(t)
    assert.Equal(validate("foo bar ! 1 2[]()><"), "foobar12", "should be equal")
}

That was a test for a sunny day, how about testing for panics?
Say I have a book shelf which can hold certain amount of books.
type shelf struct {
 books []book
}

type book struct {
 name string
}

var myShelf = shelf{books: make([]book, 5)}

Ok, I now have a shelf which has room for five books. I also need a function to add books to the shelf:
func addBookToShelf(book book, i int) {
 myShelf.books[i] = book
}

These are the books I want to put on the shelf, you might see the problem already.
var conanDoyle = []string{
 "A Study in Scarlet",
 "The Sign of Four",
 "The Adventures of Sherlock Holmes",
 "The Memoirs of Sherlock Holmes",
 "The Hound of the Baskervilles",
 "The Return of Sherlock Holmes",
 "The Valley of Fear",
}

The loop to add the books with addBookToShelf function:
for i, b := range conanDoyle {
 addBookToShelf(book{name: b}, i)
}

This panics a runtime error for index out of range. How can I test this?

By searching the panic for example
func TestAddBooksToShelf(t *testing.T) {
    defer func() {
        if r := recover(); r != nil {
            t.Errorf("handleBooks() panicked, shouldn't have")
        }
    }()
    handleBooks()
}

This is how you can catch panics in a test and not in production, it is also useful in cases you really want to panic.

Smoke or sanity testing

This is usually done right before going to production. It might be all your other tests which are run when you're making for example your Docker image, but if you have lot's of slow tests, this is then usually not the case.

It might be a shell script with a set of curl requests to check that the most important 
endpoints are answering. Or it might be a test like this, you could easily add more checking:
func TestGetUrls(t *testing.T) {
 for i := 0; i < 5; i++ {
  go makeHttpGet(t, "http://localhost:2000/endpoint1")
  go makeHttpGet(t, "http://localhost:2000/endpoint2")
  go makeHttpGet(t, "http://localhost:2000/endpoint3")
 }
}

func makeHttpGet(t *testing.T, url string) {
 client := &http.Client{}
 req, _ := http.NewRequest("GET", url, nil)
 res, _ := client.Do(req)
 data, err := ioutil.ReadAll(res.Body)
 if err != nil {
  t.Error(err)
 }
 res.Body.Close()

  body := string(data)
 if res.StatusCode != http.StatusOK {
  t.Fatalf("Status code was wrong: %v\n %v\n %v\n", res.StatusCode, url, body)
 }
}

Load testing

Load testing is usually related to a website you made and you want to
see how fast it delivers pages.

There are lot's of ready made made tools, you don't have to make them your self.

https://github.com/lubia/sniper
https://github.com/tsenart/vegeta
https://github.com/wg/wrk
http://gatling.io/

And you can profile your program easily enough too to see where the time is going.

http://dave.cheney.net/2013/07/07/introducing-profile-super-simple-profiling-for-go-programs

Race detecting

You can also detect race conditions in Go, there is a tool for that. Consider following code:
var test = make(map[int]int)

func main() {
 var i = 0
 for i < 10000 {
  go setTest(i, i)
  fmt.Println(test[i])
  i++
 }
}

func setTest(name int, val int) {
 test[name] = val
}

You can test it with: go run -race loop.go

And you get a fatal error: concurrent map writes. This is really useful if you run your program asynchronously with goroutines.


Conclusion

I hope I had known more about testing with Go from the beginning. It's so easy to just start writing working code with Go that testing doesn't seem to be relevant. Until you make enough code to notice that everything you write doesn't actually work the way you wanted it to. Refactoring and fixing code is such a bliss when you have the tests to back it up.

Oh, I have to add this too to think about, unit tests are useful, but they are not enough.



lauantai 19. maaliskuuta 2016

Saving page loads to InfluxDB through NATS and showing it as a graph with c3.js

I wanted to try out updating graphs in real time, and at the same time try something new. I’ve heard good things about Nats and InfluxDB so these were my preferred choises. I already have a web site www.uutispuro.fi, which I'm also using.

Stack used

  • c3.js - made on top of d3.js, a javascript library for graphs
  • Nats - A fast message broker
  • InfluxDB - Time series database

Installing

First installing Nats and InfluxDB were really easy. Download Nats binary and run it. For InfluxDB there are apt-get installation instructions at https://influxdata.com/downloads.

The code

To get the data to the database, I first needed to send the messages for each page load to Nats. The client is easy to use. For data to be saved I chose the ip address of the server so that I can make own graphs for each of them in the future. URI is the path which was asked, this can be used to show the pages which are read the most. User agent is also an interesting field of information. Time is used for the time based query from the database.

uri := "\"uri\":\"" + c.Request().URI() + "\""
userAgent := "\"userAgent\":\"" + c.Request().Header().Get("user-agent") + "\""
localIP := "\"localIP\":\"" + nats.LocalIP + "\""
time := "\"time\":\"" + time.Now().UTC().String() + "\""
nats.NatsConn.Publish("click", []byte("{"+time+", "+uri+", "+userAgent+", "+localIP+"}"))
if err := next.Handle(c); err != nil {
  c.Error(err)
}

And then in plotter project, I need to listen to Nats.

func GetClicks() {
  NewInflux()
  nc, err := nats.Connect("nats://192.168.0.5:4222", 
    nats.MaxReconnects(60), 
    nats.ReconnectWait(2*time.Second))
  if err != nil {
      fmt.Println(err)
    return
  }
  defer nc.Close()
  ch := make(chan *nats.Msg, 128)
  _, err = nc.ChanSubscribe("click", ch)
  if err != nil {
    fmt.Println(err)
    return
  }

  for {
    msg := <-ch
    var data map[string]interface{}
    if err := json.Unmarshal(msg.Data, &data); err != nil {
      panic(err)
    }
    Save(data)
  }
}

And save the data to the database.

func Save(click map[string]interface{}) {
  bp, err := client.NewBatchPoints(client.BatchPointsConfig{
    Database:  influx.DbName,
    Precision: "s",
  })
  if err != nil {
    log.Println(err)
  }

  stamp, _ := time.Parse("2006-01-02 15:04:05 -0700 MST", click["time"].(string))
  pt, err := client.NewPoint("click", map[string]string{}, click, stamp)
  if err != nil {
    log.Println(err)
  }
  bp.AddPoint(pt)
  err = influx.Conn.Write(bp)
  if err != nil {
    log.Println(err)
  }
}

Now to the exiting part. We have the data in the database and it's easy to fetch it from there.

func GetList() map[string]int64 {
  q := client.NewQuery("select count(localIP) from click 
      where time > now() - 1h 
      group by time(1m) fill(0)",
      "plotter", "s")
  var fields = make(map[string]int64)
  response, err := influx.Conn.Query(q)
  if err != nil || response.Error() != nil {
    fmt.Println(err)
  } else {
    for _, result := range response.Results {
      for _, value := range result.Series[0].Values {
        id := strconv.FormatInt(convertType(value[0]), 10)
        val := convertType(value[1])
        fields[id] = val
      }
    }
  }
  return fields
}

What do we do with the data? We'll send it with websockets to the browser every second.

e.Get("/ws", standard.WrapHandler(websocket.Handler(func(ws *websocket.Conn) {
  for range time.Tick(time.Second) {
    clicks, err := json.Marshal(app.GetList())
      if err != nil {
        fmt.Println(err)
      }
      err = websocket.Message.Send(ws, string(clicks))
      if err != nil {
        fmt.Printf("client closed connection %s\n", err)
        break
      }
    }
})))

In the browser we take a websocket connection and listen.

window.onload = function() {
  writeState("document loaded");
  if ('WebSocket' in window) {
    var connection = new WebSocket('ws://uutispuro.fi:7000/ws');
    connection.onopen = function() {
      writeState("connection open");
    }
    connection.onclose = function() {
      writeState("connection closed");
    }
    connection.onerror = function(error) {
      writeState("error: " + error);
    }
    connection.onmessage = function(e) {
      var msg = JSON.parse(e.data);
      writeState("got a message");
      connection.send("ack");
      arr = ['per minute'];
      Object.keys(msg).map(function(key, i) {
        if (i == 0) {
          arr.push(0);
        } else {
          arr.push(msg[key]);
        }
      });
      chart.load({
        columns: [
          arr
        ]
      });
    }
  } else {
    writeState("You're browser does not support websockets.");
  }
}

And draw the graph with the data sent from the server.

var chart = c3.generate({
  data: {
    columns: [
      ['per minute', 0],
    ],
    type: 'bar'
  },
  bar: {
    width: {
      ratio: 1.0
    }
  },
  axis: {
    x: {
      label: 'minutes',
      inverted: true
    },
    y: {
      label: 'page loads'
    }
  }
});

Links

sunnuntai 6. maaliskuuta 2016

Simple Go balancer for Kubernetes Raspberry Pi cluster

I made a really simple load balancer/proxy in front of Kubernetes to share traffic between Raspberry Pi's. There are many of these available and a lot better ones too, but I wanted to try out myself what is required to make a load balancer. Previous post was about setting up the cluster.

The load balancer has only two methods, one for socket.io and one for everything else.

http.HandleFunc("/socket.io/", websocketProxy)
http.HandleFunc("/", proxy)

Optimally there would only be one method to handle both, but Socket.io with websocket upgrades needs something more. Also I would have liked to log more about Socket.io traffic, but don't know how (Is it even possible?).

Sharing traffic between addresses is done randomly

func random(min, max int) int {
  return rand.Intn(max-min) + min
}
and usage:
urls[random(0, 3)]

It's in use now, but far from ready. First of all, I'd like to get the addresses from Kubernetes automatically. For now, it's relying on an environment variable:

export BALANCED_URLS=http://[192.168.0.21]:1300,http://[192.168.0.22]:1300,http://[192.168.0.23]:1300

Secondly if and when one of the endpoints is down, balancer should not try to use that endpoint at all. I also don't have any idea how much traffic it will handle, that would be nice to know :)

Github: https://github.com/jelinden/go-loadbalancer

lauantai 5. maaliskuuta 2016

Raspberry Pi cluster with Kubernetes

I got my first Raspberry Pi as a gift when I was an a Architecting on AWS -course. I’ve been mostly just playing with it, but wanted to use it for something useful. Then I read a blog post about making a cluster out of them and got really interested.

Creating a Raspberry Pi cluster running Kubernetes, the shopping list (Part 1) and Creating a Raspberry Pi cluster running Kubernetes, the installation (Part 2).

I ordered three (so I have one less than in the original recipe) more Raspberrys and started making a cluster. Initially I was thinking about some new database cluster but then changed to scaling Uutispuro, a rss feed title lister that I’ve been making for quite some time now. It uses Mongodb from outside of the Kubernetes.

Making the actual cluster went nicely with the help of the blog posts I followed. I did also setup ntpd and used fi_FI.UTF-8 as a locale for each Pi. Each worker node connected nicely to master, only the last one had a hickup of some kind (it got stuck to “NotReady”) but restart helped.

Docker Hub

It was a surprise for me that I had to use Docker Hub for getting the docker image to Kubernetes. At least it should be possible to use the image straigth away that I'm making.

# build a docker image
docker build -t jelinden/newsfeedreader:0.2 .
# push to docker hub
docker push jelinden/newsfeedreader:0.2
# get the image from docker hub and expose port 1300 and run it in one of the Pi's
kubectl run newsfeedreader --image=docker.io/jelinden/newsfeedreader:0.2 --port=1300
You can view what's going on inside the cluster with
kubectl get events -w
And then you can scale it up to run on three instances
kubectl scale rc newsfeedreader --replicas=3

I couldn’t get Docker Hub to work with a private repo, you can read more about it at Kubernetes PullImageError using Docker Hub with a private image. Luckily for me it doesn't matter if it's public or private.

What next?

Worker node ports are only accessible to master node, so I have to add a load balancer to the setup. Adding a haproxy or nginx is too easy, so I will make my own with Go.

lauantai 2. tammikuuta 2016

Free SSL sertificates from Letsencrypt

SSL sertificates have been too hard to get up and working. Addition to that, they mostly have been a bit costly.

Now there seems to be a working alternative. Letsencrypt offers an easy solution to fetch and use sertificates.

Here's an example what I did.

1. Get the scripts
git clone https://github.com/letsencrypt/letsencrypt
cd letsencrypt

2. For getting the sertificate, you need to have either 80 or 443 port free to be used (for a moment only), this uses port 443
./letsencrypt-auto certonly --standalone -d uutispuro.fi -d www.uutispuro.fi --standalone-supported-challenges tls-sni-01

3. Add the following lines (altered to your domain of course) to your nginx conf
ssl_certificate /etc/letsencrypt/live/uutispuro.fi/cert.pem;
ssl_certificate_key /etc/letsencrypt/live/uutispuro.fi/fullchain.pem;

See https://www.uutispuro.fi/en to see it in action :)

You do  need to update it periodically, every three months or so, but that is nothing else than doing the step 2. You can run it in crontab easily.