opentelemetry全链路初探--埋点与jaeger

OpenTelemetry链路追踪实践

前言

某天一位业务研发老哥跑来咨询

  • 研发老哥:我的服务出现了504,但是不太清楚是哪个环节报错,每次请求需要访问4个微服务、2个数据库、1个redis、1个消息队列。。。
  • 苦逼运维:停停停,不要再说了,目前不支持链路追踪,只能手动帮你一个服务一个服务的排查了
  • 先请老哥大概描述了一下业务逻辑以及访问方式,10分钟过去了。再逐级排查每个服务以及对应访问的资源层,终于在半小时之后完成了故障定位。。。

这效率也太低了,于是,关于链路建设项目提上了议程,目标只有一个,快速定位问题,提高稳定性。而链路建设,OpenTelemetry是目前行业热点,那本运维就来研究研究

环境准备

组件版本
操作系统Ubuntu 22.04.4 LTS
opentelemetry-sdk1.35.0

安装

首先先简单说一下OpenTelemetry的数据采集流程,然后先跑起来再去讨论细节

  • OpenTelemetry就是在代码中埋入采集点进行数据采集,opentelemetry-sdk
  • 再通过固定的协议将数据上传至某个地方进行数据展示,jaeger UI
安装OpenTelemetry-sdk
/* by 01022.hk - online tools website : 01022.hk/zh/requestmethod.html */
pip3 install opentelemetry-sdk opentelemetry-exporter-otlp opentelemetry-api
安装数据展示jaeger UI
/* by 01022.hk - online tools website : 01022.hk/zh/requestmethod.html */
docker pull docker.m.daocloud.io/jaegertracing/all-in-one:latest

docker run -d --name jaeger \
  -e COLLECTOR_OTLP_ENABLED=true \
  -p 16686:16686 \
  -p 4317:4317 \
  -p 4318:4318 \
  docker.m.daocloud.io/jaegertracing/all-in-one:latest

docker启动之后访问:http://127.0.0.1:16686

watermarked-first_1

第一个例子

web服务

首先先准备一个web服务,这里我们用tornado来实现,安装tornado:pip3 install tornado

import tornado.httpserver as httpserver
import tornado.web
from tornado.ioloop import IOLoop


class TestFlow(tornado.web.RequestHandler):
    def get(self):
        self.finish('hello world')


def applications():
    urls = []
    urls.append([r'/', TestFlow])
    return tornado.web.Application(urls)

def main():
    app = applications()
    server = httpserver.HTTPServer(app)
    server.bind(10000, '0.0.0.0')
    server.start(1)
    IOLoop.current().start()


if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt as e:
        IOLoop.current().stop()
    finally:
        IOLoop.current().close()

检查是否能够正常访问:

watermarked-first_2

添加埋点
import tornado.httpserver as httpserver
import tornado.web
from tornado.ioloop import IOLoop
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter


trace.set_tracer_provider(
    TracerProvider(resource=Resource.create({SERVICE_NAME: "s1"}))
)
tracer = trace.get_tracer(__name__)
span_processor = BatchSpanProcessor(OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces"))
trace.get_tracer_provider().add_span_processor(span_processor)


class TestFlow(tornado.web.RequestHandler):
    def get(self):
        views()
        self.finish('hello world')

def views():
    span = tracer.start_span("s1-span")
    span.end()

def applications():
    urls = []
    urls.append([r'/', TestFlow])
    return tornado.web.Application(urls)

def main():
    app = applications()
    server = httpserver.HTTPServer(app)
    server.bind(10000, '0.0.0.0')
    server.start(1)
    IOLoop.current().start()


if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt as e:
        IOLoop.current().stop()
    finally:
        IOLoop.current().close()

再次访问 curl http://localhost:10000 ,打开jaeger UI查看

watermarked-first_3

watermarked-first_4

已经有数据了,刚才的埋点已经上报至jaeger UI了

埋点数据属性

丰富一下埋点数据的属性

def views():
    span = tracer.start_span("s1-span")
    span.set_attribute("name", "wilson")
    span.set_attribute("addr", "cd")
    span.end()

watermarked-first_5

增加数据库访问追踪
def views():
    span = tracer.start_span("s1-span")
    span.set_attribute("name", "wilson")
    span.set_attribute("addr", "cd")
    ctx = trace.set_span_in_context(span)
    get_db(ctx)
    span.end()

def get_db(parent_ctx):
    span = tracer.start_span("s1-span-db", context=parent_ctx)
    span.end()

watermarked-first_6

增加跨服务追踪

增加第二个web服务:s2.py

import tornado.httpserver as httpserver
import tornado.web
from tornado.ioloop import IOLoop
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator



trace.set_tracer_provider(
    TracerProvider(resource=Resource.create({SERVICE_NAME: "s2"}))
)
tracer = trace.get_tracer(__name__)
span_processor = BatchSpanProcessor(OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces"))
trace.get_tracer_provider().add_span_processor(span_processor)


class TestFlow(tornado.web.RequestHandler):
    def get(self):
        ctx = TraceContextTextMapPropagator().extract(self.request.headers)
        span = tracer.start_span("s2-span", context=ctx)
        span.end()
        self.finish('hello world')

def applications():
    urls = []
    urls.append([r'/', TestFlow])
    return tornado.web.Application(urls)

def main():
    app = applications()
    server = httpserver.HTTPServer(app)
    server.bind(20000, '0.0.0.0')
    server.start(1)
    IOLoop.current().start()


if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt as e:
        IOLoop.current().stop()
    finally:
        IOLoop.current().close()

修改s1.py

from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator
import requests

def views():
    span = tracer.start_span("s1-span")
    span.set_attribute("name", "wilson")
    span.set_attribute("addr", "cd")
    ctx = trace.set_span_in_context(span)
    get_db(ctx)
    headers = {}
    TraceContextTextMapPropagator().inject(headers, context=ctx)
    requests.get("http://localhost:20000", headers=headers)
    span.end()

watermarked-first_7

改造进k8s

jaeger

编排文件:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: jaeger
  name: jaeger
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: jaeger
  template:
    metadata:
      labels:
        app: jaeger
    spec:
      containers:
      - image: docker.m.daocloud.io/jaegertracing/all-in-one:latest
        imagePullPolicy: Always
        name: jaeger
      dnsPolicy: ClusterFirst
      restartPolicy: Always

---

apiVersion: v1
kind: Service
metadata:
  labels:
    app: jaeger-service
  name: jaeger-service
  namespace: default
spec:
  ports:
  - name: port-4317
    port: 4317
    protocol: TCP
    targetPort: 4317
  - name: port-4318
    port: 4318
    protocol: TCP
    targetPort: 4318
  - name: port-16686
    port: 16686
    protocol: TCP
    targetPort: 16686
  selector:
    app: jaeger
  type: NodePort


s2

1)制作镜像

由于在k8s集群中通过svc访问jaeger,需要改造一下s2.py

s2.py

...
import os

JAEGER_ADDR=os.environ.get('JAEGER_ADDR')
...
span_processor = BatchSpanProcessor(OTLPSpanExporter(endpoint=JAEGER_ADDR))
...

Dockerfile

FROM python:3.8

WORKDIR /opt
RUN pip3 install tornado opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp -i https://pypi.tuna.tsinghua.edu.cn/simple
ADD s2.py /opt
CMD python3 s2.py

2)编排文件

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: s2
  name: s2
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: s2
  template:
    metadata:
      labels:
        app: s2
    spec:
      containers:
      - env:
        - name: JAEGER_ADDR
          value: http://jaeger-service:4318/v1/traces
        image: s2:v1
        imagePullPolicy: Always
        name: s2
      dnsPolicy: ClusterFirst
      restartPolicy: Always
---

apiVersion: v1
kind: Service
metadata:
  labels:
    app: s2-service
  name: s2-service
  namespace: default
spec:
  ports:
  - name: s2-port
    port: 20000
    protocol: TCP
    targetPort: 20000
  selector:
    app: s2
  type: NodePort


s1

1)制作镜像

由于在k8s集群中通过svc访问s2与jaeger,需要改造一下s1.py

s1.py

...
import os

S2_ADDR=os.environ.get('S2_ADDR')
JAEGER_ADDR=os.environ.get('JAEGER_ADDR')

...
span_processor = BatchSpanProcessor(OTLPSpanExporter(endpoint=JAEGER_ADDR))
...

