tencent cloud

腾讯云可观测平台

动态与公告
产品动态
产品简介
产品概述
产品优势
基本功能
基本概念
应用场景
使用限制
购买指南
云产品监控
应用性能监控
终端性能监控
前端性能监控
云拨测
Prometheus 监控服务
Grafana 服务
事件总线
云压测
快速入门
监控概览
实例分组
云产品监控
应用性能监控
云拨测
云压测
Prometheus 监控服务
Grafana 服务
创建 Dashboard
事件总线
告警服务
云产品监控
云产品监控指标
控制台操作指南
云服务器监控组件
云产品监控对接 Grafana
故障处理
实践教程
应用性能监控
应用性能监控简介
接入指南
控制台操作指南
实践教程
参考信息
常见问题
终端性能监控
终端性能监控概述
控制台操作指南
接入指南
实践教程
前端性能监控
前端性能监控简介
控制台操作指南
接入指南
常见问题
云拨测
产品简介
控制台操作指南
常见问题
云压测
云压测概述
控制台操作指南
实践教程
JavaScript API 列表
常见问题
Prometheus 监控
Prometheus 监控简介
接入指南
控制台操作指南
实践教程
Terraform
常见问题
Grafana 服务
产品简介
控制台操作指南
Grafana 平台常用功能指引
常见问题
Dashboard
什么是 Dashboard
控制台操作指南
告警管理
控制台操作指南
故障处理
常见问题
事件总线
事件总线简介
控制台操作指南
实践教程
常见问题
报表管理
常见问题
腾讯云可观测平台常见问题
告警服务相关
一般性问题
监控图表相关
云服务器监控组件相关
动态阈值告警相关
云监控对接 Grafana 相关
文档阅读指南
相关协议
应用性能监控服务等级协议
APM 隐私协议
APM 数据处理和安全协议
前端性能监控服务等级协议
终端性能监控服务等级协议
云拨测服务等级协议
Prometheus 监控服务服务等级协议
Grafana 服务服务等级协议
云压测服务等级协议
云压测使用限制
Cloud Monitor Service Level Agreement
词汇表

通过 eBPF 探针接入 Go 应用

PDF
聚焦模式
字号
最后更新时间: 2025-10-13 19:10:48
说明:
OpenTelemetry 是工具、API 和 SDK 的集合,用于检测、生成、收集和导出遥测数据(指标、日志和跟踪),帮助用户分析软件的性能和行为。关于 OpenTelemetry 的更多信息请参考 OpenTelemetry 官方网站
eBPF(扩展伯克利包过滤器)允许在内核空间运行安全的用户定义代码,而不需要修改内核源代码或加载内核模块。eBPF 与 OpenTelemetry 结合,使得用户无需在代码中手动埋点就可实现完整的监控能力。
腾讯云 eBPF 探针基于开源社区 opentelemetry-go-instrumentation 项目二次开发,功能和特性仍在快速迭代中。
本文将介绍如何通过腾讯云 eBPF 探针接入 Go 应用。

前提条件

说明:
eBPF 目前仅支持 Linux,内核版本需要在4.19及以上。
暂不支持 macOS 和 Windows 的系统(包括运行在这类宿主机中的 Linux 容器)。
Go 版本需在1.18及以上,支持的库和框架如下:
库/框架
版本
database/sql
go1.12 to go1.23.0
github.com/segmentio/kafka-go
v0.4.1 to v0.4.47
google.golang.org/grpc
v1.14.0 to v1.66.0
net/http
go1.12 to go1.22.6
注意:
基于net/http的 Web 框架,如 Gin 等都支持。

接入流程

步骤1:获取接入点和 Token

1. 登录 腾讯云可观测平台 控制台。
2. 在左侧菜单栏中选择应用性能监控 > 应用列表,单击接入应用
3. 在右侧弹出的接入抽屉框中,单击 Go 语言。
4. 接入 Go 应用页面,选择您所要接入的地域以及业务系统
5. 选择接入协议类型OpenTelemetry
6. 上报方式选择您所想要的上报方式,获取您的接入点Token
说明:
内网上报:使用此上报方式,您的服务需运行在腾讯云 VPC。通过 VPC 直接联通,在避免外网通信的安全风险同时,可以节省上报流量开销。
外网上报:当您的服务部署在本地或非腾讯云 VPC 内,可以通过此方式上报数据。请注意外网通信存在安全风险,同时也会造成一定上报流量费用。

