【Monitoring】在Kubernetes中安装Opentelemetry Operator

【资源】

【前置文章】

【参考】

1. Opentelemetry Operator介绍

传统的Opentelemetry和Java的集成,需要在启动jar的时候,加上:java -javaagent:path/to/opentelemetry-javaagent.jar -jar myapp.jar,但这样需要在项目的CI/CD部分改配置,那么,有没有一种方式,让部署在kubernetes中的应用,自动的加上这个agent,而项目本身保持clean呢?

答案是有的,那就是安装Opentelemetry Operator

【本文将演示如何通过安装Opentelemetry Operator实现distributed trace id的收集,主要步骤如下】:

    1. 安装cert manager,cert-manager是Kubernetes的附加组件,用于自动管理和颁发各种发行来源的TLS证书。它将确保证书有效并定期更新,并尝试在到期前的适当时间更新证书。
    1. 安装opentelemetry-operator,通过helm chart进行安装。
    1. 安装CRDInstrumentation,用来使opentelemetry-javaagent.jar自动的加到我们部署在k8s中的应用pod里去。
    1. 安装CRDOpenTelemetryCollector,看名字也知道,是Collector,用来收集app中发送的trace信息,并通过配置的exporters的endpoint发送到后端存储起来(这里的后端可以是Tempo)。
    1. 安装两个Spring boot项目:
    • pod: my-app
    • pod: user-app
    1. 安装Tempo,用来接收trace数据并存储。
    1. 安装Grafana,用来配置datasource=tempo,并在UI上进行trace链的展示。
    1. 开始测试trace链。

2. 安装Cert Manager

参考:cert-manager.io/docs/instal…

kubectl apply -f github.com/cert-manage…

安装好后,在namespace=cert-manager下查看资源,主要看两个service:

kubectl get all -n cert-manager

image.png

3. 通过helm安装opentelemetry-operator

3.1 首先添加helm chart:

helm repo add open-telemetry open-telemetry.github.io/opentelemet…

更新chart:

helm repo update

3.2 开始安装

namespace为opentelemetry-operator-system

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

image.png

4. 安装CRDInstrumentation

如同上述介绍的,传统的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...
apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
  name: my-instrumentation
spec:
  exporter:
    endpoint: http://otel-daemonset-collector:4317
  propagators:
    - tracecontext
    - baggage
    - b3
  sampler:
    type: always_on
复制代码

在Kubernetes中安装:

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

image.png

其实,如同上述介绍过,我们需要给我们的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:不需要被注入。

这个注解,在下述的第#6章,app的deployment.yaml中有用到。

5. 安装CRDOpenTelemetryCollector

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

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:4317"
        tls:
          insecure: true
    service:
      pipelines:
        traces:
          receivers: [otlp]
          processors: []
          exporters: [otlp]
复制代码

在Kubernetes中安装:

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

image.png

可以看到会生成:

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

6. 安装一个自己的app

6.1 创建my-app项目

创建一个简单的Spring boot项目,叫my-app,有两个api:

@GetMapping("version")
public String version() {
    log.info("/version, return version = my-app v1.");
    return "my-app v1";
}

@GetMapping("dashboard")
public String dashboard(@RequestParam String userId) {
    log.info("/dashboard, received userId = {}.", userId);
    String userName = restTemplate.getForObject("http://user-app-service:8081/user-name", String.class);
    log.info("get userName = {} from user-app.", userName);
    return String.format("I am dashboard with userId = %s, userName = %s.", userId, userName);
}
复制代码

另外,需要在logback.xml中,自定义的pattern中,加上%X,表示获取当前的MDC,具体参考:github.com/open-teleme…

<property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %X [%thread] %-5level %logger{56} %msg%n"></property>
复制代码

my-app的deployment.yaml

  • 安装在namespace=myapp。
  • image需要改。
  • 可以看到有个annotation:instrumentation.opentelemetry.io/inject-java: "opentelemetry-operator-system/my-instrumentation",因为我们的Instrumentation是放在namespace=opentelemetry-operator-system下的,所以这里并不是"true"(如果是同一个namespace下,可以设为true)。
  • 在pod的定义中,加下了env:otel.javaagent.debug=true,主要是可以在my-app中打印opentelemetry相关的debug信息,在prod中需要关闭掉。
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  labels:
    app: my-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
      annotations:
        instrumentation.opentelemetry.io/inject-java: "opentelemetry-operator-system/my-instrumentation"
    spec:
      containers:
        - name: my-app
          image: <docuer hub username>/my-app
          env:
            - name: JDK_JAVA_OPTIONS
              value:  "-Dotel.javaagent.debug=true"
          imagePullPolicy: Always
          ports:
            - containerPort: 8080

---

apiVersion: v1
kind: Service
metadata:
  name: my-app-service
spec:
  type: ClusterIP
  selector:
    app: my-app
  ports:
    - protocol: TCP
      port: 8080
      targetPort: 8080
复制代码

创建my-app这个pod,并且创建my-app-service:

kubectl apply -f deployment.yaml -n myapp

使用port-forward来转发minikube中的8080端口,以便可以在宿主机用localhost访问k8s中的service:

kubectl -n myapp port-forward service/my-app-service 8080:8080

6.2 创建user-app项目

再创建一个Spring boot项目,叫user-app,有一个api:

@GetMapping("user-name")
public String getUserName(@RequestParam String userId) {
    String name = "test 001";
    log.info("Received userId = {}, return userName = {}.", userId, name);
    return name;
}
复制代码

创建user-app这个pod,并且创建user-app-service:

kubectl apply -f deployment.yaml -n myapp

至此,在第6章,myapp namespace下创建了以下资源: image.png

7. Tempo和Grafana的安装

查看我之前的文章:

8. 开始测试

8.1 测试my-app的/version接口

首先在本地浏览器中调用:http://localhost:8080/version image.png

可以在loki中看到trace_id已经打印了: image.png

在tempo中看到调用链: image.png

8.2 测试my-app的/dashboard口

在本地浏览器中调用:http://localhost:8080/dashboard?userId=1 image.png

在loki中看到trace_id: image.png

在tempo中看到调用链,这里可以看到两个app的调用链是完整的: image.png

点击每个tab详情,可以更详细的看到一些调用链以及controller等: ps.如果有调用sql,也会显示出来的。 image.png

image.png

猜你喜欢

转载自juejin.im/post/7128277655475978276
今日推荐