Custom Telegraf Plugin

I just started looking to InfluxDB and Telegraf for collecting data on a Linux machine, then visualizing it with Grafana. I’ve historically used collectd, statsite, and graphite to accomplish the same sort of task, but wanted to see how some of the new software compares.

I’m running a Ubuntu 14.04 LTS virtual machine, so feel free to follow along.

I managed to install the packages from the InfluxDB ubuntu repositories:

$ cat /etc/apt/sources.list.d/influxdb.list
deb https://repos.influxdata.com/ubuntu trusty stable

After adding the repo, and their GPG key, update and install the packages:

$ sudo apt-get update
$ sudo apt-get install influxdb telegraf

At the time of this writing Grafana just released 3.0 beta, but the packages are not yet in the repositories. I went ahead and followed the debian documentation to install directly from .deb.

$ dpkg -l grafana
||/ Name Version Architecture Description
+++-======-=====================-=====-=======
ii grafana 3.0.0-beta21459801392 amd64 Grafana

Next I made sure my services were running for these three bad boys:

$ service influxdb status
$ service telegraf status
$ service grafana-server status

At this point you should be able to hit the Grafana on the Linux machine, the service listens on tcp port 3000, so something like the following url should work:

http://127.0.0.1:3000

On the login screen login using the following credentials:

username: admin
password: admin

Add your InfluxDB service as a Datasource in Grafana:

View post on imgur.com

Next go ahead and create a new dashboard by clicking the Home dropdown on the top toolbar. Next from the left green widget create a new Graph Panel:

View post on imgur.com

If Telegrapf is running and collecting you should be able to see a few options in your FROM select measurements Graph options:

View post on imgur.com

What we are going to do now is add a new measurement to this section by using the Telegraf exec plugin:

$ cat /etc/telegraf/telegraf.d/custom.conf
[[inputs.exec]]
 command = "/silly.py"
 data_format = "json"
 name_suffix = "_custom"
 interval = "10s"

This plugin will call a silly Python script that returns the current second /silly.py:

#!/usr/bin/env python
import json
from datetime import datetime

s = datetime.now().second
print json.dumps(dict(seconds=s))

Once we have these in place we can restart the Telegraf service and should start to see some metrics:

$ telegraf -config /etc/telegraf/telegraf.conf -test
$ service telegraf restart

Lets use this new measurement, and tweak the Display a little bit:

View post on imgur.com

Now just for fun lets alter the Interval of our plugin from 10 seconds to 1 seconds:

$ cat /etc/telegraf/telegraf.d/custom.conf
[[inputs.exec]]
 command = "/silly.py"
 data_format = "json"
 name_suffix = "_custom"
 interval = "1s"

And of course restart Telegraf:

$ telegraf -config /etc/telegraf/telegraf.conf -test
$ sudo service telegraf restart

After letting it run for about a minute you should get something like this:

View post on imgur.com

Told you it was Silly 🙂

C# applications deployed with Docker and Mono

Lately I’ve been working a lot with Mono, and building C# applications on Linux. Just recently I discovered the official mono image in the Docker Hub Repo. This image comes with xbuild and NuGet (tools we need for building).

So lets do a little work and get a mono application up and running (note I’m using a company application and will remove any references that may be sensitive.)

I start by pulling the application’s source code down beside the Dockerfile:

# tree -L 3 .
.
├── Company.Session
│   ├── README.md
│   └── src
│   ├── Company.Session
│   ├── Company.Session.SessionService
│   ├── Company.Session.sln
│   ├── Company.Session.sln.DotSettings
│   └── Company.Session.Tests
└── Dockerfile

5 directories, 4 files

The Dockerfile handles the build, running, and network exposing for this app:

# The Official Mono Docker container
# https://registry.hub.docker.com/_/mono/
FROM mono:3.12

MAINTAINER Jeffrey Ness "jeffrey.ness@...."

# The TCP ports this Docker container exposes the the host.
EXPOSE 80

ENV LISTEN_ON http://*:80/
ENV POSTGRESQL_USER_ID root
ENV POSTGRESQL_USER_PW password
ENV POSTGRESQL_HOST 172.17.42.1
ENV POSTGRESQL_PORT 5432
ENV POSTGRESQL_DATABASE session
ENV POSTGRESQL_SEARCH_PATH public

