在上一篇文章中我们介绍了 SonarQube,它是一种代码静态质量检查的工具。
但有时候一些问题在静态检查期间并不能完全暴露出来,这些问题会带到线上,在用户使用软件的时候,出现问题。
这时候就需要针对程序的运行时进行检测,这项能力也就是可观测行。也就是本篇文章要做的事。
OpenTelemetry 介绍
OpenTelemetry 是系统可观测行框架这个领域的佼佼者。
它是一个完全开源的库,它提供了一组 API/SDK 来帮助我们对程序进行监控、日志和追踪。而这三者就是目前可观测性的三大核心。
在可观测性这个领域,它几乎是唯一的事实标准。
OpenTelemetry 最初只是针对复杂的后端应用进行设计的,但现在同时也支持前端。不过 OpenTelemetry 在前端方面并不是最优的选择。
所以 ichati.cn 只在后端使用了 OpenTelemetry。前端可以选择一些更合适的工具,比如 Sentry。
OpenTelemetry 支持 C++、Java、JS、Go、Python、Rust 等 11 种主流语言。同时还可以轻松地和很多主流框架进行集成。比如 Java 的 Spring、Nodejs 的 Express、Nestjs、ASP.NET Core 等。
它的原理我就不讲了,稍微会复杂一些,通过进程间通信和上下文传播来完成的。我们主要关注如何利用 OpenTelemetry 来达到观测 ichati.cn 的目的。
OpenTelemetry 与 Nestjs 集成
ichati.cn 的后端使用 Nestjs 开发的,所以我们先将 OpenTelemetry 和项目进行集成。
这里你必须有一个 Nodejs 的项目,可以不是 Nestjs 框架的项目,比如 Express 也可以。
然后安装 OpenTelemetry 的依赖。
npm install @opentelemetry/sdk-node \ @opentelemetry/api \ @opentelemetry/auto-instrumentations-node \ @opentelemetry/sdk-metrics 接下来创建一个 instrumentation.ts 文件。 TypeScript 复制代码 import { NodeSDK } from '@opentelemetry/sdk-node'; import { ConsoleSpanExporter } from '@opentelemetry/sdk-trace-node'; import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'; import { PeriodicExportingMetricReader, ConsoleMetricExporter } from '@opentelemetry/sdk-metrics'; const sdk = new NodeSDK({ traceExporter: new ConsoleSpanExporter(), metricReader: new PeriodicExportingMetricReader({ exporter: new ConsoleMetricExporter() }), instrumentations: [getNodeAutoInstrumentations()] }); sdk .start()
然后在项目的 HTTP 服务启动之前导入这个文件。通常会是 main.ts。
重新启动项目。随着你的接口被调用,你会发现控制台会有很多类似的输出:
{ traceId: 'd4b107c7971580478eef859a2fd979aa', parentId: undefined, traceState: undefined, name: 'GET /user', id: '573d0fb86048fc11', kind: 1, timestamp: 1685629667330000, duration: 345450, attributes: { 'http.url': 'http://127.0.0.1:4401/user', 'http.host': '127.0.0.1:4401', 'net.host.name': '127.0.0.1', 'http.method': 'GET', 'http.scheme': 'http', 'http.target': '/user', 'http.user_agent': 'insomnia/2023.1.0', 'http.request_content_length_uncompressed': 0, 'http.flavor': '1.1', 'net.transport': 'ip_tcp', 'net.host.ip': '::ffff:127.0.0.1', 'net.host.port': 4401, 'net.peer.ip': '::ffff:127.0.0.1', 'net.peer.port': 53063, 'http.status_code': 200, 'http.status_text': 'OK', 'http.route': '/user' }, status: { code: 0 }, events: [], links: [] }
不仅仅是 HTTP 的入站/出站会被检测,几乎其他所有的行为都会被检测,包括TLS 的连接/断开、文件系统的调用、中间件的调用、定时任务等等。
我们仅仅是把这些数据输出到控制台中显然是不够的,因为纯文本的内容不利于数据的分析和可视化。
我们还需要有一个平台来存储、查询、分析这些日志。
SigNoz 介绍
OpenTelemetry 中有一个 Exporters 的概念,其实就是一个提供存储日志数据和搜索的系统。
这类系统的开源项目有很多,比如 Jaeger、SkyWalking、Zipkin、Haystack、SigNoz 和 Grafana 等。
它们中的大多数最初都支持系统检测功能,但是后面都演变变成了纯粹的存储和可视化系统。把检测功能让给了 OpenTelemetry,让专业的人做专业事。
这里 ichati.cn 选择了 SigNoz。
SigNoz 不是里面最好的,也不是最成熟的。选择它的理由是,它可视化功能非常强大。我认为这类系统最重要的功能就是数据可视化和查询。
安装并启动 SigNoz
安装 SigNoz 很简单,但是过程可能比较慢。
第一步,首先电脑上安装 Docker。SigNoz 只支持 Docker 一种方式部署。
第二步,运行以下三行命令。
git clone -b main https://github.com/SigNoz/signoz.git cd signoz/deploy/ ./install.sh
不出意外的话,就可以看到启动成功。
++++++++++++++++++ SUCCESS ++++++++++++++++++++++ 🟢 Your installation is complete! 🟢 Your frontend is running on http://localhost:3301
然后浏览器访问 http://localhost:3301 就可以看到 Dashboard 了。
现在的几个项目都是 SigNoz 默认的演示项目。
将 OpenTelemetry 的数据发送到 SigNoz
我们之前的代码实现是将 OpenTelemetry 的数据输出到控制台,现在我们要输出到 SigNoz。
要把 OpenTelemetry 的检测数据发送到 SigNoz,还需要安装三个包。
npm install @opentelemetry/exporter-trace-otlp-http @opentelemetry/resources @opentelemetry/semantic-conventions
然后修改代码。
import { NodeSDK } from '@opentelemetry/sdk-node'; import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'; import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; import { Resource } from '@opentelemetry/resources'; import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; const sdk = new NodeSDK({ traceExporter: new OTLPTraceExporter({ url: 'http://localhost:4318/v1/traces', }), instrumentations: [getNodeAutoInstrumentations()], resource: new Resource({ [SemanticResourceAttributes.SERVICE_NAME]: 'ichati-backend', }), }); process.on('SIGTERM', () => { sdk .shutdown() .then(() => console.log('Tracing terminated')) .catch((error) => console.log('Error terminating tracing', error)) .finally(() => process.exit(0)); }); export default sdk;
注意代码中的 4318 端口是 SigNoz 默认的端口。
最后在 main.ts 中导入 sdk 运行。
import instrumentation from './instrumentation'; instrumentation.start();
现在我们发送一些请求,就可以在 SigNoz 中查看到项目了。
SigNoz 云托管
实际上,像 ichati.cn 这类中小型团队,不想花太多时间在基础设施上面,所以运行了一段时间的 SigNoz 后,觉得管理服务器比较麻烦。
后面切换到了云服务商 elest 上面: elest.io
在 elest 上面,SigNoz 最便宜的套餐是每月 36 美元,2C、8G、80G 存储。我认为还算划算。虽然比自己买裸机要贵一点,但绝对比单独招个人来做运维划算得多。
数据的用途
以上步骤都只是一些繁琐无聊的项目搭建和配置,现在我们才正式进入主题。
我们应该关心应用的哪些数据呢?
第一是接口的延迟。
SigNoz 将接口延迟分为了三类:
- P50,也就是中位数
- P95,前 95% 的情况
- p99,也就是前 99% 的情况。
这个数据指标可以反应系统的性能和稳定性。
第二是 Rate。单位是 ops/s,也就是系统每秒处理的请求数。
这个指标主要反应系统的性能。
第三是操作的错误率。
这个指标主要反映系统的稳定性。
我们还可以通过条件筛选来查看更详细的内容,SigNoz 支持的筛选项非常多,包括时段、操作名、HTTP 请求方法、请求 URL、请求来源、Trace ID 等。
如果你想查看某些特殊的接口,我们还可以查看它的火焰图和调用堆栈,比较有利于排查问题。
以上就是常见的数据功能。
SigNoz 的强大还不止于此,它还有一些其他功能,比如自定义 Dashboard、定义 Alert、服务地图等等。
这样当系统出现故障时可以方便的通过某行方式通知到对应的人。比如邮箱、飞书、微信等等。
但目前 ichati.cn 还用不到这么多功能。所以就不对这些功能做更深入地介绍了。
篇幅所限,本文暂时结束。
后面会持续更新,让我们一起期待《大型网站重构指南》的下一篇。
如果你对最新的技术感兴趣,特别是对 Web3、AI 相关的内容感兴趣,可以添加我的微信 LZQ20130415,拉你进群交流。