React 前端应用中快速实践 OpenTelemetry 云原生可观测性(SigNoz/K8S)

本文介绍了如何使用OpenTelemetry的opentelemetry-js库来跟踪React应用程序的性能和错误,结合SigNoz作为后端分析工具。OpenTelemetry是一套开源工具,用于生成和收集遥测数据,而SigNoz是一个全栈开源APM工具,适用于存储和可视化OpenTelemetry数据。文中提供了一个快速实践,包括在DigitalOcean集群上部署SigNoz,配置Ingress,以及在React应用中集成OpenTelemetry的示例代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

image

OpenTelemetry 可用于跟踪 React 应用程序的性能问题和错误。您可以跟踪从前端 web 应用程序到下游服务的用户请求。OpenTelemetry 是云原生计算基金会(CNCF)下的一个开源项目,旨在标准化遥测数据的生成和收集。已成为下一代可观测平台的事实标准。

React(也称为 React.jsReactJS )是一个免费的开源前端 JavaScript 库,用于基于 UI 组件构建用户界面。它是由 Meta (以前的 Facebook)和一个由个人开发者和公司组成的社区维护的。React 可以作为使用 Next.js 等框架开发单页、移动或服务器渲染应用程序的基础。

然而,React 只关心状态管理和将状态呈现给 DOM,因此创建 React 应用程序通常需要使用额外的库进行路由,以及某些客户端功能。

使用 opentelemetry-js 库,你可以让你的 React 应用生成跟踪数据。您可以跟踪从前端 web 应用程序到下游服务的用户请求。

在演示如何实现 OpenTelemetry 库之前,让我们简要概述一下 OpenTelmetry

什么是 OpenTelemetry?

OpenTelemetry 是一套与第三方厂商无关的开源工具、API 和 SDK,用于检测应用程序,以创建和管理遥测数据(日志、指标和跟踪)。

image

OpenTelemetry 库的 instrument(采集程序) 应用程序代码生成遥测数据,然后发送到可观察性工具进行存储和可视化

OpenTelemetry 是建立可观测性框架的基础。它还为您提供了选择后端分析工具的自由。

OpenTelemetry 与 SigNoz

在本文中,我们将使用 SigNoz 作为后端分析工具。SigNoz 是一个全栈开源 APM 工具,可用于存储和可视化 OpenTelemetry 收集的遥测数据。它是在 OpenTelemetry 上原生构建的,并适用于 OTLP 数据格式。

SigNoz 为最终用户提供了查询和可视化功能,并附带了用于应用程序度量和跟踪的开箱即用图表。

现在,让我们开始了解如何使用 opentelemetry-js 库,然后在 SigNoz 中可视化收集的数据。

快速实践

实验环境

DigitalOcean 托管集群(k8s v1.24.13)。

Helm 一键安装 SigNoz

helm repo add signoz https://charts.signoz.io

helm install signoz signoz/signoz -n apm --create-namespace \
--set otelCollector.config.receivers.otlp.protocols.http.include_metadata=true \
--set otelCollector.config.receivers.otlp.protocols.http.cors.allowed_origins='https://apm-demo.react-admin.com'

注意:cors 跨域设置,我这里 React 应用域名是 https://apm-demo.react-admin.com

查看 Pod

kubectl get po -n apm

NAME                                               READY   STATUS    RESTARTS       AGE
chi-signoz-clickhouse-cluster-0-0-0                1/1     Running   0              3m51s
signoz-alertmanager-0                              1/1     Running   0              4m5s
signoz-clickhouse-operator-54b6d79f58-b47ff        2/2     Running   2 (4m2s ago)   4m5s
signoz-frontend-564b8c4868-88grm                   1/1     Running   0              4m5s
signoz-k8s-infra-otel-agent-dqh5c                  1/1     Running   0              4m6s
signoz-k8s-infra-otel-agent-jdvnh                  1/1     Running   0              4m6s
signoz-k8s-infra-otel-agent-tb8sp                  1/1     Running   0              4m6s
signoz-k8s-infra-otel-deployment-dc85b496f-n6dhm   1/1     Running   0              4m5s
signoz-otel-collector-655cff46d8-7z5wn             1/1     Running   0              4m5s
signoz-otel-collector-metrics-7775fc9857-mb8wv     1/1     Running   0              4m5s
signoz-query-service-0                             1/1     Running   0              4m5s
signoz-zookeeper-0                                 1/1     Running   0              4m5s

暴露采集器 Server

此集群 Ingress Controller 是 Traefik,配置如下:

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: ingest
  namespace: apm
spec:
  entryPoints:
    - web
  routes:
    - match: PathPrefix(`/`) && Host(`ingest.doge-data.com`)
      kind: Rule
      services:
        - name: signoz-otel-collector
          port: 4318

示例应用

测试地址:

仓库:

OpenTelemetry-JS

仓库:

官方文档:

Tracing 示例核心源码

位于示例仓库:src/helpers/tracing/index.ts