# Add the project tarball to Docker container
ADD Company.Session /var/mono/Company.Session/
WORKDIR /var/mono/Company.Session/src/

# Build our project
RUN nuget restore Company.Session.sln
RUN xbuild Company.Session.sln

# Change to our artifact directory
WORKDIR /var/mono/Company.Session/src/Company.Session.SessionService/bin/Debug

# Entry point should be mono binary
ENTRYPOINT mono Company.Session.SessionService.exe

All that is needed now is to build the Docker image:

# docker build --no-cache -t session:0.1 .

After the build we should have some new images:

# docker images
REPOSITORY  TAG   IMAGE ID      CREATED VIRTUAL   SIZE
session     0.1   e886dc0f6db2  3 minutes ago     405.3 MB
mono        3.12  ad04eb901ba0  2 weeks ago       348.7 MB

Let’s start the new session image and bind it’s exposed port locally to 2345:

# docker run -d -p 2345:80 e886dc0f6db2
d8c4a7088da8ba0874c63e30e564a077b1c1a544825d7d1e148862b6b81f5600

We should now have a running Docker container:

# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d8c4a7088da8 session:0.1 /bin/sh -c 'mono Big 12 seconds ago Up 11 seconds 0.0.0.0:2345->80/tcp stoic_lalande

The Docker command logs will display the output from the running command.

# docker logs d8c4a7088da8
{"date":"2015-03-24T01:44:30.3285150+00:00","level":"INFO","appname":"Company.Session.SessionService.exe","logger":"Topshelf.HostFactory","thread":"1","ndc":"(null)","message":"Configuration Result:\n[Success] Name Company.Session.SessionService\n[Success] ServiceName Company.Session.SessionService"}

...

And lastly we should verify the TCP port mapping is working and we can hit it from the host:

# curl -I localhost:2345
HTTP/1.1 302 Found
Location: http://localhost/metadata
Vary: Accept
X-Powered-By: ServiceStack/4.036 Unix/Mono
Server: Mono-HTTPAPI/1.0
Date: Tue, 24 Mar 2015 01:46:06 GMT
Content-Length: 0
Keep-Alive: timeout=15,max=100

Linux /proc/net/route addresses unreadable

So you may have looked at /proc/net/route before and thought how the heck am I suppose to read this. Well here is the low down.

This file uses endianness to store the addresses as hexadecimal,
in reverse; for example 192 as hex is C0:

In []: hex(192)
Out[]: '0xc0'

So lets take a look at our route file:

Iface   Destination     Gateway         Flags   RefCnt  Use     Metric  Mask            MTU     Window  IRTT
eth0    00087F0A        00000000        0001    0       0       0       00FFFFFF        0       0       0
eth0    0000FEA9        00000000        0001    0       0       1002    0000FFFF        0       0       0
eth0    00000000        01087F0A        0003    0       0       0       00000000        0       0       0

Now the first entry has a destination of 00087F0A, lets go ahead and chunk these in to hex
characters:

In []: x = iter('00087F0A')

In []: res = [ ''.join(i) for i in zip(x, x) ]

In []: res
Out[]: ['00', '08', '7F', '0A']

Now if we wanted we could convert these hex codes manually:

In []: int('0A', 16)
Out[]: 10

But we want to do this in one big swoop:

In []: d = [ str(int(i, 16)) for i in res ]

In []: d
Out[]: ['0', '8', '127', '10']

And there we go, our IP address; however, it appears to be backwards. Lets go ahead and fix that, and return as a string:

In []: '.'.join(d[::-1])
Out[]: '10.127.8.0'

And there we have it! Your function may look something like this when all said and done:

In []: def hex_to_ip(hexaddr):
   ....:     x = iter(hexaddr)
   ....:     res = [str(int(''.join(i), 16)) for i in zip(x, x)]
   ....:     return '.'.join(res[::-1])
   ....:

And with output like so:

In []: hex_to_ip('00087F0A')
Out[]: '10.127.8.0'

In []: hex_to_ip('0000FEA9')
Out[]: '169.254.0.0'