0%

使用opentelemetry与jaeger实现flask应用的链路追踪

链路追踪是应用可观测性的重要组成部分,它可以帮助我们快速定位问题,提高应用的可用性和稳定性。
今天我们来看看如何使用 opentelemetry 与 jaeger 实现 flask 应用的链路追踪。

OpenTelemetry 与 Jaeger

OpenTelemetry is a collection of tools, APIs, and SDKs. Use it to instrument, generate, collect, and export telemetry data (metrics, logs, and traces) to help you analyze your software’s performance and behavior.

OpenTelemetry 是一个用于收集、生成、导出遥测数据(metrics、logs、traces)的工具集合,它可以帮助我们分析软件的性能和行为。

Jaeger 是一个开源的分布式跟踪系统,它可以收集、存储和分析应用的链路追踪数据。

Jaeger 支持 Opentelemetry 协议,可以直接从 OpenTelemetry 收集数据。

在 Flask 中集成 OpenTelemetry 与 Jaeger

部署 Jaeger

Jaeger 有多种部署方式,在开发环境下最简单的方式是使用 Jaeger 官方提供的 all-in-one 镜像。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
## make sure to expose only the ports you use in your deployment scenario!
docker run -d --name jaeger \
-e COLLECTOR_OTLP_ENABLED=true \
-e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \
-p 5775:5775/udp \
-p 6831:6831/udp \
-p 6832:6832/udp \
-p 5778:5778 \
-p 16686:16686 \
-p 14250:14250 \
-p 14268:14268 \
-p 14269:14269 \
-p 4317:4317 \
-p 4318:4318 \
-p 9411:9411 \
jaegertracing/all-in-one:1.42

访问 http://localhost:16686 查看 Jaeger 的 UI。

jaeger ui

基础配置

首先安装一下相关依赖。

1
pip install flask opentelemetry-instrumentation-flask opentelemetry-exporter-jaeger

编辑main.py,定义一个简单的 flask 应用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
from flask import Flask
from opentelemetry import trace
from opentelemetry.exporter.jaeger.thrift import JaegerExporter
from opentelemetry.instrumentation.flask import FlaskInstrumentor
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor


def configure_trace(app: Flask):
trace.set_tracer_provider(
TracerProvider(resource=Resource.create({SERVICE_NAME: app.name}))
)

trace.get_tracer_provider().add_span_processor(
BatchSpanProcessor(
JaegerExporter(
agent_host_name="localhost",
agent_port=6831,
)
)
)

FlaskInstrumentor().instrument_app(app)


app = Flask(__name__)
configure_trace(app)


@app.get("/")
def hello():
return "Hello, World!"

configure_trace函数中,我们配置了 opentelemetry 的 TracerProvider,它是一个全局的单例,用于创建 tracer。

然后创建了一个 JaegerExporter 并添加为 Provider 的 Processor,它会将收集到的数据发送到 Jaeger。

最后使用 FlaskInstrumentor 来为 flask 应用添加 opentelemetry 的中间件。

运行应用并访问 http://localhost:5000。

1
flask --app=main:app run --reload

刷新 Jaeger 的 UI,选择main服务,点击”Find Traces”,可以看到之前的访问已经被成功追踪。

search trace

点击具体的 trace 可以看到详细的链路信息,包括请求的路径、响应时间等信息。

trace info

追踪业务逻辑

我们可以使用 opentelemetry 的 API 来追踪细致的业务逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
@app.get("/")
def hello():
tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span("step-1") as span:
span.add_event("step-1-event", {"key": "value"})
time.sleep(0.1)
with tracer.start_as_current_span("step-2"):
with tracer.start_as_current_span("step-2-1"):
time.sleep(0.05)
with tracer.start_as_current_span("step-2-2"):
time.sleep(0.05)
return "Hello, World!"

通过trace.get_tracer(__name__)获取到 tracer,然后使用 with start_as_current_span(span_name)创建一个 span,可以进行具体业务逻辑的追踪。

上述代码中,我们创建了两个 span,分别是step-1step-2(拥有两个子 span),并且在step-1中添加了一个 event。

重新访问 http://localhost:5000,刷新 Jaeger 的 UI,可以看到新的 trace 已经被成功追踪,并且可以追踪到每一个 span 的开始时间、结束时间、耗时等信息。

trace span

也可以切换视图,查看 Trace 的树状结构和火焰图,从不同的角度分析链路。

trace graph

trace flamegraph

追踪外部服务

业务流程中,我们经常会调用外部服务,比如远程 API、数据库、缓存、消息队列等,我们可以使用 opentelemetry 的 API 来追踪这些外部服务的调用。

首先需要安装对应的 opentelemetry-instrumentation 库。

1
pip install redis pymongo requests opentelemetry-instrumentation-redis opentelemetry-instrumentation-pymongo opentelemetry-instrumentation-requests

修改main.py,增加响应配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from flask import Flask
from opentelemetry import trace
from opentelemetry.exporter.jaeger.thrift import JaegerExporter
from opentelemetry.instrumentation.flask import FlaskInstrumentor
from opentelemetry.instrumentation.pymongo import PymongoInstrumentor
from opentelemetry.instrumentation.redis import RedisInstrumentor
from opentelemetry.instrumentation.requests import RequestsInstrumentor
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor


def configure_trace(app: Flask):
trace.set_tracer_provider(
TracerProvider(resource=Resource.create({SERVICE_NAME: app.name}))
)

trace.get_tracer_provider().add_span_processor(
BatchSpanProcessor(
JaegerExporter(
agent_host_name="localhost",
agent_port=6831,
)
)
)

FlaskInstrumentor().instrument_app(app)
RequestsInstrumentor().instrument()
PymongoInstrumentor().instrument()
RedisInstrumentor().instrument()

增加一个新的路由。

1
2
3
4
5
6
@app.get("/external-services")
def external_services():
requests.get("https://httpbin.org/delay/1")
MongoClient().test.test.find_one()
Redis().incr("key")
return "OK"

我们在external_services函数中调用了三个外部服务,分别是一次远程请求、一次数据库查询和一次缓存操作。

访问 http://localhost:5000/external-services ,刷新 Jaeger 的 UI,可以看到对应的 trace 已经被成功追踪。

trace externel services

trace 信息中提供了每一个外部服务调用详细信息,包括 http 请求的路径、响应状态码、数据库查询的语句、缓存操作的 key 等。

trace externel services flamegraph

调整到火焰图视图,可以清楚地看到本次请求的绝大多数时间用在了对”https://httpbin.org/delay/1"的 http 请求中(毕竟服务器延迟了一秒才返回响应内容)。

找到了性能瓶颈,也就能针对性优化代码。

1
2
3
4
5
6
@app.get("/external-services")
def external_services():
requests.head("https://www.baidu.com")
MongoClient().test.test.find_one()
Redis().incr("key")
return "OK"

重新访问 http://localhost:5000/external-services ,可以看到请求的响应时间得到了极大的优化。

trace externel services optimization

总结

通过集成 opentelemetry 与 jaeger,我们可以很方便地在 Flask 应用中追踪业务逻辑和外部服务的调用,从而更好地分析和优化业务流程。

在下一篇文章中,我们将介绍如何在复杂系统中使用 opentelemetry 进行跨服务追踪。

扫码加入技术交流群🖱️
QR code