Tracing in verteilten Systemen mit Spring Cloud Sleuth und Zipkin

Patrick Geschinski
, 04 November 2016

Wie lange benötigt ein Request der mehrere (Micro-)Services durchläuft und wie finde ich Flaschenhälse, die zu einem schlechten Laufzeitverhalten führen.

Dies sind häufig Fragen, die man sich stellt wenn man bereits einige Services entwickelt hat und dann nicht mehr genau nachvollziehen kann wie sich eine Anfrage durch mehrere Systeme verhält und wie man wieder Herr über diese Situation wird.

Da wir sehr stark auf Spring Boot setzen und mit Sleuth eine einfache Lösung existiert möchte ich euch hier Spring Cloud Sleuth vorstellen.

Die Demo dazu findet ihr unter: https://github.com/PG2000/spring-boot-sleuth-zipkin-demo

Was ist Spring Cloud Sleuth?

Mit Spring Cloud Sleuth ist es möglich Requests über Systemgrenzen hinaus zu markieren und Traces an ein Aggregationsystem zu versenden.

Wie gewohnt ist die Integration in Spring Boot Projekten sehr einfach gehalten.

Unter Gradle genügt folgende Dependency:

compile('org.springframework.cloud:spring-cloud-starter-sleuth')

Um die Traces auch zu einem Zipkin Server senden zu können muss noch folgende Abhängigkeit geladen werden:

compile('org.springframework.cloud:spring-cloud-starter-sleuth')

Damit hat man dann auch die Möglichkeit einen lokalen Zipkin Server zu starten. Dieser wird in meiner Demo aber als eigener Docker Container gestartet.

Für Maven gibt es unter https://cloud.spring.io/spring-cloud-sleuth/ die Quick Start Konfiguration.

In der application.yml muss noch der (spring.application.name) konfiguriert werden

spring:
  application:
    name: mothership-service-one

um eine sinnvolle Ausgabe im Trace zu erhalten.

Das war es dann auch schon.

Die Traces werden nun für folgende Spring Komponenten mit Zero-Configuration Aufwand erstellt.

Wie ist ein Trace aufgebaut?:

Der Aufbau des Trace ist relativ simpel und sieht in der Console in etwa so aus.

mothership-service-one_1  | 2016-11-04 15:27:43.303  INFO [mothership-service-one,a379156c77b387b7,5a9b9c7ce64a6275,true] 1 --- [nio-8080-exec-1] one.mothership.MothershipOneResource     : ....

die eigentlich interessante Information ist nun folgende:

[mothership-service-one,a379156c77b387b7,5a9b9c7ce64a6275,true]

Die Felder haben folgende Bedeutung:

Wert Bedeutung
mothership-service-one Spring Application Name (spring.application.name) in der application.yml
a379156c77b387b7 Die Trace ID
5a9b9c7ce64a6275 Die Span ID
true Gibt an ob der Trace an den Zipkin Server gesendet werden soll

Dieser Trace wird - falls konfiguriert - dann auch an den Zipkin Server gesendet.

Schematisch betrachtet funktioniert dies in etwa so:

Image

Die Aggregation der Daten erfolgt in Zipkin und sieht dann folgendermaßen aus:

Image

Zipkin kann auch die Abhängigkeiten zwischen Systemen visualiseren.

Image

was bei einer Vielzahl von Services sehr praktisch sein kann um Kommunikationswege nachzuvollziehen.

In der Default Konfiguration wird nicht jede Aktion getraced um einen Overhead in hochfrequentierten Services zu vermeiden. Für die Demo wollte ich jedoch jeden Aktion tracen. Dies ist mit folgendem Bean möglich.

@Bean
public Sampler defaultSampler() {
  return new AlwaysSampler();
}

Weitere Sampling Strategien findet ihr hier.

Fazit:

Mit Spring Cloud Sleuth erhaltet ihr im Spring Boot Umfeld eine schnelle Lösung um in verteilten Systemen aussagefähige Traces zu erstellen. Ein Nachteil ist es jedoch wenn die Service Landschaft polyglott entwickelt wurde und dadurch Lücken im Trace entstehen.

Dazu entwickelt sich gerade eine Vendor unabhängie Alternative unter opentracing.io

Übrigens gibt es einen schönen Nebeneffekt wenn ihr ein zentralisiertes Logging betreibt: Beim Logging wird die Trace Id ebenfalls verwendet. Daher ist es auch möglich z.B. im Graylog oder Kibana danach zu suchen und systemübergreifend Logs zu verfolgen. Dies funktioniert sogar wenn Zipkin deaktiviert ist.

Die ausführliche Dokumentation zu Spring Cloud Sleuth findet ihr hier.

Patrick Geschinski

Software Architekt, Agiler Projektleiter, Software Engineer