I am writing this down so I don't forget. I started this task but had to stop a few times, and then remember where I left off and at some point, others will need to know how to get started.
Install ansible
brew install ansible
Install Amazon EC2 Ansible integration tool
Go here and follow instructions for Amazon EC2 Ansible integration tool. You will run a Python script and setup a few environment variable. It is painless. This will create an ansible inventory file based on your EC2 environment.
Start ssh agent with your key
$ ssh-agent bash
$ ssh-add ~/.ssh/YOUR_KEY.pem
Install ansible Oracle Java install plugin
$ ansible-galaxy install ansiblebit.oracle-java
Start up an EC2 box tag it as elk=elk
However you like, start up an EC2 instance and tag it with the tag elk=elk. This is just the type of box. In this case, I am in the process of writing an ansible setup script for an ELK stack.
---
- hosts:tag_elk_elkuser:ec2-usersudo:yesroles:
- { role:ansiblebit.oracle-java,oracle_java_set_as_default:yes,oracle_java_version:8,oracle_java_version_update:102,oracle_java_version_build:14}tasks:
- name:ensure apache is at the latest versionyum:name=httpd state=latest
Run the ansible playbook
$ ansible-playbook elk_install_playbook.yml
Ok. Now back to creating my ansible ELK install script. There are also tasks for creating an Amazon box with Ansible.
We recently revised this. Here is the old version. You can see how much QBit and Reakt have progressed.
There has been a lot written on the subject of Microservices Monitoring. Monitoring is a bit of an overloaded term. There is service health monitoring, which can be done with tools like Mesosphere/Marathon, Nomad, Consul, etc. There is also KPI monitoring, which is done with tools like Grafana, Graphite, InfluxDB, StatsD, etc. Then there is log monitoring and search with tools like the ELK stack (elastic-search, LogStash and Kibana) and Splunk, where you can easily trace logs down to the requests or client ID in a request header. And, then there is system monitoring (JVM, slow query logs, network traffic, etc.), with tools like SystemD, and more. You will want all of this when you are doing Microservices Development.
The more insight you have into your system, the easier it will be to support and debug. Microservices imply async distributed development. Doing async distributed development without monitoring is like running with scissors.
Health Monitoring (e.g., Consul, Nomad, Mesosphere/Marathon, Heroku, etc.)
Log monitoring (e.g., ELK stack, Splunk, etc.)
QBit has support for ELK/Splunk by providing support for MDC. QBit has support for systems that can monitor health like Mesosphere/Marathon, Heroku, Consul, Nomad, etc. by having an internal health system that QBit service actors all check-in with that then gets rolled up to other systems like Mesosphere/Marathon, Heroku, Consul, Nomad, etc.
In this tutorial we are going to just cover KPI monitoring for microservices which is sometimes called Metrics Monitoring or Stats Monitoring. KPI stands for Key Performance Indicators. These are the things you really care about to see if your system is up and running, and how hard it is getting hit, and how it is performing.
At the heart of the QBit KPI system is the Metrics collector. QBit uses the Metrik interface for tracking Microservice KPIs.
We are recording counts per time period, current level or gauge at this instance in time and timings which is how long did something take.
Demonstrating using QBit metrics
This guide assumes you have read through the main overview of QBit and have gone through the first tutorials, but you should be able to follow along if you have not, you just will be able to follow along better if you read the docs (at least skimmed) and went through the first set of tutorials.
Let's show it. First we need to build. Use Gradle as follows:
In this example we will use StatsD but QBit is not limited to StatsD for Microservice KPI monitoring. In fact QBit can do amazing things with its StatsService like clustered rate limiting based on OAuth header info, but that is beyond this tutorial.
The easiest way to setup StatsD is to use Docker. Docker is a great tool for development, and using Docker with Nomad, CoreOS, Mesosphere/Marathon, etc. is a great way to deploy Docker containers, but at a minimum you should be using the Docker tools for development.
Depending on how you have Docker setup, your URI might look a bit different. If you are running Docker tools with a Mac, then that should be your URI. (On Linux the above IO is likely to belocalhost not 192.168.99.100, go through the docker tool tutorials if you are lost at this point. It will be worth your time. I promise. I promise.invoke promise.)
Host Port Service
3003 9000 grafana to see the results
8086 8086 influxdb to store the results
3004 8083 influxdb-admin to query the results
8125 8125 statsd server that listens to statsD UPD messages
22022 22 sshd
If you want to see the metrics and see if this is working, go through the influxDB tutorial and look around at the measurements with the influx-admin. Influx is a time series database. Grafana allows you to see pretty graphs and charts of the microservice KPIs that we are collecting. You will want to learn grafana as well.
We use the host and port of the URI to connect to the StatsD daemon that is running on the docker container.
Setting up StatsD by using QBit managedServiceBuilder
We covered using and setting up the managedServiceBuilder in the first tutorials, and the complete code listing is below. You could use managedServiceBuilder to create astatsCollector as follows:
You could do this... managedServiceBuilder to create the StatsCollector/MetricsCollector
Since services typically deal with the health system, the reactor (callback management, tasks management, repeating tasks) and the stats collector we created a ServiceManagementBundle that is a facade over the health system, stats, and the reactor.
Better way to work with stats, health and the reactor
/** Create the management bundle for this service. */finalServiceManagementBundle serviceManagementBundle =
serviceManagementBundleBuilder().setServiceName("TodoServiceImpl")
.setManagedServiceBuilder(managedServiceBuilder).build();
The QBit StatsCollector interface extends the Metrik MetricsCollector interface (from QBit 1.5 onwards). ServiceManagementBundle has a stats method that returns a StatsCollector as well as common facade methods on the ServiceManagementBundle
Using the StatsCollector.
Then we just need to use it.
Using the StatsCollector to collect KPIs about our service
For kicks, we track the KPI todoservice.i.am.alive every three seconds.
Tracking KPI i.am.am.alive
@RequestMapping("/todo-service")
publicclassTodoServiceImplimplementsTodoService {
privatefinalMap<String, Todo> todoMap =newTreeMap<>();
privatefinalServiceManagementBundle mgmt;
publicTodoServiceImpl(ServiceManagementBundlemgmt) {
this.mgmt = mgmt;
/** Send stat count i.am.alive every three seconds. */
mgmt.reactor().addRepeatingTask(Duration.ofSeconds(3),
() -> mgmt.increment("i.am.alive"));
}
Tracking calls to add method
@Override
@POST(value ="/todo")
publicPromise<Boolean> addTodo(finalTodo todo) {
return invokablePromise(promise -> {
/** Send KPI addTodo called every time the addTodo method gets called. */
mgmt.increment("addTodo.called");
todoMap.put(todo.getId(), todo);
promise.accept(true);
});
}
Tracking calls to remove method
@Override
@DELETE(value ="/todo")
publicfinalPromise<Boolean> removeTodo(final @RequestParam("id") String id) {
return invokablePromise(promise -> {
/** Send KPI addTodo.removed every time the removeTodo method gets called. */
mgmt.increment("removeTodo.called");
todoMap.remove(id);
promise.accept(true);
});
}
You can register repeating tasks with @QueueCallback as follows:
But you do not need to if you use the serviceManagementBundle. Just specify it when you add the service to the managedServiceBuilder.
Adding service to managedServiceBuilder with a serviceManagementBundle
/* Start the service. */
managedServiceBuilder
//Register TodoServiceImpl
.addEndpointServiceWithServiceManagmentBundle(todoService, serviceManagementBundle)
//Build and start the server.
.startApplication();
Complete example
Todo.java
packagecom.mammatustech.todo;
publicclassTodo {
privateString id;
privatefinalString name;
privatefinalString description;
privatefinallong createTime;
publicTodo(Stringname, Stringdescription, longcreateTime) {
this.name = name;
this.description = description;
this.createTime = createTime;
this.id = name +"::"+ createTime;
}
publicStringgetId() {
if (id ==null) {
this.id = name +"::"+ createTime;
}
return id;
}
publicStringgetName() {
return name;
}
publicStringgetDescription() {
return description;
}
publiclonggetCreateTime() {
return createTime;
}
@Overridepublicbooleanequals(Objecto) {
if (this== o) returntrue;
if (o ==null|| getClass() != o.getClass()) returnfalse;
Todo todo = (Todo) o;
if (createTime != todo.createTime) returnfalse;
return!(name !=null?!name.equals(todo.name) : todo.name !=null);
}
@OverridepublicinthashCode() {
int result = name !=null? name.hashCode() :0;
result =31* result + (int) (createTime ^ (createTime >>>32));
return result;
}
}
TodoServiceImpl.java to show tracking KPIs.
packagecom.mammatustech.todo;
importio.advantageous.qbit.admin.ServiceManagementBundle;
importio.advantageous.qbit.annotation.RequestMapping;
importio.advantageous.qbit.annotation.RequestMethod;
importio.advantageous.qbit.annotation.RequestParam;
importio.advantageous.qbit.annotation.http.DELETE;
importio.advantageous.qbit.annotation.http.GET;
importio.advantageous.qbit.annotation.http.POST;
importio.advantageous.reakt.promise.Promise;
importjava.time.Duration;
importjava.util.ArrayList;
importjava.util.Map;
importjava.util.TreeMap;
import staticio.advantageous.reakt.promise.Promises.invokablePromise;
/** * Default port for admin is 7777. * Default port for main endpoint is 8888. * <p> * <pre> * <code> * * Access the service: * * $ curl http://localhost:8888/v1/... * * * To see swagger file for this service: * * $ curl http://localhost:7777/__admin/meta/ * * To see health for this service: * * $ curl http://localhost:8888/__health -v * Returns "ok" if all registered health systems are healthy. * * OR if same port endpoint health is disabled then: * * $ curl http://localhost:7777/__admin/ok -v * Returns "true" if all registered health systems are healthy. * * * A node is a service, service bundle, queue, or server endpoint that is being monitored. * * List all service nodes or endpoints * * $ curl http://localhost:7777/__admin/all-nodes/ * * * List healthy nodes by name: * * $ curl http://localhost:7777/__admin/healthy-nodes/ * * List complete node information: * * $ curl http://localhost:7777/__admin/load-nodes/ * * * Show service stats and metrics * * $ curl http://localhost:8888/__stats/instance * </code> * </pre> */
@RequestMapping("/todo-service")
publicclassTodoServiceImplimplementsTodoService {
privatefinalMap<String, Todo> todoMap =newTreeMap<>();
privatefinalServiceManagementBundle mgmt;
publicTodoServiceImpl(ServiceManagementBundlemgmt) {
this.mgmt = mgmt;
/** Send stat count i.am.alive every three seconds. */
mgmt.reactor().addRepeatingTask(Duration.ofSeconds(3),
() -> mgmt.increment("i.am.alive"));
}
@Override@POST(value="/todo")
publicPromise<Boolean>addTodo(finalTodotodo) {
return invokablePromise(promise -> {
/** Send KPI addTodo called every time the addTodo method gets called. */
mgmt.increment("addTodo.called");
todoMap.put(todo.getId(), todo);
promise.accept(true);
});
}
@Override@DELETE(value="/todo")
publicfinalPromise<Boolean>removeTodo(final@RequestParam("id") Stringid) {
return invokablePromise(promise -> {
/** Send KPI addTodo.removed every time the removeTodo method gets called. */
mgmt.increment("removeTodo.called");
todoMap.remove(id);
promise.accept(true);
});
}
@Override@GET(value="/todo", method=RequestMethod.GET)
publicfinalPromise<ArrayList<Todo>>listTodos() {
return invokablePromise(promise -> {
/** Send KPI addTodo.listTodos every time the listTodos method gets called. */
mgmt.increment("listTodos.called");
promise.accept(newArrayList<>(todoMap.values()));
});
}
}
TodoServiceMain.java showing how to configure StatsD QBit for MicroService KPI tracking
packagecom.mammatustech.todo;
importio.advantageous.qbit.admin.ManagedServiceBuilder;
importio.advantageous.qbit.admin.ServiceManagementBundle;
importjava.net.URI;
import staticio.advantageous.qbit.admin.ManagedServiceBuilder.managedServiceBuilder;
import staticio.advantageous.qbit.admin.ServiceManagementBundleBuilder.serviceManagementBundleBuilder;
publicclassTodoServiceMain {
publicstaticvoidmain(finalString... args) throwsException {
/* Create the ManagedServiceBuilder which manages a clean shutdown, health, stats, etc. */finalManagedServiceBuilder managedServiceBuilder = managedServiceBuilder()
.setRootURI("/v1") //Defaults to services
.setPort(8888); //Defaults to 8080 or environment variable PORT
managedServiceBuilder.enableStatsD(URI.create("udp://192.168.99.100:8125"));
managedServiceBuilder.getContextMetaBuilder().setTitle("TodoMicroService");
/** Create the management bundle for this service. */finalServiceManagementBundle serviceManagementBundle =
serviceManagementBundleBuilder().setServiceName("TodoServiceImpl")
.setManagedServiceBuilder(managedServiceBuilder).build();
finalTodoService todoService =newTodoServiceImpl(serviceManagementBundle);
/* Start the service. */
managedServiceBuilder
//Register TodoServiceImpl
.addEndpointServiceWithServiceManagmentBundle(todoService, serviceManagementBundle)
//Build and start the server.
.startApplication();
/* Start the admin builder which exposes health end-points and swagger meta data. */
managedServiceBuilder.getAdminBuilder().build().startServer();
System.out.println("Todo Server and Admin Server started");
}
}