步骤2:下载探针

linux/amd64探针 下载
linux/arm64探针 下载
赋予探针执行权限:
chmod +x otel-go-instrumentation

步骤3:接入并上报

1. 确认应用的运行路径

将需要接入的应用编译成二进制文件,确认应用启动时的完整路径

2. 运行探针

运行探针需要有系统的 root 权限,使用以下命令接入:
sudo OTEL_GO_AUTO_TARGET_EXE=</path/to/executable_binary> \\
OTEL_SERVICE_NAME=<serviceName> \\
OTEL_EXPORTER_OTLP_PROTOCOL=grpc \\
OTEL_TRACES_EXPORTER=otlp \\
OTEL_EXPORTER_OTLP_ENDPOINT=<endpoint> \\
OTEL_RESOURCE_ATTRIBUTES=token=<token>,host.name=<hostName> \\
./otel-go-instrumentation
对应字段的说明如下:
</path/to/executable_binary> :应用可执行文件的路径,这里一定要是绝对路径
<serviceName>:应用名,多个使用相同 serviceName 接入的应用进程,在 APM 中会表现为相同应用下的多个实例。应用名最长63个字符,只能包含小写字母、数字及分隔符“ - ”,且必须以小写字母开头,数字或小写字母结尾。
<endpoint>:前置步骤中拿到的接入点,注意这里必须添加http://前缀。
<token>:前置步骤中拿到业务系统 Token。
<hostName>:该实例的主机名,是应用实例的唯一标识,通常情况下可以设置为应用实例的 IP 地址。
下述内容以应用名为 myService,运行路径为/root,业务系统 Token 为 myToken,主机名为 192.168.0.10,接入点以http://ap-guangzhou.apm.tencentcs.com:4317为例,完整的启动命令为:
sudo OTEL_GO_AUTO_TARGET_EXE=/root/myService \\
OTEL_SERVICE_NAME=myService \\
OTEL_EXPORTER_OTLP_PROTOCOL=grpc \\
OTEL_TRACES_EXPORTER=otlp \\
OTEL_EXPORTER_OTLP_ENDPOINT=http://ap-guangzhou.apm.tencentcs.com:4317 \\
OTEL_RESOURCE_ATTRIBUTES=token=myToken,host.name=192.168.0.10 \\
./otel-go-instrumentation
了解更多接入方式,请参考 官方文档

3. 运行应用

运行应用,当发生接口调用时,探针会输出日志。如果应用停止运行,探针不需要停止,应用下次启动时会自动埋点。

4. 接入验证

接入侧
探针的日志输出中含有instrumentation loaded successfully字段表示接入成功:
{"level":"info","ts":1725609047.2234442,"logger":"go.opentelemetry.io/auto","caller":"cli/main.go:119","msg":"starting instrumentation..."}
{"level":"info","ts":1725609047.2235398,"logger":"Instrumentation.Manager","caller":"instrumentation/manager.go:195","msg":"loading probe","name":"net/http/server"}
{"level":"info","ts":1725609047.388379,"logger":"go.opentelemetry.io/auto","caller":"cli/main.go:115","msg":"instrumentation loaded successfully"}
APM 控制台
在有正常流量的情况下,应用性能监控 > 应用列表 中将展示接入的应用。单击应用名称/ID 进入应用详情页,再选择实例分析,即可看到接入的应用实例。由于可观测数据的处理存在一定延时,如果接入后在控制台没有查询到应用或实例,请等待30秒左右。

接入错误排查

探针仅有一条日志输出:
{"level":"info","ts":1725609014.2038825,"logger":"go.opentelemetry.io/auto","caller":"cli/main.go:86","msg":"building OpenTelemetry Go instrumentation ...","globalImpl":false}
一般是如下两种情况:
1. 应用没有启动。
2. 应用的启动路径(OTEL_GO_AUTO_TARGET_EXE)错误。
探针日志报错:
traces export: failed to exit idle mode: dns resolver: missing address
一般是接入点没有添加http://前缀导致。

