作者:古琦

介绍

随着 Kubernetes 和容器化技术的普及,Go 语言不仅在云原生基础组件领域广泛应用,也在各类业务场景中占据了重要地位。如今,越来越多的新兴业务选择 Golang 作为首选编程语言。得益于丰富的 RPC 框架(如 Gin、Kratos、Kitex 等),Golang在微服务生态中愈加成熟,并被用于很多重要的开源项目,如 OpenTelemetry Collector、ETCD、Prometheus、Istio 等。

但是跟 Java 相比,Golang 在微服务生态上依然处于劣势,相比 Java 可以使用字节码增强的技术来实现无侵入的应用监控能力,Golang 没有成熟的对应方案,当前,大多数面向 Golang 应用的监控能力主要是通过 SDK 方式接入,如 OTel SDK,需要开放人员手动进行埋点,手动埋点的方案就会存在以下的两个问题:

  • Trace 需要每个调用点都需要进行埋点,同时要注意 Trace 上下文的传递,避免链路串联错误
  • Metrics 统计,需要针对每次调用都进行统计,同时注意指标发散的问题
  • 工作量非常大,对业务侵入性,每增加一个接口就需要同步增加对应的埋点

为了解决上述问题,可观测 Go Agent 应运而生。

实现原理

Java 有 JVM 提供的基于字节码增强的能力可以进行无侵入的埋点,Golang 没有类似的能力,因此这里我们是通过编译期注入的方案,在编译期完成埋点的注入,架构如下所示:

全链路追踪 & 性能监控,GO 应用可观测全面升级_云原生

1. 语法树分析

我们在编译过程中,利用语法树(AST, Abstract Syntax Tree)解析每个 .go 文件。通过分析语法树,能够精确定位到合适的插入点。具体实现中,我们使用了 dst 库,这是一个强大的语法树解析和变换工具。

2. 编译劫持

利用 Golang 提供的 -toolexec 参数,我们可以在编译时指定一个定制工具来劫持编译过程。该工具——即我们的 Go Agent,在 Golang 程序编译时介入,通过分析和修改语法树,插入监控代码。修改后的文件将编译储存在 .a 文件中,最后生成的二进制文件中包含我们的插入代码。

3. 代码插入

具体流程如下:

  • 解析源代码:在每次编译一个 .go 文件时,Go Agent 通过 toolexec 参数接管编译过程。
  • 语法树分析:使用 dst 库解析每个文件的语法树,找到适合插入监控代码的位置,如函数调用点、HTTP 请求处理点等。
  • 插入监控代码:在选定的插入点中写入监控代码,这些监控代码用来收集运行时信息,如请求数、响应时间、错误率等。
  • 继续编译过程:插入监控代码后,将修改后的文件继续传递给编译器,编译成 .a 文件。最终生成的二进制文件包含了监控逻辑。

Go Agent 支持的三方 SDK

通过我们定义插件 API,我们可以非常方便的对不同的SDK进行埋点,当前 Go Agent 支持 1.18 及以上的 Golang 版本,支持如下常见的微服务框架、协议、中间件等我们在 Go Agent 中都做了埋点,如下是当前版本支持的情况,当前已经支持了 20 款的插件能力。

全链路追踪 & 性能监控,GO 应用可观测全面升级_Go_02

产品能力

1. 应用概览

展示应用的请求数、错误数、平均耗时、实例数等信息。

全链路追踪 & 性能监控,GO 应用可观测全面升级_阿里云_03

2. 应用拓扑

展示应用的上下游信息。

全链路追踪 & 性能监控,GO 应用可观测全面升级_Go_04

3. 提供服务

展示提供的接口的被调用情况。

全链路追踪 & 性能监控,GO 应用可观测全面升级_可观测_05

4. 依赖服务

展示依赖的下游服务的调用情况。

全链路追踪 & 性能监控,GO 应用可观测全面升级_可观测_06

5. 调用链分析

