使用 OpenTelemetry 和 Loki 实现高效的应用日志采集和分析

使用 OpenTelemetry 和 Loki 实现高效的应用日志采集和分析

在之前的文章陆续介绍了 如何在 Kubernetes 中使用 Otel 的自动插桩 以及 Otel 与 服务网格协同实现分布式跟踪,这两篇的文章都将目标聚焦在分布式跟踪中,而作为可观测性三大支柱之一的日志也是我们经常使用的系统观测手段,今天这篇文章就来体验下应用日志的操作闭环。

背景

OpenTelemetry 简介

OpenTelemetry (以下简称 Otel)是一个开源项目,旨在为分布式追踪、度量和日志提供统一的标准,简化应用程序的观测性(Observability)。它提供了一系列工具和 API,用于收集和传输应用程序的性能数据和日志,帮助开发者和运维团队更好地理解系统的行为。功能包括自动和手动检测应用程序的追踪数据,收集关键度量指标,以及捕获和传输日志。Otel 支持多种编程语言和框架,可以与多个后端系统集成,如 Prometheus、Jaeger、Elasticsearch 等。

Log 是 OpenTelemetry 项目的一部分,旨在提供一种标准化的方式来收集、传输和存储日志数据。

Loki 简介

LokiGrafana Labs 开发的一个水平可扩展、高可用性、多租户的日志聚合系统,专为效率和易用性而设计。与传统的日志聚合系统不同,Loki 主要索引日志内容的元数据而不是内容本身,这使得它既轻量又高效。Loki 采用了与 Prometheus 类似的标签系统,使得日志查询更加灵活和强大。常用于存储和查询大量日志数据,特别是与 Grafana 结合使用时,提供了强大的日志可视化和分析能力。

演示

在本演示中将使用 Java 应用进行日志闭环操作的演示,在 Otel Log 支持的语言 中,Java 是的最全面的语言之一。

架构

  1. Otel Operator 通过自动插桩的配置,为 Java 工作负载安装探针并加载配置
  2. 应用通过 otlp 端点上报日志到 Otel collector
  3. Otel collector 将日志输出到 Loki
  4. grafana 将 Loki 作为数据源进行日志的可视化展示

前置条件

  • Kubernetes 集群
  • kubectl cli
  • helm cli

安装 Loki 和 Grafana

安装 Grafana helm 库。

helm repo add grafana https://grafana.github.io/helm-charts
helm repo update

准备 Loki 的配置文件 values.yaml

loki:
  auth_enabled: false
  commonConfig:
    replication_factor: 1
  storage:
    type: 'filesystem'
singleBinary:
  replicas: 1

安装 Loki。

helm install --values values.yaml loki grafana/loki

安装 Grafana。

helm install grafana grafana/grafana

通过 port forward 可以访问 Grafana http://localhost:3000 。

POD_NAME="$(kubectl get pod -l app.kubernetes.io/name=grafana -o jsonpath='{.items[0].metadata.name}')"
kubectl --namespace default port-forward $POD_NAME 3000

在 Grafana 中配置 Loki 数据源,指向上面部署 Loki。

安装 Otel Operator

Otel Operator 依赖 cert-manager 进行证书的管理,安装 operator 之前需要安装 cert-manager。

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.2/cert-manager.yaml

执行下面命令安装 Otel Operator

kubectl apply -f https://github.com/open-telemetry/opentelemetry-operator/releases/latest/download/opentelemetry-operator.yaml

配置 Instrumentation

成功安装 Otel Operator 之后,接下来就是配置探针的安装和配置了,详细的配置说明,可以参考 Instrumentation API 文档

Instrumentation 是 Otel Operator 的另一个 CRD,用于自动安装 Otel 探针和配置。本演示虽然主要聚焦在日志,但我们依然保留了之前使用的分布式跟踪的配置,保证链路信息的传递。

  • propagators 用于配置跟踪信息在上下文的传递方式。
  • sampler 采样器
  • env[language].env 添加到容器的环境变量

针对 Java 应用,通过环境变量 OTEL_EXPORTER_OTLP_ENDPOINT 设置 oltp 的端点,以及 OTEL_LOGS_EXPORTER 设置应用 日志的输出方式 oltp。也可以设置为 logging、oltp,将日志输出到控制台以及 oltp 端点。

kubectl apply -f - <<EOF
apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
  name: instrumentation-sample
