Building on Top of HashiCorp Nomad’s Event Stream
Get better observability and debuggability in your Nomad clusters with the new Event Stream feature in Nomad 1.0.
HashiCorp Nomad 1.0 includes a new feature called Event Stream: a function that allows operators to view and subscribe to a single unified timeline that streams all high-level events to better understand how the Nomad cluster is performing. By providing stronger tracing and debuggability, Event Steam helps operators unlock a new way to build applications on top of Nomad.
» Before the Event Stream
Before the Event Stream, subscribing to and monitoring for changes within a Nomad cluster required the use of blocking queries. Blocking queries can be used to wait for a potential change of a particular endpoint using long polling.
Blocking queries have a few drawbacks as they relate to subscribing to a set of changes within a cluster. One drawback is that blocking queries have no guarantee of a change. It’s possible that the query may time out or that there was an idempotent write that does not affect the result of the query that was being made. Another pain point is that blocking queries require coordination on the client if you want to monitor multiple types of requests such as Deployments and Jobs. A blocking query for each must be made, and then responses from each endpoint need to be strung together by the user.
With the event stream, we believe that Nomad now has a way to easily subscribe to a set of changes within a cluster through a single endpoint and a single request.
» The Nomad Event Stream
When enabled, Nomad will create a set of events when a state change occurs in Nomad. State changes occur in Nomad via its Finite State Machine (FSM). When a request is made to update a value in Nomad, like registering a new Job, the underlying state store is updated through the FSM with the updated values. In Nomad 1.0, these updated values are now converted into events and pushed onto the server’s event broker. The event broker must be enabled by setting enable_event_broker
in the server’s configuration block. Since events are sourced via the FSM this means that every server will have an identical set of events in its broker.
» What Does an Event Look Like?
Each event contains a Topic
, Type
, Key
, Namespace
, FilterKeys
, Index
, and Payload
. The contents of Payload
depend on the event Topic. Responses from the /v1/event/stream
API are new line delimited JSON objects (ndjson). Since multiple values can be updated in a single raft transaction, each API response contains an Index
and set of Events.
{
"Index": 7,
"Events": [
{
"Topic": "Node",
"Type": "NodeRegistration",
"Key": "ccc4ce56-7f0a-4124-b8b1-a4015aa82c40",
"Namespace": "",
"FilterKeys": null,
"Index": 7,
"Payload": {
"Node": {... entire node object}
}
}
]
}
» Accessing the Event Stream
The event stream currently exists in the API so users can access the new event stream using an http client like curl or programmatically using the Nomad API package.
The following would subscribe to all events
$ curl -s -v -N http://127.0.0.1:4646/v1/event/stream
You can filter on certain topics by specifying a topic query param, where the key to the topic parameter is the event topic to subscribe to, and the value is the key to filter on.
$ curl -s -v -N \
--data-urlencode "topic=Node:ccc4ce56-7f0a-4124-b8b1-a4015aa82c40" \
--data-urlencode "topic=Deployment" \
--data-urlencode "topic=Job:web" \
http://127.0.0.1:4646/v1/event/stream
The Go package to use Nomad’s API makes it easy to subscribe to the event stream from your Go application. The following code segment shows how you can use the API to subscribe to Deployment events.
func main() {
client, _ := api.NewClient(&api.Config{})
eventsClient := client.EventStream()
// Subscribe to all Deployment events
topics := map[api.Topic][]string{
api.TopicDeployment: {"*"},
}
// Begin stream
ctx := context.Background()
eventCh, err := eventsClient.Stream(ctx, topics, 0, &api.QueryOptions{})
if err != nil {
fmt.Printf("received error %s", err)
os.Exit(1)
}
for {
select {
case <-ctx.Done():
return
case event := <-eventCh:
if event.Err != nil {
fmt.Printf("received error %s", err)
break
}
// Ignore heartbeats used to keep connection alive
if event.IsHeartbeat() {
continue
}
for _, e := range event.Events {
deployment, err := e.Deployment()
if err != nil {
fmt.Printf("received error %s", err)
continue
}
fmt.Printf("Deployment update %s", deployment.Status)
}
}
}
}
To see another example take a look at this slackbot integration to send deploy notifications.
» Share Your Feedback
We are excited to hear from the community on what you intend to build on top of the event stream! If you have questions, comments, or feedback around the event stream or are interested in a new type of event let us know on discuss!
Sign up for the latest HashiCorp news
More blog posts like this one
Terraform Enterprise improves deployment flexibility with Nomad and OpenShift
Customers can now deploy Terraform Enterprise using Red Hat OpenShift or HashiCorp Nomad runtime platforms.
Nomad’s internal garbage collection and optimization discovery during the Nomad Bench project
A look into Nomad’s internal garbage collection process and the optimization discovered during the bench project.
New approaches to measuring Nomad performance
See how the HashiCorp Nomad team re-examined how to capture performance for a workload orchestrator, resulting in new metrics to better capture Nomad’s performance.