可以完整的展示整个系统的调用链数据。

全链路追踪 & 性能监控,GO 应用可观测全面升级_云原生_07

全链路追踪 & 性能监控,GO 应用可观测全面升级_云原生_08

6. 实例监控

全链路追踪 & 性能监控,GO 应用可观测全面升级_云原生_09

7. 场景化分析

数据库分析:

全链路追踪 & 性能监控,GO 应用可观测全面升级_阿里云_10

异常分析:

全链路追踪 & 性能监控,GO 应用可观测全面升级_阿里云_11

8. 应用配置

可以配置采样率,目前支持按比例采样,还可以配置探针的开关,实现注入能力的实时插拔。

全链路追踪 & 性能监控,GO 应用可观测全面升级_可观测_12

9. 智能告警

可以配置应用响应的告警规则,针对如应用提供的服务、HTTP 状态码、数据库指标、依赖服务、主机监控配置告警。

全链路追踪 & 性能监控,GO 应用可观测全面升级_阿里云_13

兼容性

1)兼容已有的 OTel Go SDK,支持 v1.6.0-v1.26.0 版本,已经使用了 OTel SDK 的应用无需修改也可以通过 Go Agent 接入到 ARMS 应用监控。

2)Trace 透传协议支持 W3C、Jaeger、EagleEye、Zipkin,可以跟已有的其他 Trace 协议进行打通。

Go 应用接入应用监控

当您需要对部署在容器服务 Kubernetes 版 ACK(Container Service for Kubernetes)中的 Go 应用进行监控时,通过安装 ARMS 应用监控组件 ack-onepilot 并编译 Go 二进制文件,即可在应用实时监控服务 ARMS 中查看对应应用的应用拓扑、接口调用、数据库分析等相关监控数据。本文将介绍如何为部署在容器服务 Kubernetes 版 ACK(Container Service for Kubernetes)中的 Go 应用安装探针。

前提条件

  • 创建 Kubernetes 集群。您可按需选择创建[1]、创建 ACK 托管集群[2]或创建 ACK Serverless 集群[3]
  • 创建命名空间,具体操作,请参见管理命名空间与配额[4]。本文示例中的命名空间名称为 default。
  • 检查您的编译环境操作系统和架构,以及 Go 版本和框架版本。具体要求,请参见 Golang 探针兼容性要求[5]
  • 应用监控目前只支持使用 Go mod 的仓库编译,如果没有 Go mod 文件,可以使用 Go init 创建 Go mod。

步骤一:安装 ARMS 应用监控组件

1. 登录容器服务管理控制台[6]

2. 在左侧导航栏单击集群,然后在集群列表页面单击目标集群名称。

3. 在左侧导航栏选择运维管理 > 组件管理,然后在右上角通过关键字搜索 ack-onepilot。

重要:请确保 ack-onepilot 的版本在 3.2.0 或以上。

4. 在 ack-onepilot 卡片上单击安装。

说明:ack-onepilot 组件默认支持 1000 个 pod 规模,集群 pod 每超过 1000 个,ack-onepilot 资源对应的 CPU 请增加 0.5 核、内存请增加 512 M。

5. 在弹出的页面中可以配置相关的参数,建议使用默认值,单击确定。

说明:安装完成后,您可以在组件管理页面升级、配置或卸载 ack-onepilot 组件。

步骤二:编译 Go 二进制文件

1. 使用 wget 命令下载编译工具,请根据编译环境和编译机器所在地域,选择对应的下载地址。

说明:各地域下载的编译工具是相同的,如果您的公网环境可以访问 OSS 地址,可以直接使用对应操作系统和架构的杭州公网地址获取编译工具。

查看各地域对应的下载命令:

 https://help.aliyun.com/zh/arms/application-monitoring/getting-started/ack-installs-a-go-probe-through-ack-onepilot-components?spm=a2c4g.11186623.0.0.243928a8HbTaeb#439ba6b5802t6

