云函数 应用性能观测

应用性能监控(Application Performance Management ,APM)是一款应用性能管理平台,基于实时的多语言应用探针全量采集技术,提供分布式应用性能分析和故障自检能力,全方位保障系统的可用性和稳定性。协助你在复杂的业务系统快速定位性能问题,降低 MTTR(平均故障恢复时间)。实时了解并追踪应用性能情况,提升用户体验。
APM 基于 OpenTracing 开源协议,支持多种主流框架和编程语。

相关产品
云函数 产品页,含版本规格、价格与购买入口。

一、简介

SCF 已经接入 APM 产品,开启函数应用性能观测后,SCF 将使用基于 OpenTracing 的 Jaeger 实现将函数运行总耗时、冷启动耗时、执行耗时三段关键时间上报至 APM:

·函数运行总耗时:作为父分段上报,对应 APM 链路中 invocation 接口,表示函数从接收到调用命令开始到函数执行完成总耗时。

·冷启动耗时:作为函数运行总耗时的子分段上报,对应 APM 链路中initialization接口,表示函数从接收到调用命令开始,到实例准备完成、函数初始化逻辑执行完成耗时。(该分段仅出现在冷启动调用请求中)

·执行耗时:作为函数运行总耗时的子分段上报,对应 APM 链路中execution接口,表示入口函数执行耗时(事件函数)或完成9000端口监听后每次执行耗时(Web 函数和镜像函数)。

除此之外,还可以根据业务需要自定义埋点进行上报。

注意:APM 为独立计费产品,APM 计费方式请参考 计费概述。

二、权限说明

1. 首次使用请按照操作步骤引导完成授权。子账号如无权限操作,请联系主账号管理员完成授权流程。

2. 为保证 APM 数据正常查看,子账号下至少拥有应用性能观测 APM 只读权限 QcloudAPMReadOnlyFullAccess。主账号为子账号授权方法请参见 授权管理。

三、设置应用性能观测

1. 登录 Serverless 控制台,单击左侧导航栏中的函数服务。

2. 在页面上方选择地域,单击需要进行应用性能观测配置的函数名。

3. 在函数配置页面,选择右上角的编辑,勾选启用应用性能观测。


4. APM 的资源单元为业务系统,请选择数据上报的地域并选择对应的业务系统。新建业务系统请参考 应用性能观测资源管理。

5. 单击保存完成函数应用性能观测配置。配置更新后对函数 $LATEST 版本即刻生效,已发布版本配置不会变更。

四、查看函数应用性能观测配置

1. 登录 Serverless 控制台,单击左侧导航栏中的函数服务。

2. 在页面上方选择地域,并单击函数名,即可在“函数配置”中查看当前应用性能观测配置。

五、查看函数及所在业务系统健康情况

函数应用性能观测功能开启后,SCF 将上报函数执行基本信息到指定业务系统,一个函数将对应业务系统下的一个应用。应用命名方式为:命名空间名称/函数名称。

六、查看函数所在业务系统健康情况

1. 在函数应用性能观测配置中单击业务系统,即可跳转至应用性能观测控制台。

2. 单击左侧导航栏中的应用监控-应用列表,可查看业务系统下各应用的健康情况。

3. 单击系统看板右上角的拓扑,可查看该业务系统下应用之间的调用关系拓扑图。

七、函数调用链路分析

1. 开启了应用性能观测的函数,可在函数执行日志中获取到 APM TraceId。函数执行未重试场景下,TraceId 与函数执行 RequestId 一一对应;函数重试场景下,多次重试具有相同的 RequestId,TraceId 与函数执行 RequestId 为多对一的关系。


2. 登录 应用性能观测控制台,单击左侧导航栏链路追踪 > 调用查询。

3. 在页面上方选择地域和业务系统。

4. 输入需要查询的 TraceID,单击查询。以下图一个冷启动调用的函数请求为例,查询结果中的内容含义如下:

·TraceID/SpanID:本次请求的 TraceID(地域唯一)/当前 Span 的 SpanID(Trace 内唯一)

·状态:状态码为 200 时为正常,其他为异常。

·响应时间:分段运行耗时。

·服务端:命名空间/函数名称。

·接口:invocation表示函数运行总耗时,initialization表示冷启动耗时,execution表示函数执行耗时。

·调用类型/状态码:云函数平台上报分段调用类型记为 scf,状态码为函数请求状态码。


5. 单击请求详情,可查看链路瀑布图及函数执行日志。


八、SCF 自定义参数及含义

