Deploy Jaeger instance in KIND cluster using Jaeger operator
Table of contents
- About this article
- what is Jaeger?
- Jaeger installation steps
- KIND configuration
- cert-manager and jaeger-operator yaml file reference
- Shell script to install Jaeger in KIND cluster
- Port forward the UI and the Jaeger collector service port
- SpringBoot Application to send traces
- Execute the spring boot application services
- Snapshot of the Jaeger UI after shipping the traces
This blog will explain how to deploy the Jaeger all-in-one image in KIND cluster using the Jaeger operator.
Pre-requisites
Basic understanding of Jaeger architecture
Docker Desktop installed and running
KIND CLI configured
About this article
First, we will see instructions on how to deploy Jaeger instance in the Docker Desktop KIND cluster
Next, we will run the SpringBoot application locally to send traces with OpenTracing.
what is Jaeger?
Jaeger is an open-source project used for distributed tracing. Jaeger project was developed by Uber and is now a CNCF project.
Say, when a request is being processed by multiple microservice the request flow between services can be traced in the Jaeger. The span tracing can be used to analyze the performance since the Jaeger UI provides details of how much time is spent on each service.
Jaeger installation steps
Install KIND cluster with necessary ports exposed
Install cert-manager, using cert-manager yaml
- Based on the Jaeger Installation document it requires
cert-manager
to be installed in the Kubernetes cluster before installing the Jaeger operator.
- Based on the Jaeger Installation document it requires
Create Namespace and Install Jaeger operator yaml
Create Jaeger instance using CRD manifest
- In this case, have used
all-in-one
mode Jaeger instance
- In this case, have used
NOTE:
There are different modes the Jaeger can be deployed, the default mode is
all-in-one
mode where the trace data will not be persisted.
all-in-one
mode is mostly used for local development and data is held in memory.For production, it is better to use
production
mode. Refer Jaeger documentation for more details.
KIND configuration
KIND cluster configuration below exposes multiple ports export, it is not necessary to expose all the ports, latter only ports 16686 and 14268 are forwarded.
Save the YAML content as
jaeger_kind_cluster.yaml
file.
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
networking:
ipFamily: ipv4
apiServerAddress: 127.0.0.1
apiServerPort: 6443
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 6831
hostPort: 6831
listenAddress: "127.0.0.1"
protocol: UDP
- containerPort: 6832
hostPort: 6832
listenAddress: "127.0.0.1"
protocol: UDP
- containerPort: 16686
hostPort: 16686
listenAddress: "127.0.0.1"
protocol: TCP
- containerPort: 16685
hostPort: 16685
listenAddress: "127.0.0.1"
protocol: TCP
- containerPort: 9411
hostPort: 9411
listenAddress: "127.0.0.1"
- containerPort: 4317
hostPort: 4317
listenAddress: "127.0.0.1"
protocol: TCP
- containerPort: 4318
hostPort: 4318
listenAddress: "127.0.0.1"
protocol: TCP
- containerPort: 5778
hostPort: 5778
listenAddress: "127.0.0.1"
protocol: TCP
- containerPort: 14250
hostPort: 14250
listenAddress: "127.0.0.1"
protocol: TCP
- containerPort: 14268
hostPort: 14268
listenAddress: "127.0.0.1"
protocol: TCP
- containerPort: 14269
hostPort: 14269
listenAddress: "127.0.0.1"
protocol: TCP
- containerPort: 80
hostPort: 80
protocol: TCP
- containerPort: 443
hostPort: 443
protocol: TCP
cert-manager and jaeger-operator yaml file reference
Save the content from the Cert-Manager/GitHub file to
cert-manager.yaml
Save the content from the Jaegertracing/GitHub file to
jaeger-opertor.yaml
Below is the Jaeger CRD manifest YAML, which deploys Jaeger instance in KIND cluster.
apiVersion: jaegertracing.io/v1
kind: Jaeger
metadata:
name: simplest
Shell script to install Jaeger in KIND cluster
- Once the yaml files are stored under a directory, run below script in Git Bash to install the Jaeger Instance.
#! /bin/sh
# Create kind cluster in Docker desktop
kind create cluster --name jaeger-cluster --config jaeger_kind_cluster.yaml
# install the cert manager
kubectl apply -f cert-manager.yaml
# create namespace trace
kubectl create ns trace
# sleep for 10 sec
sleep 10
# install the jaeger operator
kubectl -n trace apply -f jaeger-operator.yaml
# pause for 100 seconds
sleep 100
# install the simple cluster
kubectl -n trace apply -f simple-jaeger.yaml
- After execution of the shell script, with the below command status and strategy of deployed Jaeger instance can be verified.
$ kubectl -n trace get jaegers
NAME STATUS VERSION STRATEGY STORAGE AGE
simplest Running 1.40.0 allinone memory 10d
- Below is the output of status of the deployed Jaeger resources in the KIND cluster
# get the pods name of the jaeger
$ kubectl -n trace get pods
# output
NAME READY STATUS RESTARTS AGE
jaeger-operator-6787f4df85-ww7mx 2/2 Running 7 (147m ago) 10d
simplest-6b8dbb67c5-kpz5j 1/1 Running 0 146m
# ge the service status
$ kubectl -n trace get svc
# output sample
NAME TYPE CLUSTER-IP PORT(S)
jaeger-operator-metrics ClusterIP 10.96.230.32 8443/TCP
jaeger-operator-webhook-service ClusterIP 10.96.137.93 443/TCP
simplest-agent ClusterIP None 5775/UDP,5778/TCP,6831/UDP,6832/UDP
simplest-collector ClusterIP 10.96.135.45 9411/TCP,14250/TCP,14267/TCP,14268/TCP,4317/TCP,4318/TCP
simplest-collector-headless ClusterIP None 9411/TCP,14250/TCP,14267/TCP,14268/TCP,4317/TCP,4318/TCP
simplest-query ClusterIP 10.96.212.68 16686/TCP,16685/TCP
Port forward the UI and the Jaeger collector service port
In the spring application, we will send the traces to the 14268 port, which is the Jaeger collector service running in the cluster.
Based on the Jaeger architecture, the collector service will collect the traces.
# port forward the jaeger pod (the pod name can be fetched from above command)
kubectl -n trace port-forward pod/simplest-6b8dbb67c5-kpz5j 16686:16686
# port forward the jaeger service
kubectl -n trace port-forward svc/simplest-collector 14268:14268
NOTE:
SpringBoot application is developed using 2.7.7 version with OpenTracing dependencies.
SpringBoot 3.0.0 doesn't support OpenTracing, it is deprecated in favor of OpenTelementry.
SpringBoot Application to send traces
- The pom.xml file with
OpenTracing
dependencies.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.7</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>app</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>app</name>
<description>application</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>io.opentracing.contrib</groupId>
<artifactId>opentracing-spring-jaeger-cloud-starter</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- The entry point of SpringBoot application.
package com.example.app;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class JaegerApplication {
public static void main(String[] args) {
SpringApplication.run(KafkaApplication.class, args);
}
// Used by the Jeager dependencies to intercept http header
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder){
return builder.build();
}
}
- Simple Spring controller below exposes two endpoint
/api/generate
andapi/fetch
INFO:
We will execute the same SpringBoot application in different ports (
8080
and8090
) on the same host machine.The endpoint
http://localhost:8080/api/fetch
will invoke the application running onhttp:/localhost:8090/api/generate
. The traces will be sent to the Jaeger instance by the application.We can update the Jaeger configuration to send traces in the sample rather than sending all the traces for each request which is not shown here.
package com.example.kafka;
import io.jaegertracing.Configuration;
import io.jaegertracing.internal.samplers.ConstSampler;
import io.jaegertracing.internal.samplers.ProbabilisticSampler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.Random;
@RestController
@RequestMapping("/api")
@Slf4j
public class AppController {
private RestTemplate restTemplate;
public AppController(RestTemplate restTemplate){
this.restTemplate = restTemplate;
}
Random rand = new Random();
@GetMapping("/generate")
public long calculate(){
long number = rand.nextInt(1000);
log.info("api /generate invoked {}",number);
return number;
}
@Value("${app2.port:8090}")
private String appPort;
@Value("${app2.host:localhost}")
private String hostName;
@GetMapping("/fetch")
public ResponseEntity getNum(){
String url = "http://"+hostName+":"+appPort+"/api/generate";
String resp = restTemplate.getForObject(url, String.class);
log.info("api/fetch invoked - {} = {}",url,resp);
return ResponseEntity.ok("random num : "+resp);
}
}
application.yml
file defining theOpenTracing
Jaeger URL configuration.The spring application name is not passed during runtime with
Java
command.
opentracing:
jaeger:
http-sender:
url: http://localhost:14268/api/traces
Execute the spring boot application services
Use
mvn clean install
command to create the jar file.Once jar file is generated, execute two instances of application with the below commands.
java -jar .\target\app-0.0.1-SNAPSHOT.jar --spring.application.name=service-1 --server.port=8080 --debug
java -jar .\target\app-0.0.1-SNAPSHOT.jar --spring.application.name=service-2 --server.port=8090 --debug
- After the application is up and running, use
curl
command to access the REST end-point like below
curl -i http://localhost:8080/api/fetch
HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8
Content-Length: 16
Date: Wed, 11 Jan 2023 02:09:58 GMT
random num : 771
Once the
curl
command access the endpoint the traces are sent to Jaeger and we can view the spans from Jaeger UI athttp://localhost:16686
INFO
In Jaeger UI the service name
jaeger-query
is the default.After accessing the REST endpoint and traces are collected the Jaeger UI spans can be viewed under service. This service is the SpringBoot application name. In this case,
service-1
andservice-2
.
Snapshot of the Jaeger UI after shipping the traces
Subscribe to my newsletter
Read articles from Thirumurthi S directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by