2. 通过对 SHA256 校验和进行校验,验证下载结果的完整性,并为编译工具赋予可执行权限。

您可以在探针下载地址后面添加 .sha256 下载获取 SHA256 校验和。

全链路追踪 & 性能监控,GO 应用可观测全面升级_云原生_14

全链路追踪 & 性能监控,GO 应用可观测全面升级_阿里云_15

全链路追踪 & 性能监控,GO 应用可观测全面升级_Go_16

3. 通过 OpenAPI 获取 LicenseKey,具体方法,参见 DescribeTraceLicenseKey - 列出 LicenseKey[7]

4. 在项目 main 函数所在文件的目录下执行编译工具编译 Go 项目,请替换命令行中的 {licenseKey} 为上一步获取到的 LicenseKey。

如果您需要测试编译效果,或暂时无法获取到 LicenseKey,可以添加 --dev 来开启 Dev 编译模式,此模式下不需要传入 LicenseKey。

重要:Dev 模式下编译结果部分功能有降级,请勿用于线上。

全链路追踪 & 性能监控,GO 应用可观测全面升级_云原生_17

全链路追踪 & 性能监控,GO 应用可观测全面升级_云原生_18

5. 如果您原本编译命令中含有后续的编译参数,请在所有选项添加完毕后,添加--分隔符,并将编译参数按照原有格式添加在后面,例如:

全链路追踪 & 性能监控,GO 应用可观测全面升级_可观测_19

全链路追踪 & 性能监控,GO 应用可观测全面升级_Go_20

编译后会产生对应的二进制文件,二进制文件的名称可以通过编译参数指定。

如下所示,编译后的二进制名称为 http,如果不添加对应配置,输出的二进制的名称为 go.mod 中 module 配置的名称。

全链路追踪 & 性能监控,GO 应用可观测全面升级_阿里云_21

相关信息

Instgo 常见命令与释义如下:

全链路追踪 & 性能监控,GO 应用可观测全面升级_Go_22

初次编译时,探针会默认被下载到 /opt 目录下(Windows 为 C:\ProgramData),如果该目录没有权限,可以使用--cacheDir flag 指定缓存目录,或使用 sudo -E 命令执行编译(Windows 请使用管理员权限执行编译命令)。

编译过程中如果强制退出或出现异常退出,可能会存在编译残留。清理Golang探针编译残留的方法,请参见清理 Golang 探针编译残留[8]

步骤三:授予 ARMS 资源的访问权限

如果需监控 ASK(容器服务 Serverless 版)或对接了 ECI 的集群应用,请在云资源访问授权[9]页面完成授权,然后重启 ack-onepilot 组件下的所有 Pod。

如果需监控 ACK 集群应用,但 ACK 集群中不存在ARMS Addon Token,请执行以下操作手动为集群授予 ARMS 资源的访问权限。如果已经存在 ARMS Addon Token,请跳转至步骤四。

查看集群是否存在 ARMS Addon Token:

a. 登录容器服务管理控制台[10],在集群列表页面,单击目标集群名称进入集群详情页。

b. 在左侧导航栏选择配置管理 > 保密字典,然后在顶部选择命名空间为 kube-system,查看 addon.arms.token 是否存在。

说明:集群存在 ARMS Addon Token 时,ARMS 会进行免密授权。Kubernetes 托管版集群默认存在 ARMS Addon Token,但对于部分早期创建的 Kubernetes 托管版集群,可能会存在没有 ARMS Addon Token 的情况,因此,对于 Kubernetes 托管版集群,建议首先检查 ARMS Addon Token 是否存在。若不存在,需进行手动授权。