Key 含义 示例
fass.status_code 函数执行 状态码。 200
component 固定字段,填 scf。 scf
faas.namespace 函数所在命名空间。 default
faas.alias 触发的函数别名,可能为空。 $DEFAULT
faas.version 触发的函数版本。 $LATEST
faas.runtime 函数运行环境。 Python3.6
faas.trigger 函数触发源。 YUNAPI
faas.request_id 函数请求 id。 97fa4d93-xxxx-xxxx-xxxx-66e60cdcb977
faas.async_execution 异步执行函数标识,true 表示是,false 表示否。 false
faas.websocket websocket 函数标识,true 表示是,false 表示否。 false
faas.function_type 函数类型,事件函数或 Web 函数。 Event
faas.region 函数所在地域,参考函数 支持地域。 ap-guangzhou
faas.retrynum 请求重试次数。 0
faas.coldstart 是否为冷启动调用,true 表示是,false 表示否。 true

九、自定义埋点上报

启用应用性能观测后,平台会默认上报函数运行总耗时、冷启动耗时(仅在冷启动调用下上报)、执行耗时三段基本时间到 APM。除此之外,你还可以通过在需要监控的代码段前后埋点进行自定义上报。下文将描述如何进行自定义上报:

注意:SCF 平台使用基于 OpenTracing 的 Jaeger 上报函数信息到 APM,在函数代码中进行自定义上报时,如需自定义上报 Span 与 SCF 平台上报 Span 使用同一个 TraceId 进行串联,自定义上报需要使用 Jaeger SDK 或与 Jaeger 兼容的 SDK。

十、获取 TraceId

启用应用性能观测后,平台默认上报 APM 数据的同时,会将 APM 的 Trace 信息注入到函数环境中,可通过如下方式进行获取:

·非镜像部署的事件函数:
可在函数入参 context 中获取 function_trace 中的 Uber-Trace-Id。

·镜像部署函数:
可在函数请求 header 中获取 Uber-Trace-Id。

十一、自定义上报 Span

Node.jsPython

