使用 OpenTelemetry 和 Loki 实现高效的应用日志采集和分析
在之前的文章陆续介绍了 如何在 Kubernetes 中使用 Otel 的自动插桩 以及 Otel 与 服务网格协同实现分布式跟踪,这两篇的文章都将目标聚焦在分布式跟踪中,而作为可观测性三大支柱之一的日志也是我们经常使用的系统观测手段,今天这篇文章就来体验下应用日志的操作闭环。
背景
OpenTelemetry 简介
OpenTelemetry (以下简称 Otel)是一个开源项目,旨在为分布式追踪、度量和日志提供统一的标准,简化应用程序的观测性(Observability)。它提供了一系列工具和 API,用于收集和传输应用程序的性能数据和日志,帮助开发者和运维团队更好地理解系统的行为。功能包括自动和手动检测应用程序的追踪数据,收集关键度量指标,以及捕获和传输日志。Otel 支持多种编程语言和框架,可以与多个后端系统集成,如 Prometheus、Jaeger、Elasticsearch 等。
Log 是 OpenTelemetry 项目的一部分,旨在提供一种标准化的方式来收集、传输和存储日志数据。
Loki 简介
Loki 是 Grafana Labs 开发的一个水平可扩展、高可用性、多租户的日志聚合系统,专为效率和易用性而设计。与传统的日志聚合系统不同,Loki 主要索引日志内容的元数据而不是内容本身,这使得它既轻量又高效。Loki 采用了与 Prometheus 类似的标签系统,使得日志查询更加灵活和强大。常用于存储和查询大量日志数据,特别是与 Grafana 结合使用时,提供了强大的日志可视化和分析能力。
演示
在本演示中将使用 Java 应用进行日志闭环操作的演示,在 Otel Log 支持的语言 中,Java 是的最全面的语言之一。
架构
- Otel Operator 通过自动插桩的配置,为 Java 工作负载安装探针并加载配置
- 应用通过 otlp 端点上报日志到 Otel collector
- Otel collector 将日志输出到 Loki
- 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 的 exporter:lokiexporter。
lokiexporter 来自 Otel Collector 的 Contrib 库,并不在官方的 release 中。要想在 collector 中使用 lokiexporter 有两种方式:
- 使用官方提供的工具 OpenTelemetry Collector Builder (ocb) 在构建 collector 的二进制时将 lokiexporter 加入其中。
- 使用 官方提供的发行包 otelcol-contrib,这个包中包含了 Contrib 库所有的第三方组件。**但不推荐在生产环境中使用,仅供测试。**我们的演示中将会使用该发行包。
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-web
和 lombok
。
<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 日志的标签,将极大地增强了日志数据的可追踪性和可分析性。