a. 登录容器服务管理控制台。b. 在左侧导航栏选择集群,然后单击目标集群名称。c. 在目标集群的集群信息页面单击集群资源页签,然后单击Worker RAM角色右侧的链接。d. 在角色页面的权限管理页签上,单击新增授权。e. 选择权限为 AliyunARMSFullAccess,然后单击确定。如果需要监控专有版集群和注册集群应用,请确认对应的阿里云账号已包含 AliyunARMSFullAccess 和 AliyunSTSAssumeRoleAccess 权限。添加权限的操作,请参见为 RAM 用户授权[11]

a. 安装 ack-onepilot 组件后,还需要在 ack-onepilot 中填写有 ARMS 权限的阿里云账号 AK/SK。

b. 在左侧导航栏选择应用 > Helm 页面,单击 ack-onepilot 组件右侧的更新。

将 accessKey 和 accessKeySecret 替换为当前账号的 AccessKey,然后单击确定。

说明:获取 AccessKey 的操作,请参见创建 AccessKey[12]

全链路追踪 & 性能监控,GO 应用可观测全面升级_云原生_23

c. 重启应用 Deployment。

步骤四:为 Go 应用开启 ARMS 应用监控

如需在创建新应用的同时开启 ARMS 应用监控,请完成以下操作。

1. 在容器服务管理控制台[13]左侧导航栏单击集群,在集群列表页面上的目标集群右侧操作列单击应用管理。2. 在无状态页面右上角单击使用 YAML 创建资源。

3. 选择示例模板,并在模板(YAML 格式)中将以下 labels 添加到 spec.template.metadata 层级下。

labels:
  aliyun.com/app-language: golang # Go应用必填,标明此应用是Go应用。
  armsPilotAutoEnable: 'on'
  armsPilotCreateAppName: "<your-deployment-name>"    #请将<your-deployment-name>替换为您的应用名称。
  • 1.
  • 2.
  • 3.
  • 4.

全链路追踪 & 性能监控,GO 应用可观测全面升级_Go_24

创建一个无状态(Deployment)应用并开启 ARMS 应用监控的完整 YAML 示例模板如下:

查看完整示例 YAML 文件(Java):

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: fasthttp-server
  name: fasthttp-server
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: fasthttp-server
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: fasthttp-server
        aliyun.com/app-language: golang
        armsPilotAutoEnable: 'on'
        armsPilotCreateAppName: fasthttp-server
        hostLabelA: aaa
        hostLabelB: bbb
    spec:
      containers:
        - image: >-
            registry.cn-hangzhou.aliyuncs.com/private-mesh/hellob:fasthttp-server-arms-go-test-0.0.1
          imagePullPolicy: Always
          name: fasthttp-server
          resources:
            requests:
              cpu: 250m
              memory: 300Mi
          terminationMessagePath: /dev/termination-log
          terminationMessagePolicy: File
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext: {}
      terminationGracePeriodSeconds: 30
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: fasthttp-server
  name: fasthttp-server
  namespace: default
spec:
  internalTrafficPolicy: Cluster
  ipFamilies:
    - IPv4
  ipFamilyPolicy: SingleStack
  ports:
    - name: http
      port: 8080
      protocol: TCP
      targetPort: 8080
  selector:
    app: fasthttp-server
  sessionAffinity: None
  type: ClusterIP


---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: fasthttp-client
  name: fasthttp-client
  namespace: default
spec:
  replicas: 1
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app: fasthttp-client
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: fasthttp-client
        aliyun.com/app-language: golang
        armsPilotAutoEnable: 'on'
        armsPilotCreateAppName: fasthttp-client
        hostLabelA: aaa
        hostLabelB: bbb
    spec:
      containers:
        - image: >-
            registry.cn-hangzhou.aliyuncs.com/private-mesh/hellob:fasthttp-client-arms-go-test-0.0.1
          imagePullPolicy: Always
          name: fasthttp-client
          resources:
            requests:
              cpu: 250m
              memory: 300Mi
          terminationMessagePath: /dev/termination-log
          terminationMessagePolicy: File
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext: {}
      terminationGracePeriodSeconds: 30
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.

执行结果