def views():
    span = tracer.start_span("s1-span")
    span.set_attribute("name", "wilson")
    span.set_attribute("addr", "cd")
    ctx = trace.set_span_in_context(span)
    get_db(ctx)
    headers = {}
    TraceContextTextMapPropagator().inject(headers, context=ctx)
    requests.get(S2_ADDR, headers=headers)
    span.end()

...

Dockerfile:

FROM python:3.8

WORKDIR /opt
RUN pip3 install tornado opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp -i https://pypi.tuna.tsinghua.edu.cn/simple
ADD s1.py /opt
CMD python3 s1.py

2)编排文件

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: s1
  name: s1
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: s1
  template:
    metadata:
      labels:
        app: s1
    spec:
      containers:
      - env:
        - name: S2_ADDR
          value: http://s2-service:20000
        - name: JAEGER_ADDR
          value: http://jaeger-service:4318/v1/traces
        image: s1:v1
        imagePullPolicy: Always
        name: s1
      dnsPolicy: ClusterFirst
      restartPolicy: Always

---

apiVersion: v1
kind: Service
metadata:
  labels:
    app: s1-service
  name: s1-service
  namespace: default
spec:
  ports:
  - name: s1-port
    port: 10000
    protocol: TCP
    targetPort: 10000
  selector:
    app: s1
  type: NodePort

查看结果
▶ kubectl get pod -owide
NAME                            READY   STATUS    RESTARTS         AGE     IP             NODE       NOMINATED NODE   READINESS GATES
jaeger-6669cd7c4-4pl5j          1/1     Running   0                7m31s   10.244.0.236   minikube   <none>           <none>
s1-5c569c5b4b-lctzq             1/1     Running   0                73s     10.244.0.237   minikube   <none>           <none>
s2-5bb648dcdf-mlnbj             1/1     Running   0                61s     10.244.0.238   minikube   <none>           <none>

▶ kubectl get svc
NAME             TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                                         AGE
jaeger-service   NodePort    10.106.13.217    <none>        4317:31891/TCP,4318:31997/TCP,16686:31002/TCP   5m49s
s1-service       NodePort    10.102.25.195    <none>        10000:32376/TCP                                 4m23s
s2-service       NodePort    10.103.114.198   <none>        20000:30032/TCP                                 3m40s

进行数据测试:

  • 访问s1服务

    ▶ curl http://192.168.49.2:32376
    hello world%
    
  • 查看jaeger日志,访问:http://192.168.49.2:31002/
    watermarked-first_10

总结

在第一个例子中,我们主要采集了业务服务的trace记录,即一个完整的请求需要经过的路径,包括读取数据库、跨服务请求等等

在整个跟踪过程中trace_idspan_id发挥了决定性的作用,前者为请求链路的唯一标识,串联了整个访问步骤;而后者则是链路上每一次不同的具体操作的标识

watermarked-first_8

  • 采集:通过嵌入代码埋点,采集重点监控的流程,比如数据库读写速度、下游服务速度等
  • 处理:opentelemetry-sdk对数据进行处理:过滤、缓存、合并
  • 导出:将处理过的数据,通过固定的协议(otlp协议、grpc协议、http协议等)发送到后端系统,比如jaeger

watermarked-first_9

联系我

  • 联系我,做深入的交流


至此,本文结束
在下才疏学浅,有撒汤漏水的,请各位不吝赐教...

