k8s上部署Opentelemetry Operator

1. OpenTelemetry介绍

OpenTelemetry 是一个可观测性框架和工具包, 旨在创建和管理遥测数据,如链路、 指标日志。 重要的是,OpenTelemetry 是供应商和工具无关的,这意味着它可以与各种可观测性后端一起使用, 包括 Jaeger 和 Prometheus 这类开源工具以及商业化产品。

OpenTelemetry 不是像 Jaeger、Prometheus 或其他商业供应商那样的可观测性后端。 OpenTelemetry 专注于遥测数据的生成、采集、管理和导出。 OpenTelemetry 的一个主要目标是, 无论应用程序或系统采用何种编程语言、基础设施或运行时环境,你都可以轻松地将其仪表化。 重要的是,遥测数据的存储和可视化是有意留给其他工具处理的。

什么是可观测性?

可观测性是通过检查系统输出来理解系统内部状态的能力。 在软件的背景下,这意味着能够通过检查遥测数据(包括链路、指标和日志)来理解系统的内部状态。

要使系统可观测,必须对其进行仪表化。也就是说,代码必须发出链路、指标或日志。 然后,仪表化的数据必须发送到可观测性后端。

为什么选择 OpenTelemetry?

随着云计算、微服务架构的兴起和日益复杂的业务需求,软件和基础设施的可观测性需求比以往任何时候都要强烈。

OpenTelemetry 满足可观测性的需求,并遵循两个关键原则:

  1. 你所生成的数据归属于你自己,不会被供应商锁定。
  2. 你只需要学习一套 API 和约定。

这两个原则的结合赋予团队和组织在当今现代计算世界中所需的灵活性。

如果你想了解更多信息,请查阅 OpenTelemetry 的使命、愿景和价值观

主要的 OpenTelemetry 组件

OpenTelemetry 包括以下主要组件:

OpenTelemetry 广泛应用于许多已集成 OpenTelemetry 提供默认可观测性的库、服务和应用

OpenTelemetry 得到众多供应商的支持,其中许多为 OpenTelemetry 提供商业支持并直接为此项目做贡献。

可扩展性

OpenTelemetry 被设计为可扩展的。一些扩展 OpenTelemetry 的例子包括:

  • 向 OpenTelemetry Collector 添加接收器以支持来自自定义源的遥测数据
  • 将自定义仪表化库加载到 SDK 中
  • 创建适用于特定用例的 SDK 或 Collector 的分发
  • 为尚不支持 OpenTelemetry 协议(OTLP)的自定义后端创建新的导出器
  • 为非标准上下文传播格式创建自定义传播器

尽管大多数用户可能不需要扩展 OpenTelemetry,但此项目几乎每个层面都可以实现扩展。

历史

OpenTelemetry 是云原生计算基金会 (CNCF)的一个项目,是由 OpenTracing 和 OpenCensus 项目合并而成的。原来这两个项目都是为解决同样的问题而创建的: 缺乏一种标准的方法来为代码进行仪表化并将遥测数据发送到可观测性后端。 由于这两个项目都无法独立解决这个问题,所以将其合并成立了 OpenTelemetry, 吸收了双方的优势,提供了统一的解决方案。

image.png

通过instrument-agent注入traceid

  • 在log的pattern中加上%X,如: image.png
  • 打印出来的日志sample: 2022-08-01 17:39:11.735 trace_id=de6441b0701b34536d51b6333e95b0af, trace_flags=01, span_id=180fff77be7955ad [http-nio-8080-exec-1] INFO com.controller.VersionController [] - i am log.

接下来是操作步骤了:

2 通过helm安装opentelemetry-operator

2.1 添加helm chart:

1
helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts

更新chart:

1
helm repo update

2.2 开始安装

创建namespace:opentelemetry-operator-system 安装命令如下:

1
helm install my-opentelemetry-operator open-telemetry/opentelemetry-operator -n opentelemetry-operator-system

可以看到安装了:

  • pod为opentelemetry-operator-controller-manager
  • 两个service:
    • opentelemetry-operator-controller-manager-metrics-service
    • opentelemetry-operator-webhook-service

kubectl get all -n opentelemetry-operator-system|grep opentelemetry image.png

2.3 安装CRD Instrumentation

