2017-03-31 18:01:58 +02:00
|
|
|
# Zipkin
|
|
|
|
|
|
|
|
## Development and Testing Set-up
|
|
|
|
|
|
|
|
Great efforts have been made to make [Zipkin] easier to test, develop and
|
|
|
|
experiment against. [Zipkin] can now be run from a single Docker container or by
|
|
|
|
running its self-contained executable jar without extensive configuration. In
|
|
|
|
its default configuration you will run Zipkin with a HTTP collector, In memory
|
|
|
|
Span storage backend and web UI on port 9411.
|
|
|
|
|
|
|
|
Example:
|
|
|
|
```
|
|
|
|
docker run -d -p 9411:9411 openzipkin/zipkin
|
|
|
|
```
|
|
|
|
|
|
|
|
[zipkin]: http://zipkin.io
|
|
|
|
|
|
|
|
Instrumenting your services with Zipkin distributed tracing using the default
|
|
|
|
configuration is now possible with the latest release of [zipkin-go-opentracing]
|
|
|
|
as it includes an HTTP transport for sending spans to the [Zipkin] HTTP
|
|
|
|
Collector.
|
|
|
|
|
|
|
|
## Middleware Usage
|
|
|
|
|
|
|
|
Follow the [addsvc] example to check out how to wire the Zipkin Middleware. The
|
|
|
|
changes should be relatively minor.
|
|
|
|
|
|
|
|
The [zipkin-go-opentracing] package has support for HTTP, Kafka and Scribe
|
|
|
|
collectors as well as using Go Kit's [Log] package for logging.
|
|
|
|
|
|
|
|
### Configuring for the Zipkin HTTP Collector
|
|
|
|
|
|
|
|
To select the transport for the HTTP Collector, you configure the `Recorder`
|
|
|
|
with the appropriate collector like this:
|
|
|
|
|
|
|
|
```go
|
|
|
|
var (
|
|
|
|
debugMode = false
|
|
|
|
serviceName = "MyService"
|
|
|
|
serviceHostPort = "localhost:8000"
|
|
|
|
zipkinHTTPEndpoint = "localhost:9411"
|
|
|
|
)
|
|
|
|
collector, err = zipkin.NewHTTPCollector(zipkinHTTPEndpoint)
|
|
|
|
if err != nil {
|
|
|
|
// handle error
|
|
|
|
}
|
|
|
|
tracer, err = zipkin.NewTracer(
|
|
|
|
zipkin.NewRecorder(collector, debugMode, serviceHostPort, serviceName),
|
|
|
|
...
|
|
|
|
)
|
|
|
|
```
|
|
|
|
|
|
|
|
### Span per Node vs. Span per RPC
|
|
|
|
By default Zipkin V1 considers either side of an RPC to have the same identity
|
|
|
|
and differs in that respect from many other tracing systems which consider the
|
|
|
|
caller to be the parent and the receiver to be the child. The OpenTracing
|
|
|
|
specification does not dictate one model over the other, but the Zipkin team is
|
|
|
|
looking into these [single-host-spans] to potentially bring Zipkin more in-line
|
|
|
|
with the other tracing systems.
|
|
|
|
|
|
|
|
[single-host-spans]: https://github.com/openzipkin/zipkin/issues/963
|
|
|
|
|
|
|
|
In case of a `span per node` the receiver will create a child span from the
|
|
|
|
propagated parent span like this:
|
|
|
|
|
|
|
|
```
|
|
|
|
Span per Node propagation and identities
|
|
|
|
|
|
|
|
CALLER: RECEIVER:
|
|
|
|
---------------------------------
|
|
|
|
traceId -> traceId
|
|
|
|
spanId (new)
|
|
|
|
spanId -> parentSpanId
|
|
|
|
parentSpanId
|
|
|
|
```
|
|
|
|
|
|
|
|
**Note:** most tracing implementations supporting the `span per node` model
|
|
|
|
therefore do not propagate their `parentSpanID` as its not needed.
|
|
|
|
|
|
|
|
A typical Zipkin implementation will use the `span per RPC` model and recreate
|
|
|
|
the span identity from the caller on the receiver's end and then annotates its
|
|
|
|
values on top of it. Propagation will happen like this:
|
|
|
|
|
|
|
|
```
|
|
|
|
Span per RPC propagation and identities
|
|
|
|
|
|
|
|
CALLER: RECEIVER:
|
|
|
|
---------------------------------
|
|
|
|
traceId -> traceId
|
|
|
|
spanId -> spanId
|
|
|
|
parentSpanId -> parentSpanId
|
|
|
|
```
|
|
|
|
|
|
|
|
The [zipkin-go-opentracing] implementation allows you to choose which model you
|
|
|
|
wish to use. Make sure you select the same model consistently for all your
|
|
|
|
services that are required to communicate with each other or you will have trace
|
|
|
|
propagation issues. If using non OpenTracing / legacy instrumentation, it's
|
|
|
|
probably best to use the `span per RPC call` model.
|
|
|
|
|
|
|
|
To adhere to the more common tracing philosophy of `span per node`, the Tracer
|
|
|
|
defaults to `span per node`. To set the `span per RPC call` mode start your
|
|
|
|
tracer like this:
|
|
|
|
|
|
|
|
```go
|
|
|
|
tracer, err = zipkin.NewTracer(
|
|
|
|
zipkin.NewRecorder(...),
|
|
|
|
zipkin.ClientServerSameSpan(true),
|
|
|
|
)
|
|
|
|
```
|
|
|
|
|
|
|
|
[zipkin-go-opentracing]: https://github.com/openzipkin/zipkin-go-opentracing
|
|
|
|
[addsvc]:https://github.com/go-kit/kit/tree/master/examples/addsvc
|
|
|
|
[Log]: https://github.com/go-kit/kit/tree/master/log
|
|
|
|
|
|
|
|
### Tracing Resources
|
|
|
|
|
|
|
|
In our legacy implementation we had the `NewChildSpan` method to allow
|
|
|
|
annotation of resources such as databases, caches and other services that do not
|
|
|
|
have server side tracing support. Since OpenTracing has no specific method of
|
|
|
|
dealing with these items explicitely that is compatible with Zipkin's `SA`
|
|
|
|
annotation, the [zipkin-go-opentracing] has implemented support using the
|
|
|
|
OpenTracing Tags system. Here is an example of how one would be able to record
|
|
|
|
a resource span compatible with standard OpenTracing and triggering an `SA`
|
|
|
|
annotation in [zipkin-go-opentracing]:
|
|
|
|
|
|
|
|
```go
|
|
|
|
// you need to import the ext package for the Tag helper functions
|
|
|
|
import (
|
|
|
|
"github.com/opentracing/opentracing-go"
|
|
|
|
"github.com/opentracing/opentracing-go/ext"
|
|
|
|
)
|
|
|
|
|
|
|
|
func (svc *Service) GetMeSomeExamples(ctx context.Context, ...) ([]Examples, error) {
|
|
|
|
// Example of annotating a database query:
|
|
|
|
var (
|
|
|
|
serviceName = "MySQL"
|
|
|
|
serviceHost = "mysql.example.com"
|
|
|
|
servicePort = uint16(3306)
|
|
|
|
queryLabel = "GetExamplesByParam"
|
|
|
|
query = "select * from example where param = 'value'"
|
|
|
|
)
|
|
|
|
|
|
|
|
// retrieve the parent span, if not found create a new trace
|
|
|
|
parentSpan := opentracing.SpanFromContext(ctx)
|
|
|
|
if parentSpan == nil {
|
|
|
|
parentSpan = opentracing.StartSpan(queryLabel)
|
|
|
|
defer parentSpan.Finish()
|
|
|
|
}
|
|
|
|
|
|
|
|
// create a new span to record the resource interaction
|
|
|
|
span := opentracing.StartChildSpan(parentSpan, queryLabel)
|
|
|
|
|
|
|
|
// span.kind "resource" triggers SA annotation
|
|
|
|
ext.SpanKind.Set(span, "resource")
|
|
|
|
|
|
|
|
// this will label the span's service & hostPort (called Endpoint in Zipkin)
|
|
|
|
ext.PeerService.Set(span, serviceName)
|
2017-05-18 18:54:23 +02:00
|
|
|
ext.PeerHostname,Set(span, serviceHost)
|
2017-03-31 18:01:58 +02:00
|
|
|
ext.PeerPort.Set(span, servicePort)
|
|
|
|
|
|
|
|
// a Tag is the equivalent of a Zipkin Binary Annotation (key:value pair)
|
|
|
|
span.SetTag("query", query)
|
|
|
|
|
|
|
|
// a LogEvent is the equivalent of a Zipkin Annotation (timestamped)
|
|
|
|
span.LogEvent("query:start")
|
|
|
|
|
|
|
|
// do the actual query...
|
|
|
|
|
|
|
|
// let's annotate the end...
|
|
|
|
span.LogEvent("query:end")
|
|
|
|
|
|
|
|
// we're done with this span.
|
|
|
|
span.Finish()
|
|
|
|
|
|
|
|
// do other stuff
|
|
|
|
...
|
|
|
|
}
|
|
|
|
```
|