spec:
  propagators:
    - tracecontext
    - baggage
    - b3
  sampler:
    type: parentbased_traceidratio
    argument: "1"
  env:
    - name: OTEL_EXPORTER_OTLP_ENDPOINT
      value: otel-collector.default:4318
  java:    
    env:
      - name: OTEL_EXPORTER_OTLP_ENDPOINT
        value: http://otel-collector.default:4317   
      - name: OTEL_LOGS_EXPORTER
        value: otlp
EOF

配置 OpenTelemetry Collector

在我们的设计用,Otel Collector 会将日志输出到 Loki,实际上是通过 Loki 的 HTTP API 来发送日志,因此需要使用适配 Loki API 的 exporterlokiexporter

lokiexporter 来自 Otel Collector 的 Contrib 库,并不在官方的 release 中。要想在 collector 中使用 lokiexporter 有两种方式:

Otel 收集器的详细配置可以参考 官方文档

  • 接收器(receiver),我们配置 otlp 来接收来自应用程序的跟踪信息。
  • 处理器(processor),将日志中的部分资源属性作为 loki 的标签,比如服务名、容器名、命名空间、pod 名。
  • 输出器(exporter),配置 Loki 的 HTTP API 端点 http://loki.default:3100/loki/api/v1/push
  • 管道服务(pipeline service),使用 otlp 作为输入源,将 loki 作为输出目的地。
kubectl apply -f - <<EOF
apiVersion: opentelemetry.io/v1alpha1
kind: OpenTelemetryCollector
metadata:
  name: otel
spec:
  image: ghcr.io/open-telemetry/opentelemetry-collector-releases/opentelemetry-collector-contrib:0.90.1
  config: |
    receivers:
      otlp:
        protocols:
          grpc:
          http:

    processors:
      resource:
        attributes:
          - action: insert
            key: loki.resource.labels
            value: service.name, k8s.container.name, k8s.namespace.name, k8s.pod.name

    exporters:
      debug:
        verbosity: detailed
      loki:
        endpoint: "http://loki.default:3100/loki/api/v1/push"
        tls:
          insecure: true
        default_labels_enabled:
          exporter: true
          job: true   

    service:
      pipelines:
        logs:
          receivers: [otlp]
          processors: [resource]
          exporters: [loki]          
EOF

部署示例应用

这是一个非常简单的 Java 应用,监听 8080 端口,在响应请求时打印日志。

@SpringBootApplication
@Slf4j
@RestController
public class SpringBootRestApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringBootRestApplication.class, args);
	}

	@GetMapping("/")
	public String hello() {
		log.info("Hello World");
		return "Hello World";
	}
}

在 Maven 的 pom 中只引入了两个依赖 :spring-boot-starter-weblombok

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.28</version>
        </dependency>
    </dependencies>

部署应用。

kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: java-sample
spec:
  replicas: 1
  selector:
    matchLabels:
      app: java-sample
  template:
    metadata:
      labels:
        app: java-sample
      annotations:
        instrumentation.opentelemetry.io/inject-java: "true"
    spec:
      containers:
      - name: java-sample
        image: addozhang/spring-boot-rest
        imagePullPolicy: Always
        ports:
        - containerPort: 8080
EOF

应用部署完成后,通过端口转发可以成功访问应用。

curl localhost:8080
Hello World

测试

Grafana 配置完 Loki 的数据源之后,在 Explore 中选择配置的 Loki 数据源,然后在下方的 Label Filters 中选择过滤器名 service_name 和值 java-sample

点击 Run query 后可以看到搜索结果。

总结

在本文中,我们探讨了如何利用 OpenTelemetry 的自动检测功能来高效采集应用日志,通过 OpenTelemetry Collector 进行处理,并利用 Loki Exporter 将日志数据发送到 Loki。最后,我们展示了如何使用 Grafana 对这些日志进行深入的查询和分析。这一过程不仅优化了日志管理流程,还提升了数据的可视化和可用性。这种集成为开发者和运维团队提供了一个全面的视角,帮助他们更有效地理解和优化他们的应用和基础设施。特别是,如果将分布式跟踪的 traceid、spanid 等信息作为 Loki 日志的标签,将极大地增强了日志数据的可追踪性和可分析性。

(转载本站文章请注明作者和出处乱世浮生,请勿用于任何商业用途)

comments powered by Disqus