使用 OpenTelemetry OpAMP 动态修改服务遥测
博客文章在发布后不会更新。这篇文章已经发布一年多了,其内容可能已过时,部分链接可能无效。在依赖任何信息之前,请务必核实。
您的服务遥测应该有多详细?服务是否应该 100% 的时间输出所有跟踪、指标和日志?服务流量的多少应该被采样?我想建议的答案是“这取决于”。服务在其生命周期中,从开发到持续部署,所需的遥测数据是不同的。当客户遇到错误或服务面临大规模扩展时,这些数据也会随之变化。
可以更改服务遥测配置或采样率。通常,这只需要最小的代码更改和部署过程。这可能看起来不算什么,但当整个系统面临这样的更改时,我们往往会避免它。相反,通常的做法是收集尽可能多的数据,而这本身就会造成问题。我们能否在没有这些障碍的情况下动态修改服务遥测?感谢 OpAMP 协议及其背后的人们,我相信答案即将改变。
OpAMP 是 Open Agent Management Protocol 的缩写。它旨在管理大规模的数据收集代理,其 GoLang 实现处于 Beta 阶段。它允许进行配置更改以及包下载。它定义了 OpAMP 服务器和 OpAMP 客户端之间的通信,但不假设任何特定的客户端-代理关系,这使其具有很大的灵活性。
在下面的示例中,我们将创建一个简单的 GoLang 服务器,对其进行插桩,然后使用 OpAMP 服务器和 Supervisor 来控制它。我们不会深入研究 OpAMP 本身,而是通过这些示例来关注它的应用。
首先,考虑这个基本的 go 服务器
package main
import (
"fmt"
"log"
"net/http"
)
func httpHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hi! This action could create a trace!")
}
func main() {
handler := http.HandlerFunc(httpHandler)
http.Handle("/", handler)
fmt.Println("Starting server on port 8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
接下来,添加一个名为 effective.yaml 的基本配置文件。将其放在与 main.go 文件相同的文件夹中,并进行以下配置
instrument: false
让我们为我们的服务器添加一个基本配置处理程序
package main
import (
"fmt"
"gopkg.in/yaml.v3"
"io/ioutil"
"log"
"net/http"
"path/filepath"
)
type configurations struct {
Instrument bool
}
func httpHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hi! This action could create a trace!")
}
func main() {
filename, _ := filepath.Abs("./effective.yaml")
yamlFile, _ := ioutil.ReadFile(filename)
var config configurations
yaml.Unmarshal(yamlFile, &config)
handler := http.HandlerFunc(httpHandler)
http.Handle("/", handler)
fmt.Println("Starting server on port 8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
接下来,让我们用插桩包装我们的处理程序,并用我们的配置文件对其进行条件化。大致如下
package main
import (
"context"
"fmt"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
"go.opentelemetry.io/otel/trace"
"gopkg.in/yaml.v3"
"io/ioutil"
"log"
"net/http"
"os"
"path/filepath"
)
type configurations struct {
Instrument bool
}
var tracer trace.Tracer
func newConsoleExporter() (sdktrace.SpanExporter, error) {
return stdouttrace.New(
stdouttrace.WithWriter(os.Stdout),
stdouttrace.WithPrettyPrint(),
)
}
func httpHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hi! This action could create a trace!")
}
func setHandler(handler http.Handler, config configurations) http.Handler {
if config.Instrument {
return otelhttp.NewHandler(handler, "instrumentation activated by OpAMP")
}
return http.HandlerFunc(httpHandler)
}
func main() {
filename, _ := filepath.Abs("./effective.yaml")
yamlFile, _ := ioutil.ReadFile(filename)
var config configurations
yaml.Unmarshal(yamlFile, &config)
exp, _ := newConsoleExporter()
tp := sdktrace.NewTracerProvider(sdktrace.WithBatcher(exp))
defer func() { _ = tp.Shutdown(context.Background()) }()
otel.SetTracerProvider(tp)
tracer = tp.Tracer("ControlledOpAMPAgentDemo")
handler := http.HandlerFunc(httpHandler)
http.Handle("/", setHandler(handler, config))
fmt.Println("Starting server on port 8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
构建并运行此应用程序
go build .
go run .
在浏览器中打开并访问 https://:8080。不会显示任何特殊内容。现在是时候添加一些 OpAMP 了。Git 克隆 opamp-go 并使用以下命令运行服务器
cd internal/examples/server
go run .
访问 https://:4321 来验证服务器是否正在运行。请注意,没有显示任何代理

接下来,编辑 internal/examples/supervisor/bin/supervisor.yaml 文件,使其指向我们的代理。它应该如下所示
server:
endpoint: ws://127.0.0.1:4320/v1/opamp
agent:
executable: <absolute|relative path to previous build>
然后打开一个新的终端并运行以下命令
cd internal/examples/supervisor/bin
go build -o ./supervisor ../main.go
./supervisor
现在我们有一个由 OpAMP 服务器 Supervisor 和我们的服务器组成的系统

通过 Supervisor,我们现在可以在 https://:4321 上看到我们的代理正在运行。选择它,然后将其配置设置为 instrument: true。

您可以在 Supervisor 的控制台日志中看到更改
Received remote config from server, hash=0008886301f3ccb3520216823cfa09a.
Effective config changed.
Config is changed. Signal to restart the agent.
Restarting the agent with the new config.
Stopping agent process, PID=19206
Agent process PID=19206 successfully stopped.
Starting agent <agent path>
Agent process started, PID=19506
最后,访问 https://:8080。跟踪信息现在应该会出现在 internal/examples/supervisor/bin/agent.log 中。
Starting server on port 8080
{
"Name": "instrumentation activated by OpAMP server",
"SpanContext": {
"TraceID": "d2f76958023624d4c1def3f44899b6d4",
"SpanID": "085510f551dc31a1",
"TraceFlags": "01",
"TraceState": "",
"Remote":false
...
这些行就是跟踪本身!
总而言之,我们有一个服务器可以控制我们的服务是否生成跟踪。尝试使用 instrument: false 配置将其关闭。
这是一个非常基础的实现。将 OpAMP 包装到系统中可以作为一个插桩协调器。起点是能够外部添加和匹配为您系统量身定制的动态遥测。想象一下 AI 在这种系统上能取得什么成就。它可以收集整个系统的指标,自动动态地添加跟踪/日志收集到任何检测到的瓶颈上。使用此协议可以实现许多新的可能性,我相信它有潜力改变我们对遥测的看法。