本文来自博客园,作者:it排球君,转载请注明原文链接:https://www.cnblogs.com/MrVolleyball/p/19206221

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文连接,否则保留追究法律责任的权利。
基于数据驱动的 Koopman 算子的递归神经网络模型线性化,用于纳米定位系统的预测控制研究(Matlab代码实现)内容概要:本文围绕“基于数据驱动的 Koopman 算子的递归神经网络模型线性化,用于纳米定位系统的预测控制研究”展开,提出了一种结合数据驱动方法Koopman算子理论的递归神经网络(RNN)模型线性化方法,旨在提升纳米定位系统的预测控制精度动态响应能力。研究通过构建数据驱动的线性化模型,克服了传统非线性系统建模复杂、计算开销大的问题,并在Matlab平台上实现了完整的算法仿真验证,展示了该方法在高精度定位控制中的有效性实用性。; 适合人群:具备一定自动化、控制理论或机器学习背景的科研人员工程技术人员,尤其是从事精密定位、智能控制、非线性系统建模预测控制相关领域的研究生研究人员。; 使用场景及目标:①应用于纳米级精密定位系统(如原子力显微镜、半导体制造设备)中的高性能预测控制;②为复杂非线性系统的数据驱动建模线性化提供新思路;③结合深度学习经典控制理论,推动智能控制算法的实际落地。; 阅读建议:建议读者结合Matlab代码实现部分,深入理解Koopman算子RNN结合的建模范式,重点关注数据预处理、模型训练控制系统集成等关键环节,并可通过替换实际系统数据进行迁移验证,以掌握该方法的核心思想工程应用技巧。
基于粒子群算法优化Kmeans聚类的居民用电行为分析研究(Matlb代码实现)内容概要:本文围绕基于粒子群算法(PSO)优化Kmeans聚类的居民用电行为分析展开研究,提出了一种结合智能优化算法传统聚类方法的技术路径。通过使用粒子群算法优化Kmeans聚类的初始聚类中心,有效克服了传统Kmeans算法易陷入局部最优、对初始值敏感的问题,提升了聚类的稳定性和准确性。研究利用Matlab实现了该算法,并应用于居民用电数据的行为模式识别分类,有助于精细化电力需求管理、用户画像构建及个性化用电服务设计。文档还提及相关应用场景如负荷预测、电力系统优化等,并提供了配套代码资源。; 适合人群:具备一定Matlab编程基础,从事电力系统、智能优化算法、数据分析等相关领域的研究人员或工程技术人员,尤其适合研究生及科研人员。; 使用场景及目标:①用于居民用电行为的高效聚类分析,挖掘典型用电模式;②提升Kmeans聚类算法的性能,避免局部最优问题;③为电力公司开展需求响应、负荷预测和用户分群管理提供技术支持;④作为智能优化算法机器学习结合应用的教学科研案例。; 阅读建议:建议读者结合提供的Matlab代码进行实践操作,深入理解PSO优化Kmeans的核心机制,关注参数设置对聚类效果的影响,并尝试将其应用于其他相似的数据聚类问题中,以加深理解和拓展应用能力。
在大数据技术快速发展的背景下,网络爬虫已成为信息收集数据分析的关键工具。Python凭借其语法简洁和功能丰富的优势,被广泛用于开发各类数据采集程序。本项研究“基于Python的企查查企业信息全面采集系统”即在此趋势下设计,旨在通过编写自动化脚本,实现对企查查平台所公示的企业信用数据的系统化抓取。 该系统的核心任务是构建一个高效、可靠且易于扩展的网络爬虫,能够模拟用户登录企查查网站,并依据预设规则定向获取企业信息。为实现此目标,需重点解决以下技术环节:首先,必须深入解析目标网站的数据组织呈现方式,包括其URL生成规则、页面HTML架构以及可能采用的JavaScript动态渲染技术。准确掌握这些结构特征是制定有效采集策略、保障数据完整准确的前提。 其次,针对网站可能设置的反爬虫机制,需部署相应的应对方案。例如,通过配置模拟真实浏览器的请求头部信息、采用多代理IP轮换策略、合理设置访问时间间隔等方式降低被拦截风险。同时,可能需要借助动态解析技术处理由JavaScript加载的数据内容。 在程序开发层面,将充分利用Python生态中的多种工具库:如使用requests库发送网络请求,借助BeautifulSoup或lxml解析网页文档,通过selenium模拟浏览器交互行为,并可基于Scrapy框架构建更复杂的爬虫系统。此外,json库用于处理JSON格式数据,pandas库则协助后续的数据整理分析工作。 考虑到采集的数据规模可能较大,需设计合适的数据存储方案,例如选用MySQL或MongoDB等数据库进行持久化保存。同时,必须对数据进行清洗、去重结构化处理,以确保其质量满足后续应用需求。 本系统还需包含运行监控维护机制。爬虫执行过程中可能遭遇网站结构变更、数据格式调整等意外情况,需建立及时检测自适应调整的能力。通过定期分析运行日志,评估程序的效率稳定性,并持续优化其性能表现。 综上所述,本项目不仅涉及核心爬虫代码的编写,还需在反爬应对、数据存储及系统维护等方面进行周密设计。通过完整采集企查查的企业数据,该系统可为市场调研、信用评价等应用领域提供大量高价值的信息支持。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值