1. 请参考 依赖安装 安装 jaeger-client SDK。2. 创建 Node.js 函数,启用应用性能观测,并参考以下示例进行自定义埋点:'use strict';const initTracer = require('jaeger-client').initTracer;const spanContext = require('jaeger-client').SpanContext;function sleep (time) { return new Promise((resolve) => setTimeout(resolve, time));}const config = { serviceName: 'namespace/functionname', // 服务名称,根据业务需要自行修改,对应 APM 业务系统中的应用名称,SCF 默认上报应用名称命名规范为 命名空间/函数名称 sampler: { type: 'const', param: 1 }, reporter: { logSpans: true, collectorEndpoint: 'http://ap-shanghai.apm.tencentcs.com:14268/api/traces' //应用性能观测配置中的接入点信息 } }; const options = { tags: { token: 'IJc**************aQ'//应用性能观测配置中的 Token 信息 } };// ******** 初始化 tracer 实例对象 ********const tracer = initTracer(config, options);exports.main_handler = async (event, context) => { // ******** 获取 context 中 trace 信息,未启用应用性能观测状态下,该信息不存在 ******** const obj = JSON.parse(context['function_trace']) const TraceId = obj['Uber-Trace-Id'][0] // ******** 自定义创建一个子 span,命名为 custom-span ******** const ScfSpanContext = spanContext.fromString(TraceId) const span = tracer.startSpan('custom-span', { childOf: ScfSpanContext }) sleep(200).then(() => { console.log("custom span"); // ******** 标记 Span 结束 ******** span.finish(); tracer.close(); }) // ******** 自定义设置 span 的标签(可选,支持多个)******** //span.setTag('span.kind', 'server'); //span.setTag('tagName', 'tagValue'); //span.log({ event: 'timestamp', value: Date.now() }); return ("hello from SCF");}3. 接入点信息与 Token 可参考上文 查看函数应用性能观测配置 获取。4. 函数调用后,各 Span 信息会上报至指定的 APM 业务系统。

注意:1. Jaeger 最新版 Python SDK 已经不支持 HTTP 协议上报,因此在 Python 运行环境下,推荐使用 Open Telemetry SDK。

注意:2. Open Telemetry SDK 仅支持 Python 3.7 及以上版本,这篇提供基于镜像部署的自定义上报示例。

1. 请参考 使用镜像部署函数 完成前提条件准备。

2. 参考以下示例代码准备镜像函数源码。

·index.py

from flask import Flask, requestimport timefrom opentelemetry import tracefrom opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporterfrom opentelemetry.sdk.resources import Resourcefrom opentelemetry.sdk.trace import TracerProviderfrom opentelemetry.sdk.trace.export import BatchSpanProcessorfrom opentelemetry.trace import NonRecordingSpan, SpanContext, TraceFlagsfrom opentelemetry.trace import set_span_in_contextclass OpenTelemetrySLSProvider(object): def __init__(self, service="", token="", endpoint=""): ''' :param service: Your Application Service Name :param service: server name :param token: scf project ''' self.sls_otel_endpoint = endpoint self.resource = Resource(attributes={ "service.name": service, "token": token}) def initTracer(self): trace.set_tracer_provider(TracerProvider(resource=self.resource)) otlp_exporter = OTLPSpanExporter(endpoint=self.sls_otel_endpoint) trace.get_tracer_provider().add_span_processor(BatchSpanProcessor(otlp_exporter))server = "namespace/functionname" # 服务名称,根据业务需要自行修改,对应 APM 业务系统中的应用名称,SCF 默认上报应用名称命名规范为 命名空间/函数名称endpoint = "ap-shanghai.apm.tencentcs.com:4317" # 应用性能观测配置中的接入点信息instance = "apm-Q*******Q" # 应用性能观测配置中的业务系统 ID 信息token = "Tr***********a" # 应用性能观测配置中的 Token 信息# ******** 初始化 tracer 实例对象 ********sls_ot_provider = OpenTelemetrySLSProvider(service=server, token=token, endpoint=endpoint)sls_ot_provider.initTracer()tracer = trace.get_tracer("__name__")app = Flask(__name__)# ******** 事件函数 ********@app.route('/event-invoke', methods=['POST'])def invoke(): # ******** 获取请求 header 中 trace 信息,未启用应用性能观测状态下,该信息不存在 ******** TraceId = request.headers.get("Uber-Trace-Id", "") SCF_span = TraceId.split(":") tid = int(SCF_span[0], 16) sid = int(SCF_span[1], 16) fid = int(SCF_span[3], 16) span_context = trace.SpanContext( trace_id=tid, span_id=sid, is_remote=True, trace_flags=trace.TraceFlags(fid), ) ctx = trace.set_span_in_context(NonRecordingSpan(span_context)) # ******** 自定义创建一个子 span,命名为 custom-span ******** child = tracer.start_span("custom-span", context=ctx) time.sleep(0.2) print("child") # ******** 标记 Span 结束 ******** child.end() return "hello from SCF"# ******** Web 函数 ********@app.route('/web-invoke/python-flask-http', methods=['POST', 'GET'])def web_invoke(): # ******** 获取请求 header 中 trace 信息,未启用应用性能观测状态下,该信息不存在 ******** TraceId = request.headers.get("Uber-Trace-Id", "") SCF_span = TraceId.split(":") tid = int(SCF_span[0], 16) sid = int(SCF_span[1], 16) fid = int(SCF_span[3], 16) span_context = trace.SpanContext( trace_id=tid, span_id=sid, is_remote=True, trace_flags=trace.TraceFlags(fid), ) ctx = trace.set_span_in_context(NonRecordingSpan(span_context)) # ******** 自定义创建一个子 span,命名为 custom-span ******** child = tracer.start_span("custom-span", context=ctx) time.sleep(0.2) print("child") # ******** 标记 Span 结束 ******** child.end() return "hello from SCF"if __name__ == '__main__': app.run(host='0.0.0.0', port=9000)

·requirements.txt

Flask==1.1.2opentelemetry-api==1.7.1opentelemetry-exporter-jaeger==1.7.1opentelemetry-exporter-jaeger-proto-grpc==1.7.1opentelemetry-exporter-jaeger-thrift==1.7.1opentelemetry-exporter-otlp==1.7.1opentelemetry-instrumentation-flask==0.26b1opentelemetry-instrumentation-requests==0.26b1opentelemetry-sdk==1.7.1opentelemetry-semantic-conventions==0.26b1

·dockerfile

FROM python:3.7.12-slimWORKDIR /usr/src/appCOPY ./requirements.txt ./RUN python -m pip install –upgrade pip && pip install –no-cache-dir -r ./requirements.txtCOPY . .CMD [ "python", "-u", "./index.py" ]

3. 将步骤2中的 index.py、requirements.txt、dockerfile 文件放在同一目录下,参考 使用控制台创建函数 完成镜像构建与函数创建。上述示例代码执行后上报的链路详情如下:

赞 (0)
上一篇 4天前
下一篇 4天前