约一分钟后,若 Golang 应用出现在 ARMS 控制台[14]的应用监控 > 应用列表页面中且有数据上报,则说明接入成功。

全链路追踪 & 性能监控,GO 应用可观测全面升级_可观测_25

点击 此处立即开通 ARMS - 应用监控,享受每月 50GB 免费额度!加入钉钉群(群号:35568145)获得在线技术支持。

相关链接:

[1] 创建

 https://help.aliyun.com/zh/ack/ack-managed-and-ack-dedicated/user-guide/create-an-ack-dedicated-cluster?spm=a2c4g.11186623.0.i4#task-skz-qwk-qfb

[2]创建 ACK 托管集群

 https://help.aliyun.com/zh/ack/ack-managed-and-ack-dedicated/user-guide/create-an-ack-managed-cluster-2?spm=a2c4g.11186623.0.i5#task-skz-qwk-qfb

[3]创建 ACK Serverless 集群

 https://help.aliyun.com/zh/ack/serverless-kubernetes/user-guide/create-an-ask-cluster-2?spm=a2c4g.11186623.0.i6#task-e3c-311-ydb

[4]管理命名空间与配额

 https://help.aliyun.com/zh/ack/ack-managed-and-ack-dedicated/user-guide/manage-namespaces-and-resource-quotas-1?spm=a2c4g.11186623.0.i7#task-2060973

[5]Golang 探针兼容性要求

 https://help.aliyun.com/zh/arms/application-monitoring/developer-reference/go-components-and-frameworks-supported-by-arms-application-monitoring?spm=a2c4g.11186623.0.i8

[6]容器服务管理控制台

 https://cs.console.aliyun.com/?spm=a2c4g.11186623.0.0.5de2554b7Zi2Ir#/k8s/cluster/list

[7]DescribeTraceLicenseKey - 列出 LicenseKey

 https://help.aliyun.com/zh/arms/application-monitoring/developer-reference/api-arms-2019-08-08-describetracelicensekey-apps?spm=a2c4g.11186623.0.i9

[8]清理 Golang 探针编译残留

 https://help.aliyun.com/zh/arms/application-monitoring/user-guide/unloading-the-golang-probe?spm=a2c4g.11186623.0.i10#376f76252fhsm

[9]云资源访问授权

 https://ram.console.aliyun.com/role/authorization?spm=a2c4g.11186623.0.0.5de24bc27D1EPz&request=%7B%22Services%22%3A%5B%7B%22Service%22%3A%22ECS%22%2C%22Roles%22%3A%5B%7B%22RoleName%22%3A%22AliyunMSEForECIRole%22%2C%22TemplateId%22%3A%22AliyunMSEForECIRole%22%7D%5D%7D%5D%2C%22ReturnUrl%22%3A%22https%3A%2F%2Farms.console.aliyun.com%22%7D

[10]容器服务管理控制台

 https://cs.console.aliyun.com/?spm=a2c4g.11186623.0.0.5de24bc27D1EPz#/k8s/cluster/list

[11]为 RAM 用户授权

 https://help.aliyun.com/zh/ram/user-guide/grant-permissions-to-the-ram-user?spm=a2c4g.11186623.0.i11

[12]创建 AccessKey

 https://help.aliyun.com/zh/ram/user-guide/create-an-accesskey-pair?spm=a2c4g.11186623.0.i12

[13]容器服务管理控制台

 https://cs.console.aliyun.com/?spm=a2c4g.11186623.0.0.5de22c6a8Wb7NM#/k8s/cluster/list

[14]ARMS 控制台

 https://arms.console.aliyun.com/?spm=a2c4g.11186623.0.0.5de22c6a8Wb7NM#/home

参考文档:

[1] 手动安装 Golang 探针

 https://help.aliyun.com/zh/arms/application-monitoring/getting-started/manually-install-the-golang-probe?spm=a2c4g.11186623.0.0.760528a8VT9OFu