添加手动埋点

为了增加埋点的灵活性,eBPF 探针支持用户自定义埋点,用户可以添加与业务逻辑和内部功能相关的自定义 span 信息。示例代码如下:
import (
...
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
...
)
...

var tracer = otel.Tracer("rolldice") // 创建 Tracer

...

func (s *Server) rollDice(w http.ResponseWriter, req *http.Request) {
ctx, span := tracer.Start(req.Context(), "roll") // 新建一个 span
defer span.End()

n := s.rand.Intn(6) + 1

_, err := s.db.ExecContext(ctx, "INSERT INTO results (roll_value) VALUES (?)", n)
if err != nil {
panic(err)
}

span.SetAttributes(attribute.Int("roll.value", n)) // 添加 span attributes

fmt.Fprintf(w, "%v", n)
}

eBPF 接入代码示例

说明:
以下将给出多组使用 eBPF 探针接入 APM 的代码示例,帮助用户更方便地把业务的链路数据上报到 APM。
使用 eBPF 探针上报数据时,几乎不需要更改已有的业务代码,但是需要一些细节,例如代码中 context 的传递。

对外提供接口的数据库操作程序

本项目的主要功能是对外提供一个 HTTP 接口,调用此接口可以操作 sqlite 数据库。基于 Go 语言的标准库 net/httpdatabase/sql 实现。
package main
import (
"database/sql"
"fmt"
"net/http"
"os"
_ "github.com/mattn/go-sqlite3"
"go.uber.org/zap"
)

const (
sqlQuery = "SELECT * FROM contacts"
dbName = "test.db"
tableDefinition = `CREATE TABLE contacts (
contact_id INTEGER PRIMARY KEY,
first_name TEXT NOT NULL,
last_name TEXT NOT NULL,
email TEXT NOT NULL,
phone TEXT NOT NULL);`

tableInsertion = `INSERT INTO 'contacts'
('first_name', 'last_name', 'email', 'phone') VALUES
('Moshe', 'Levi', 'moshe@gmail.com', '052-1234567');`
)

type Server struct {
db *sql.DB
}
// 初始化数据库
func CreateDb() {
file, err := os.Create(dbName)
if err != nil {
panic(err)
}
err = file.Close()
if err != nil {
panic(err)
}
}

func NewServer() *Server {
CreateDb()
database, err := sql.Open("sqlite3", dbName)
if err != nil {
panic(err)
}
_, err = database.Exec(tableDefinition)
if err != nil {
panic(err)
}
return &Server{
db: database,
}
}

func (s *Server) queryDb(w http.ResponseWriter, req *http.Request) {
ctx := req.Context()
conn, err := s.db.Conn(ctx)
if err != nil {
panic(err)
}
// 注意这里一定要传递 `ctx` 或者 `req.Context()`,使 HTTP 请求和数据库的操作记录可以保持在一条链路中。
// 如果在这里没有传递请求的 context,写成了 `s.db.Exec(tableInsertion)`,就会导致链路中断,HTTP span 和 database span 出现在两条链路中!
_, err = s.db.ExecContext(ctx, tableInsertion)
if err != nil {
panic(err)
}
rows, err := conn.QueryContext(req.Context(), sqlQuery)
if err != nil {
panic(err)
}
logger.Info("queryDb called")
for rows.Next() {
var id int
var firstName string
var lastName string
var email string
var phone string
err := rows.Scan(&id, &firstName, &lastName, &email, &phone)
if err != nil {
panic(err)
}
fmt.Fprintf(w, "ID: %d, firstName: %s, lastName: %s, email: %s, phone: %s\\n", id, firstName, lastName, email, phone)
}
}
注意:
请关注代码中的注释说明,一定要正确传递 context 才能正确构造 span 之间的父子关系,保证链路不会中断。

帮助和支持

本页内容是否解决了您的问题?

填写满意度调查问卷,共创更好文档体验。

文档反馈