Go 探针
安装Go 探针
使用UniAgent Linux版
当服务器上存在多种语言的应用时,推荐使用UniAgent安装,安装步骤请参见UniAgent Linux部署说明。
影响:ps进程列表中原本Go应用进程的位置会被agentinject替换,Go应用进程的父进程id会变为1,如果Go应用由启停脚本或服务维护,请务必在测试环境验证启停脚本或服务的有效性并做适当修改。
方式一:修改应用启动脚本
注意:仅适用于版本大于2.5.7.0版本的探针
UniAgent安装完毕后,在应用启动脚本内增加/opt/tingyun-oneagent/agent/go_current/bin/agentinject
,重启应用即可。
例如,原Go应用启动脚本如下:
/opt/go-app/go-demo
更改后的应用启动脚本如下:
...
/opt/tingyun-oneagent/agent/go_current/bin/agentinject /opt/go-app/go-demo
卸载探针:将应用启动脚本恢复为原来的脚本并重启应用。
嵌码原理:使用bin/agentinject
启动Go应用, agentinject利用ptrace技术替换Go应用中相关的函数并获取性能数据。
方式二:修改UniAgent配置
注意:仅适用于版本大于2.5.8.0版本的探针
手工开启Go应用嵌码开关
修改
/opt/tingyun-oneagent/conf/interceptor.conf
文件中go_enabled
为go_enabled=true
重启UniAgent服务
sudo systemctl restart tingyun-oneagent
Go探针产生的trace需要通过中间进程转发到Collector,重启UniAgent服务后,如果配置是
go_enabled=true
,会启动中间进程接收trace数据。如果不重启UniAgent服务,Go探针日志'golang_agent.log'里面会有
Connect file:///opt/tingyun-oneagent/agent/go_version/run/goagent.sock failed
日志,说明和中间进程通讯失败。手工修改Go应用的识别模式
Go应用识别采用白名单的方式,修改
/opt/tingyun-oneagent/conf/interceptor.conf
文件中的go.namelist=
,多个名称以英文逗号分隔。例如要监控
/app/go-demo1
和/test/go-test2
, 需要修改配置为go.namelist=go-demo1,go-test2
重启Go应用
如果Go应用是通过shell脚本直接启动,则可以直接重启Go应用。
如果Go应用是直接执行命令启动,并且当前shell是在UniAgent服务启动之前打开的,需要新开一个shell进行操作或执行
sh -c 'Go应用及参数'
启动;如果当前shell是在UniAgent服务启动之后打开的,则可以直接重启Go应用。如果Go应用是通过系统服务直接启动(systemctl),需要修改服务为
/bin/sh -c 'Go应用及参数'
的方式启动。注入探针需依赖于父进程在启动Go应用时对应用嵌码,这要求Go应用的父进程必须是动态链接C运行时库的进程,如果Go应用是由静态链接的进程拉起,则无法嵌码。
对于运行在容器内的Go应用需满足以下要求:
C 运行时依赖:
容器基础镜像必须包含完整的 C 运行时库(glibc 或 musl libc)
不支持使用无 C 运行时的精简镜像,如 scratch 或 busybox:musl。
可以通过检查命令
docker exec -it 容器名称 cat /proc/self/maps
的输出结果是否包含*.so
来确认容器镜像是否包含完整的 C 运行时库。进程关系依赖:
注入探针需依赖于父进程在启动Go应用时对应用嵌码,这要求Go应用的父进程必须是动态链接C运行时库的进程,如果Go应用是由静态链接的进程拉起,则无法嵌码。
如果Go应用为entrypoint入口 (pid=1),可以将entrypoint修改为
sh -c 'Go应用及参数'
的方式启动Go应用。
权限依赖:
注入探针时需要父进程拥有PTRACE_ME权限
如果容器内运行Go应用探针加载失败,golang_bootstrap.log 内有
Operation not permitted
日志,需要在运行容器时增加参数--cap-add=SYS_PTRACE
。
卸载探针:Go应用识别采用白名单的方式,修改/opt/tingyun-oneagent/conf/interceptor.conf
文件中的go.namelist=
,移除不需要监控的应用名称。
嵌码原理:利用Preload技术(/etc/ld.so.preload)由父进程加载预加载模块(libinterceptor.so), 预加载模块会监控父进程启动子进程的动作, 如果发现子进程名称在go.namelist
白名单之内, 则使用bin/agentinject
替换为原来的子进程,并使用bin/agentinject
启动Go应用, agentinject利用ptrace技术替换Go应用中相关的函数并获取性能数据。
使用UniAgent Kubernetes版
当使用Kubernetes或Openshift云平台时,安装步骤请参见UniAgent Kubernetes部署说明。
探针需要至少50M内存资源,如果Pod设置了request和limit,请增加CPU和内存的资源设置。
注意:2.5.7.0版本的探针仅适用于直接启动Go应用的Pod
通过shell脚本直接启动Go应用,如果镜像包含完整的 C 运行时库(glibc 或 musl libc):打标签
tingyun-go-enabled: enabled
通过shell脚本直接启动Go应用,但使用了无 C 运行时的精简镜像(例如busybox:musl):不支持,无法嵌码。
如果Go应用是由静态链接的进程拉起,不支持,无法嵌码。
镜像直接启动Go应用(例如scratch):打标签
tingyun-go-entrypoint: enabled
,且确认command不为空,如果command为空,需要将command修改为Go应用路径和Go应用参数
例1:
通过构建Dockerfile时指定ENTRYPOINT ['/app/go-demo', 'param']
,部署Deployment时没有指定command
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo1
namespace: default
spec:
selector:
matchLabels:
app: demo1
template:
spec:
containers:
- name: demo1
image: test/go-demo:1.0
需要将tingyun-go-entrypoint设置为enabled,并且增加command,内容为镜像Dockerfile的ENTRYPOINT值,配置如下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo1
namespace: default
spec:
selector:
matchLabels:
app: demo1
template:
metadata:
labels:
tingyun-go-entrypoint: enabled
spec:
containers:
- name: demo1
image: test/go-demo:1.0
command: ['/app/go-demo', 'param']
例2:
通过构建Dockerfile时指定ENTRYPOINT ['/app/go-demo']
,部署Deployment时没有指定command,但指定了args
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo1
namespace: default
spec:
selector:
matchLabels:
app: demo1
template:
spec:
containers:
- name: demo1
image: test/go-demo:1.0
args: ['param1', 'param2']
需要将tingyun-go-entrypoint设置为enabled,删除args,增加command,内容为镜像Dockerfile的ENTRYPOINT值加上原有args,配置如下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo1
namespace: default
spec:
selector:
matchLabels:
app: demo1
template:
metadata:
labels:
tingyun-go-entrypoint: enabled
spec:
containers:
- name: demo1
image: test/go-demo:1.0
command: ['/app/go-demo', 'param1', 'param2']
在Kubernetes平台中容器默认拥有SYS_PTRACE
能力,但也不排除某些定制化的Kubernetes平台限制了容器的SYS_PTRACE
能力,需酌情处理。
嵌码原理:
利用Preload技术(LD_PRELOAD)由父进程加载预加载模块(libinterceptor.so), 预加载模块会监控父进程启动子进程的动作, 并使用bin/agentinject
替换为原来的子进程。bin/agentinject
判断子进程是否为go应用,如果不是go应用,则直接启动子进程;如果是go应用,agentinject利用ptrace技术替换Go应用中相关的函数并获取性能数据。
如果go应用没有父进程,并将tingyun-go-entrypoint设置为enabled,探针会修改容器command为 agentinject 原Go应用启动命令
,agentinject利用ptrace技术替换Go应用中相关的函数并获取性能数据。
使用独立探针
下载Go独立探针,安装完毕后,修改 /安装路径/conf/goagent.conf
中collector地址和license,在应用启动脚本内增加/安装路径/bin/agentinject
,重启应用即可。
例如,原Go应用启动脚本如下:
/opt/go-app/go-demo
更改后的应用启动脚本如下:
...
/opt/tingyun-go-v1.0/bin/agentinject /opt/go-app/go-demo
卸载探针:将应用启动脚本恢复为原来的脚本并重启应用。
嵌码原理:使用bin/agentinject
启动Go应用, agentinject利用ptrace技术替换Go应用中相关的函数并获取性能数据。
Go SDK 嵌码
安装Go SDK
Go SDK与所有第三方模块的安装方式相同。
GOPATH模式下安装:
$ go get github.com/TingYunGo/goagent
GOMOD模式下安装:
在应用文件夹下执行:
$ go mod tidy
使用SDK嵌码
根据应用使用HTTP框架的不同,需要import不同的路径。
我们以一个使用内置HTTP框架的简单例子说明如何嵌码。
源文件: main.go
代码如下:
package main
import (
"encoding/json"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
header := w.Header()
header.Set("Cache-Control", "no-cache")
header.Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusOK)
b, _ := json.Marshal(map[string]interface{}{
"status": "success",
"URI": r.URL.RawQuery,
})
w.Write(b)
})
http.ListenAndServe(":3000", nil)
}
要对如上这个应用嵌码,在源文件同级目录下,创建tingyun.go文件,内容如下:
package main
import (
_ "github.com/TingYunGo/goagent"
)
至此,全部嵌码工作已完成。
嵌码说明:此例应用使用了内置HTTP框架,对于内置HTTP框架,需要引入github.com/TingYunGo/goagent。
如果您不确定嵌码要使用哪个/哪些 import 模块路径,可通过查看支持列表了解引用依赖。
获取当前应用的依赖模块:
GOMOD 方式:查看go.mod文件,或者使用命令:
$ go mod graph
GOPATH 方式:编译时使用
-a
-v
参数:$ go build -a -v
嵌码示例
以开源项目 photoprism 为例,项目地址 https://github.com/photoprism/photoprism
步骤1:克隆项目。
$ git clone https://github.com/photoprism/photoprism.git
步骤2:确定项目使用哪些框架和库。
进入项目文件夹,查看 go.mod文件。
$ cd photoprism $ cat go.mod
我们会看到,此项目使用了gin框架,数据库支持:
postgresql:(github.com/lib/pq) postgresql:(github.com/lib/pq) sqlite:(github.com/mattn/go-sqlite3)
步骤3:查询支持列表确定引用路径,添加源码。
查看框架支持列表,我们的嵌码操作需要引用两个路径:
github.com/TingYunGo/goagent/frameworks/gin github.com/TingYunGo/goagent/database
在代码目录 internal/photoprism下创建 tingyun.go文件,内容如下:
package photoprism import ( _ "github.com/TingYunGo/goagent/database" _ "github.com/TingYunGo/goagent/frameworks/gin" )
步骤4:在项目go.mod所在目录下执行
go mod tidy
命令,然后编译。$ go mod tidy $ make
以上4个步骤完成后,项目编译和SDK嵌码工作就已全部完成。更多SDK嵌码示例,请参见使用Go SDK嵌码示例。