传统的Instrumentation,需要我们在jar启动的时候加上-javaagent。如果我们使用Custom Resource = Instrumentation的定义文件,可以自动的kubernetes运行时,给我们的应用程序pod加上-javaagent,从而使得我们的应用程序可以自动生成traceId,并发送给collector。

需要做两个配置:

    1. 配置CRD,Kind=Instrumentation。
    1. 在我们的pod yaml定义中加上annotation:instrumentation.opentelemetry.io/inject-java: "true"

首先,新建一个文件,叫instrumentation.yaml,spec中可以定义:

  • exporter: 这里定义的exporter,即我们在下一章(#5)生成的collector的service name,如果是不同的namespace下,需要写http://.:4317。
  • propagator:无侵入式Trace上下文传播类型,可选tracecontext, baggage, b3等等。(具体可参考:github.com/open-teleme…)。
  • resource
    • 可配:addk8sUIDattributes, 值可以为true,表示在span中启用k8s Uids相关的标签。
    • 可配:resourceAttributes, 值可以是一些属性如:“Servicename: test"或"Environment: dev”。配上的属性可以在span中包含进来。
  • sampler:涉及到是否将所有的trace数据都发送给存储的后端(如Tempo)。类型有很多,如:always_on, always_off, traceidratio, parentbased_always_on…
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
  name: my-instrumentation
spec:
  exporter:
    endpoint: http://otel-daemonset-collector.opentelemetry-operator-system:4317
  propagators:
    - tracecontext
    - baggage
    - b3
  sampler:
    type: always_on

image.png

在Kubernetes中安装:

kubectl apply -f instrumentation.yaml -n opentelemetry-operator-system

其实,如同上述介绍过,我们需要给我们的Pod yaml定义加上annotation(还支持NodeJS, Python等,这里只列举java的):

instrumentation.opentelemetry.io/inject-java: “true”

值可以为:

  • true:表示该Pod允许被CRD Instrumentation注入agent,实现trace相关数据的自动生成。Instrumentation需要被安装在同一个namespace下。
  • my-instrumentation:Instrumentation的名字(也需要在同一个namespace下)。主要是为了区别如果同一个namespace下有多个Instrumentation的case。
  • my-other-namespace/my-instrumentation:如果需要引用的Instrumentation不在同一个namespace下,需要显示的指定namespace以及Instrumentation的名字。
  • false:不需要被注入。

这个注解,是为了让部署的应用能正常提交日志到opentelemetry中。

2.4 安装CRD OpenTelemetryCollector

OpenTelemetryCollector将之前的Opentelemetry Collector pipeline的定义(如receivers, processors, exporters)统一整合到当前的CRD中。

其中,.Spec.Mode有三种:

  • Sidecar:需要在自己的app的pod定义中添加一个annotation,如:sidecar.opentelemetry.io/inject: "Boolean/String",可以配true,意思是在pod中自动配置collector,或是false,意思是不配置。或是当前namespace下有多个OpenTelemetryCollector的时候,这里可以写上想选择的name。
  • DaemonSet,这个模式下不需要添加annotation,但是,这种模式下,同一个namespace下所有的pod都会发送trace信息给这个collector。
  • Deployment(默认),同样的,也需要添加annotation:sidecar.opentelemetry.io/inject

从设计上来说,我们可以为每个pod各自配置自己的sidecar模式的collector,用来收集各个pod中的trace数据,然后再setup一个daemonset模式的collector,用来收集各个sidecar中的数据。

新建一个文件,叫openTelemetryCollector.yaml

 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
apiVersion: opentelemetry.io/v1alpha1
kind: OpenTelemetryCollector
metadata:
  name: otel-daemonset
spec:
  mode: daemonset
  hostNetwork: true
  config: |
    receivers:
      otlp:
        protocols:
          grpc:
          http:
    processors:
    exporters:
      otlp:
        endpoint: "http://tempo.monitoring:4317"
        tls:
          insecure: true
    service:
      pipelines:
        traces:
          receivers: [otlp]
          processors: []
          exporters: [otlp]    

注意· endpoint: “http://tempo.monitoring:4317”·是为了能让traceid正常发送到tempo服务中。 可以看到会生成:

  • pod:otel-daemonset-collector --> 和exporter的集成,日志在这里看。
  • service:
    • otel-daemonset-collector
    • otel-daemonset-collector-headless
    • otel-daemonset-collector-monitoring
  • daemonset:otel-daemonset-collector

注意这里的一个坑是image是·otel/opentelemetry-collector-contrib:0.124.1·,这个镜像不存在。所以我采用latest的镜像,然后转化成0.124.1 image.png

2.5 安装springboot项目来测试一下

2.5.1 创建一个springboot项目,叫demo

使用springboot的初始项目创建一个demo项目

 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
plugins {
	id 'java'
	id 'org.springframework.boot' version '3.4.5'
	id 'io.spring.dependency-management' version '1.1.7'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'

java {
	toolchain {
		languageVersion = JavaLanguageVersion.of(17)
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	//添加web
	implementation 'org.springframework.boot:spring-boot-starter-web'
	testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
	compileOnly 'org.projectlombok:lombok:1.18.30'
	annotationProcessor 'org.projectlombok:lombok:1.18.30'

}

tasks.named('test') {
	useJUnitPlatform()
}

def jarname = String.format("%s-%s.jar",project.name,version)
// 拷贝文件 dependsOn: bootJar 依赖springboot 插件的 bootJar打包命令
task copyConfigFile(type: Copy, dependsOn: bootJar) {
	// 清除app目录的历史文件
	delete "app/"
	// copy jar包 从 build/libs/ 目录到 app/ 目录
	from('build/libs/' + jarname)
	into 'app/'
	// 重命名成我们要的名字
	rename(jarname, project.name + '.jar')
}
// 依赖 clean 任务
task buildTodoCoderJar(dependsOn: clean) {
	dependsOn copyConfigFile
}

[!NOTE] 注意生成的jar包名称

在logback中添加自定义的pattern中,需要设置‘%X’,X可以获取当前的MDC

Spring Boot

For Spring Boot configuration which uses logback, you can add MDC to log lines by overriding only the logging.pattern.level:

1
logging.pattern.level = trace_id=%mdc{trace_id} span_id=%mdc{span_id} trace_flags=%mdc{trace_flags} %5p

logback.xml:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<configuration>
    <property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %X [%thread] %-5level %logger{56} %msg%n"></property>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${LOG_PATTERN}</pattern>

        </encoder>
    </appender>
    <root level="DEBUG">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

The OTel Java agent injects several pieces of information about the current span into each logging event’s MDC copy:

  • trace_id - the current trace id (same as Span.current().getSpanContext().getTraceId());
  • span_id - the current span id (same as Span.current().getSpanContext().getSpanId());
  • trace_flags - the current trace flags, formatted according to W3C traceflags format (same as Span.current().getSpanContext().getTraceFlags().asHex()). 下面展示一下生成的log样例
1
2025-05-09 05:25:39.580 trace_id=21c326f0f1106671528ccffde3370765, trace_flags=01, span_id=457aa935e34346a6 [http-nio-8080-exec-9] DEBUG org.springframework.web.servlet.DispatcherServlet "ERROR" dispatch for GET "/error?userId=1", parameters={masked}

直接使用build.sh脚本来支持一建部署: image.png image.png

这里需要注意的是opentelemetry-operator-system/my-instrumentation是指安装在opentelemertry-operator-system下的instrumentation。

2.5.2 创建user-app项目:

步骤跟上面一样: image.png 也是通过build.sh 一键创建测试即可。 通过k9s可以直接看到部署成功的服务 image.png

3 安装tempo和grafana服务

1
2
3
 helm show values grafana/loki-stack > ./loki-stack.yaml
helm upgrade --install loki --namespace=monitoring grafana/loki-stack --values ./loki-stack.yaml
helm upgrade --install tempo grafana/tempo -n monitoring

4 接下来在grafana中查看一下

获取grafna的密码如下操作

1
2
3
kubectl get secret loki-grafana -n monitoring -o yaml
#把获取到的密码转化一下
echo "bnFaUFY3RGgydjRxRmp2aUNHeXJTUHBiUjEwUEp0Sm5IYVZSMkdUaA==" |base64 -d;echo

image.png image.png 根据traceid在tempo中就可以查询到要的数据流程了 image.png

github地址: https://github.com/huangxiaofeng10047/user-app

Licensed under CC BY-NC-SA 4.0
最后更新于 May 09, 2025 06:14 UTC
comments powered by Disqus
Built with Hugo
主题 StackJimmy 设计
Caret Up