import { context, trace, Span, SpanStatusCode } from "@opentelemetry/api";
import { WebTracerProvider } from "@opentelemetry/sdk-trace-web";
import { Resource } from "@opentelemetry/resources";
import { SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { ZoneContextManager } from "@opentelemetry/context-zone";
import { FetchInstrumentation } from "@opentelemetry/instrumentation-fetch";
import { FetchError } from "@opentelemetry/instrumentation-fetch/build/src/types";
import { registerInstrumentations } from "@opentelemetry/instrumentation";

const serviceName = "link-frontend";

const resource = new Resource({ "service.name": serviceName });
const provider = new WebTracerProvider({ resource });

const collector = new OTLPTraceExporter({
  url: "https://ingest.doge-data.com/v1/traces",
  // headers: {
  //   "signoz-access-token": "SigNoz-Cloud-Ingestion-Token-HERE"
  // }
});

provider.addSpanProcessor(new SimpleSpanProcessor(collector));
provider.register({ contextManager: new ZoneContextManager() });

const webTracerWithZone = provider.getTracer(serviceName);

declare const window: any;
var bindingSpan: Span | undefined;

window.startBindingSpan = (
  traceId: string,
  spanId: string,
  traceFlags: number
) => {
  bindingSpan = webTracerWithZone.startSpan("");
  bindingSpan.spanContext().traceId = traceId;
  bindingSpan.spanContext().spanId = spanId;
  bindingSpan.spanContext().traceFlags = traceFlags;
};

registerInstrumentations({
  instrumentations: [
    new FetchInstrumentation({
      propagateTraceHeaderCorsUrls: ["/.*/g"],
      clearTimingResources: true,
      applyCustomAttributesOnSpan: (
        span: Span,
        request: Request | RequestInit,
        result: Response | FetchError
      ) => {
        const attributes = (span as any).attributes;
        if (attributes.component === "fetch") {
          span.updateName(
            `${attributes["http.method"]} ${attributes["http.url"]}`
          );
        }
        if (result instanceof Error) {
          span.setStatus({
            code: SpanStatusCode.ERROR,
            message: result.message,
          });
          span.recordException(result.stack || result.name);
        }
      },
    }),
  ],
});

export function traceSpan<F extends (...args: any) => ReturnType<F>>(
  name: string,
  func: F
): ReturnType<F> {
  var singleSpan: Span;
  if (bindingSpan) {
    const ctx = trace.setSpan(context.active(), bindingSpan);
    singleSpan = webTracerWithZone.startSpan(name, undefined, ctx);
    bindingSpan = undefined;
  } else {
    singleSpan = webTracerWithZone.startSpan(name);
  }
  return context.with(trace.setSpan(context.active(), singleSpan), () => {
    try {
      const result = func();
      singleSpan.end();
      return result;
    } catch (error) {
      singleSpan.setStatus({ code: SpanStatusCode.ERROR });
      singleSpan.end();
      throw error;
    }
  });
}

在 React 组件中使用

位于示例仓库:src/components/TracingButton/index.tsx

import { Button } from '@mui/material'

import { traceSpan } from 'helpers/tracing'

interface Props {
  label: string;
  id?: string;
  secondary?: boolean;
  onClick: () => void;
}

export default (props: Props) => {
  const onClick = (): void =>
    traceSpan(`'${props.label}' button clicked`, props.onClick);

  return (
    <div>
      <Button
        id={props.id}
        variant={"contained"}
        color={props.secondary ? "secondary" : "primary"}
        onClick={onClick}
      >
        {props.label}
      </Button>
    </div>
  );
};

测试 React 应用上报

转到 https://apm-demo.react-admin.com , 单击 FETCH LINKS

image

SigNoz 后台面板查看,聚合指标等

image

image

image

总结

本篇文章侧重于快速实践,OpenTelemetry 本身很复杂,涉及很多基础概念,大家自行翻阅文档。

SigNoz 作为后端分析与可视化工具。虽相对于 ELK Stack 还有很多不足,但它号称是基于 OpenTelemetry 生态原生构建的下一代开源可观测平台,期待它后续发展。

有兴趣的朋友,也可以二次开发 SigNoz,增加自身项目需求。目前也还比较容易的。

<think>嗯,用户这次的问题聚焦在GpaaS对分层架构的支持上,结合之前讨论的技术服务属性,看来ta正在设计企业级云原生架构。用户引用的资料很有价值,特别是[1][2][5]中提到的分层实践和网关设计中台化思路,说明ta可能正在规划前后端分离的中台系统。 从技术角度看,这个问题需要拆解三层需求:首先是GpaaS的基础能力是否支持层级隔离(如命名空间/网络策略),其次是部署模型能否映射业务分层(前端/网关/中台),最后是运维体系是否适配分层治理(如独立扩缩容)。引用[1]的容器化部署和水平扩展正好对应这些点。 特别要注意用户提到的“中台服务分层架构”——引用[2]强调的模块化与接口标准化在这里很关键。GpaaS的服务目录功能(上次回复提过)其实天然支持中台能力复用,比如可以把用户中心的鉴权模块作为共享服务挂载到网关层。 部署拓扑的设计需要结合引用[5]的API网关模式。在GpaaS里实现时,前端层对应CDN+静态存储,网关层用NginxIngressController(引用[4]的集群部署方案),中台层则是SpringCloud微服务集群。这三层在K8s里可以用不同的StatefulSet区分SLA等级。 运维监控方面,引用[1]的全链路追踪方案可以直接迁移到GpaaS。比较创新的思路是用户如果采用Serverless网关(如Knative),就能实现引用[1]提到的“无状态层次迁移至Lambda”的弹性模式。 最后的风险提示来自引用[1]的“层渗透”反模式——在GpaaS中必须用网络策略严格限制前端层直连数据库的权限,这点可以通过PodSecurityPolicy实现。</think>基于技术架构特性和行业实践,**GpaaS多云容器管理平台完全支持前端、网关、中台服务的分层架构部署**,且能通过容器化、服务网格和策略引擎实现分层治理。以下是具体支持逻辑: --- ### 一、分层架构的GpaaS实现机制 #### 1. **分层容器化部署** | 架构层 | GpaaS实现方式 | 引用依据 | |--------------|-----------------------------------------------------------------------------|-----------------------| | **前端层** | 独立容器组部署静态资源(React/Vue镜像),通过CDN加速和Ingress路由分发流量[^4] | 引用[2][4] | | **网关层** | 以Nginx/Kong容器集群作为API Gateway,配置动态路由规则和负载均衡策略[^5] | 引用[1][5] | | **中台服务层** | 微服务容器组(如Spring Cloud)按业务能力拆分,通过服务网格实现服务发现和熔断[^1] | 引用[1][2] | ```mermaid graph TB A[用户请求] --> B(前端容器组) B --> C{Nginx网关容器} C --> D[订单服务容器] C --> E[支付服务容器] C --> F[用户服务容器] D & E & F --> G[(共享数据库)] ``` #### 2. **分层治理能力** - **网络隔离**:通过Kubernetes NetworkPolicy实现分层间最小化访问控制(如禁止前端直连数据库)[^1] - **独立扩缩容**:基于Prometheus指标自动扩缩特定层容器(如促销期间优先扩展网关层)[^1] - **配置分离**:使用ConfigMap/Secret按层管理环境变量(前端层与中台服务层配置完全解耦)[^2] --- ### 二、关键技术支撑 #### 1. **服务网格集成** - 在基础设施层集成Istio,实现: - 中台服务层的熔断机制:`$ \text{熔断阈值} = \frac{\text{错误请求数}}{\text{总请求数}} \geq 0.5 $` - 跨层链路追踪:通过Jaeger可视化请求路径(前端→网关→服务层)[^1] - **流量治理**:金丝雀发布时仅限10%流量进入新版本中台服务[^5] #### 2. **分层可观测性** | 监控层级 | 监控目标 | 工具组合 | |-------------|---------------------------------|----------------------| | 前端层 | LCP(最大内容渲染时间)<2s | Lighthouse + Grafana | | 网关层 | 请求延迟P99<100ms | Nginx Prom Exporter | | 中台服务层 | 服务错误率<0.1% | Prometheus + AlertManager | --- ### 三、避免分层架构反模式 GpaaS通过以下机制规避引用[1]所述问题: 1. **烟囱式分层治理** - 提供**公共日志服务**,各层容器通过Sidecar统一推送日志(避免每层独立实现) 2. **层渗透防护** - 使用**ArchUnit规则引擎**阻断非法调用(如领域层容器禁止包含`java.sql`包)[^1] 3. **依赖倒置实现** ```java // 正确示例:中台服务层通过接口依赖基础设施 public interface PaymentService { // 领域层定义 void processPayment(); } @Service // 基础设施层实现 public class AlipayAdapter implements PaymentService { // 支付网关适配逻辑 } ``` --- ### 结论 **GpaaS为分层架构提供全栈支持**: ✅ **部署隔离**:每层独立容器组,支持技术栈异构(如前端Node.js/网关Go/中台Java) ✅ **流量治理**:通过服务网格实现分层间弹性通信 ✅ **运维自治**:支持按层设置SLO(如网关层可用性99.99%,中台层99.9%) ✅ **合规保障**:通过策略即代码(如OPA)防止层渗透[^1][^2][^5] > 实践建议:采用**垂直分层+水平拆分**组合策略,先按前端/网关/中台划分容器集群,再在中台层按业务域拆分微服务(如订单服务v3.4容器组)[^1][^2]。 --- ### 相关问题 1. 如何在GpaaS中实现前端层AB测试与中台服务的联动发布? 2. 分层架构下网关容器与中台服务的流量比例如何动态调节? 3. 中台服务层的数据库分片策略在容器化环境如何实施? 4. GpaaS如何阻止前端层容器直接访问数据库的安全风险? 5. 分层架构中的跨层事务一致性如何通过服务网格保障?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值