在Kubernetes中从0打造可观测性

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在Kubernetes中从0打造可观测性相关的知识,希望对你有一定的参考价值。



!! 大家好,我是乔克,一个爱折腾的运维工程,一个睡觉都被自己丑醒的云原生爱好者。

作者:乔克
公众号:运维开发故事
博客:www.jokerbai.com

在这篇文章中,我们将在Kubernetes中使用Grafana、Prometheus、Loki、Tempo、OpenTelemetry来搭建可观测性平台。其中Grafana作为操作面板,Prometheus、Loki、Tempo作为数据源,分别用来获取指标、日志以及跟踪数据。同时,我们还将使用Exemplars将trace_id与Java指标相关联,使用OpenTelemetry对应用进行检测。

在开始之前,先简单介绍一下这些开源工具。

  1. OpenTelemetry:它是CNCF的 开源产品,通过使用代理来收集指标、日志和链路,然后将它们发送给其他工具,它支持多种语言集成,并且有很大的仪表功能。
  2. Prometheus:CNCF的毕业产品,是目前主流的监控工具之一。
  3. Examplars:它可以将trace_id和metrics联系起来,可以帮助我们通过指标获取到具体日志以及链路状况,通常和Prometheus配合工作。
  4. Promtail:日志收集工具,将日志发送到Loki。
  5. Loki:收集并处理日志,并且支持通过LogQL来查询日志,其语法和PromQL类似
  6. Tempo:接收OpenTelemetry的数据,并且可以通过Jaeger将其可视化
  7. Grafana:支持多种数据源的可视化面板

在Kubernetes中从0打造可观测性_spring

image.png

准备后端应用程序

在这个示例中,我们将使用java spring boot项目作为例子。

首先,我们使用start.spring.io创建一个java spring boot项目,它可以帮我们快速创建一个Java项目,并且支持在项目中添加依然和其他配置。

在Kubernetes中从0打造可观测性_jar_02

其中:

  • 使用Gradle作为构建自动化工具
  • 使用2.7版本的Spring Boot
  • 使用JAR作为包构建格式
  • 使用JDK11

当完成配置并生成之后,就可以将其压缩包下载下来并用IDE打开。

在Kubernetes中从0打造可观测性_jar_03

我们先配置​​build.gradle​​,确保所有依赖是没问题的。

plugins 
id org.springframework.boot version 2.7.0
id io.spring.dependency-management version 1.0.11.RELEASE
id java


repositories
maven
url = uri(https://repo.spring.io/libs-snapshot)

mavenCentral()


dependencyManagement
imports
mavenBom io.micrometer:micrometer-bom:1.9.0-SNAPSHOT



dependencies
implementation org.springframework.boot:spring-boot-starter-actuator
implementation io.micrometer:micrometer-registry-prometheus:1.9.0
implementation org.springframework.boot:spring-boot-starter-web
implementation io.opentelemetry:opentelemetry-api:1.12.0


tasks.named(test)
useJUnitPlatform()


group = com.staz
version = 0.0.1-SNAPSHOT
sourceCompatibility = 11

然后我们将创建一个控制器类​​Controller.java​​​,有两个端点:​​/fail​​​ 和 ​​/success​​​。该文件必须位于​​$project/src/main/java/com/staz/observability/​​的路径下。

package com.staz.observability;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class Controller

@PostMapping("/fail")
public String fail()
return "Fail!";


@GetMapping("/success")
public String success()

return "Success!";


为了将metrics和trace_id关联起来,我们需要在​​$project/src/main/java/com/staz/observability/​​​路径下创建一个公共配置类​​PrometheusExemplarConfiguration.java​​。

package com.staz.observability;

import io.micrometer.core.instrument.Clock;
import io.micrometer.prometheus.PrometheusConfig;
import io.micrometer.prometheus.PrometheusMeterRegistry;
import io.opentelemetry.api.trace.Span;
import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.exemplars.DefaultExemplarSampler;
import io.prometheus.client.exemplars.tracer.otel_agent.
OpenTelemetryAgentSpanContextSupplier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class PrometheusExemplarConfiguration
@Bean
public PrometheusMeterRegistry prometheusMeterRegistryWithExemplar
(PrometheusConfig prometheusConfig, CollectorRegistry collectorRegistry,
Clock clock)
return new PrometheusMeterRegistry(prometheusConfig, collectorRegistry,
clock, new DefaultExemplarSampler(new OpenTelemetryAgentSpanContextSupplier()

@Override
public String getTraceId()
if (!Span.current().getSpanContext().isSampled())
return null;

return super.getTraceId();

)
);

最后,编辑​​$project/src/main/resources/​​​目录下的配置文件​​application.yml​​:

# Enable Actuator endpoints including Prometheus
management:
endpoints:
web:
exposure:
include: health, info, prometheus
metrics:
# Exemplar metrics
distribution:
percentiles-histogram:
http.server.requests: true
minimum-expected-value:
http.server.requests: 5ms
maximum-expected-value:
http.server.requests: 1000ms

# Add trace_id in log. OpenTelemetry set this value using logger-mdc.
# https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/docs/logger-mdc-instrumentation.md
logging:
pattern:
level: %prefix(%mdctrace_id:-0) %5p

如果想要在本地运行项目,需要下载​​OpenTelemetry Agent​​,该项目中使用的版本是1.12.1。

准备工作做完过后,我们在本地来测试一下。

首先,使用​​gradle build -x test​​编译项目。

在Kubernetes中从0打造可观测性_jar_04

然后使用以下命令启动:

java -javaagent:opentelemetry-javaagent.jar -Dspring.config.location=src/main/resources/application.yml -jar build/libs/observability-0.0.1-SNAPSHOT.jar

在Kubernetes中从0打造可观测性_spring_05

image.png然后可以使用​​htttp://localhost:8080/fail​​​和​​htttp://localhost:8080/success​​进行访问测试。

在Kubernetes中从0打造可观测性_jar_06

在Kubernetes中从0打造可观测性_java_07

再来使用​​localhost:8080/actuator/prometheus​​来验证Prometheus指标是否有效。

在Kubernetes中从0打造可观测性_jar_08

最后,验证metrice和trace_id的关联情况。

curl -H Accept: application/openmetrics-text; version=1.0.0; charset=utf-8 http://localhost:8080/actuator/prometheus | grep trace_id

在Kubernetes中从0打造可观测性_spring_09

image.png

我们的Spring Boot应用程序已经准备好了,现在我们需要安装观察性工具。在此之前,我们会在本地创建一个K3s集群,所有的软件都将部署到里面。

容器化应用程序

首先,在项目根目录创建一个Dockerfile,内容如下:

# Download OpenTelemetryAgent
FROM curlimages/curl:7.81.0 AS OTEL_AGENT
ARG OTEL_AGENT_VERSION="1.12.1"
RUN curl --silent --fail -L "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v$OTEL_AGENT_VERSION/opentelemetry-javaagent.jar" \\
-o "/tmp/opentelemetry-javaagent.jar"

# Build .JAR file
FROM gradle:7.1.1-jdk11-hotspot AS BUILD_IMAGE
COPY --chown=gradle:gradle . /home/gradle/src
WORKDIR /home/gradle/src
RUN gradle build -x test --no-daemon

# Final image copying OTEL Agent and .JAR File
FROM gradle:7.1.1-jdk11-hotspot
ENV TIME_ZONE America/Lima
ENV TZ=$TIME_ZONE
ENV JAVA_OPTS "-Dspring.config.location=src/main/resources/application.yml"
COPY --from=OTEL_AGENT /tmp/opentelemetry-javaagent.jar /otel-javaagent.jar
COPY --from=BUILD_IMAGE home/gradle/src/build/libs/*.jar app.jar
ENTRYPOINT exec java -javaagent:/otel-javaagent.jar -jar app.jar

使用以下命令构建并测试:

$ docker build --no-cache -t otel-springboot-prometheus .
$ docker run -it -p 8080:8080 otel-springboot-prometheus

待容器启动过后,使用​​http://localhost:8080/success​​验证是否可以正常使用。

创建单节点集群

首先,使用multipass创建一个Ubuntu实例:

$ multipass launch --name demo --mem 4G --disk 20G

在Kubernetes中从0打造可观测性_spring_10

image.png

然后登录实例:

$ multipass shell demo

在Kubernetes中从0打造可观测性_jar_11

image.png

可以通过​​sudo su​​命令验证是否正确进去Ubuntu实例。

其次,使用以下命令安装K3s:

$ curl -sfL https://get.k3s.io | sh -

集群创建完成后,将​​KUBECONFIG​​添加到环境变量。

$ export KUBECONFIG=/etc/rancher/k3s/k3s.yaml

检查集群是否正常运行。

$ kubectl cluster-info

在Kubernetes中从0打造可观测性_jar_12

image.png

然后,安装Helm,后续都将使用它来安装应用软件。

$ snap install helm --classic

将K3s的​​KUBECONFIG​​​拷贝到​​~/.kube/config​​目录下。

$ kubectl config view --raw > ~/.kube/config

最后,检查Helm是否能正常工作。

$ helm

在Kubernetes中从0打造可观测性_java_13

部署可观测性组件

在该阶段,我们将使用Helm部署Prometheus、Promtail、Loki、Tempo以及Grafana,最后部署应用并验证。

!! 以上应用都将部署到K3s中。

首先,从仓库把需要的manifests克隆下来。

$ git clone https://github.com/stazdx/otel-springboot-grafana-tools.git
$ cd otel-springboot-grafana-tools/kubernetes

然后,添加Helm仓库。

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

在Kubernetes中从0打造可观测性_spring_14

image.png

最后,创建一个namespace,所有应用都部署到该namespace下。

$ kubectl create ns observability

在Kubernetes中从0打造可观测性_spring_15

image.png

部署Promtail

使用以下命令进行部署:

$ cd promtail
$ helm upgrade --install promtail grafana/promtail -n observability -f promtail.yaml

在Kubernetes中从0打造可观测性_jar_16

image.png

注意检查​​Promtail​​​所指向的​​Loki​​地址。

部署Loki

部署命令如下:

$ helm upgrade --install loki grafana/loki-distributed -n observability

在Kubernetes中从0打造可观测性_spring_17

image.png

​loki-loki-distributed-gateway​​这个Service非常重要,Promtail将向它发送数据,Grafana将通过它获取数据。

部署Tempo

首先,进入Tempo清单所在的目录:

$ cd ../tempo

在安装Tempo之前,我们需要先安装minio,命令如下:

$ kubectl apply -f minio.yaml

在Kubernetes中从0打造可观测性_java_18

image.png

!! Minio被部署在default命名空间中,因为它是一个更通用的对象存储工具,而不是直接用于观察性。

现在,使用以下命令部署Tempo:

$ helm upgrade --install tempo grafana/tempo-distributed -n observability -f tempo.yaml

在Kubernetes中从0打造可观测性_spring_19

image.png

!! Grafana将通过​​_tempo-tempo-distributed-query-frontend:3100_​​来获取数据。

部署Prometheus和Grafana

Prometheus和Grafana直接使用官网仓库进行部署。

首先,添加Helm仓库。

$ helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
$ helm repo update

然后,使用仓库清单进行部署。

$ cd ../prometheus-grafana
$ helm dependency update
helm upgrade --install kube-prometheus-stack -n observability .

在Kubernetes中从0打造可观测性_jar_20

image.png

检查Deployments

使用Helm命令查看部署的所有应用。

$ helm ls -n observability

在Kubernetes中从0打造可观测性_java_21

image.png

然后,使用kubectl检查应用是否都启动成功。

$ kubectl get po -n observability

在Kubernetes中从0打造可观测性_jar_22

image.png

检查Service是否正常。

$ kubectl get svc -n observability

在Kubernetes中从0打造可观测性_jar_23

image.png

我们看到所有应用都正常部署完成。

部署后端应用

直接到仓库目录清单部署即可。

$ cd ../springboot-app

需要注意的是,为了能够让Prometheus能够正常抓取指标,我们需要添加以下​​Annotations​​。

annotations:
# Annotations for Prometheus - scrape config
prometheus.io/path: /actuator/prometheus
prometheus.io/port: actuator
prometheus.io/scrape: true

另外一个重要的配置就是OpenTelemetry配置,如下:

env:
- name: SERVER_PORT
value: 8080
- name: MANAGEMENT_SERVER_PORT
value: 8081
# Setting OTEL_EXPORTER_METRICS: none - Default: OTLP
- name: OTEL_METRICS_EXPORTER
value: none
- name: OTEL_TRACES_EXPORTER
value: otlp,logging
# Setting Tempo Distributor Service using GRPC Port -> 4317
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: http://tempo-tempo-distributed-distributor.observability.svc.cluster.local:4317
- name: OTEL_SERVICE_NAME
value: springboot-app
- name: KUBE_POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: OTEL_RESOURCE_ATTRIBUTES
value: app=springboot-app

最后,我们有一个包含Spring Boot的Grafana仪表盘的配置图,这将使我们能够通过请求延迟等指标看到Exemplar与Tempo的关联。

配置检查无误后,就可以进行部署了。

$ kubectl apply -f springboot-app.yaml

在Kubernetes中从0打造可观测性_jar_24

image.png

检查应用是否部署成功。

$ kubectl get deploy,svc,cm -l app=springboot-app

在Kubernetes中从0打造可观测性_spring_25

image.png

接口测试

首先,测试​​/fail​​​接口:​​http://external-ip:8080/fail​​。

在Kubernetes中从0打造可观测性_spring_26

然后,测试​​/success​​​接口:​​http://external-ip:8080/success​​。

在Kubernetes中从0打造可观测性_jar_27

最后,测试​​/actuator/prometheus​​​接口:​​http://external-ip:8081/actuator/prometheus​​。

在Kubernetes中从0打造可观测性_spring_28

可以看到所有接口返回正常。

Grafana测试

上面以及完成了所有的配置,接下来就在Grafana中验证是否能够正常使用。

首先,获取Grafana的访问地址。

$ kubectl get svc -n observability

在Kubernetes中从0打造可观测性_java_29

image.png在浏览器输入地址​​http://external-ip:32656​​。

在Kubernetes中从0打造可观测性_spring_30

然后,添加数据源。

在Kubernetes中从0打造可观测性_jar_31

我们把Prometheus、Loki以及Tempo数据源都添加上。

在Kubernetes中从0打造可观测性_jar_32

其中,Prometheus的配置如下:

在Kubernetes中从0打造可观测性_jar_33

!! 可以看到Prometheus和Tempo通过Exemplars进行关联了。

Loki的配置如下:

在Kubernetes中从0打造可观测性_java_34

!! 可以看到Loki和Tempo通过trace_id进行关联了。

Tempo的配置如下:

在Kubernetes中从0打造可观测性_java_35

!! 在这里我们将Tempo与Loki相关联,并映射我们在微服务中配置的应用标签。

测试一下

通过Explore可以查看应用日志。

在Kubernetes中从0打造可观测性_java_36

选择Loki数据源。

在Kubernetes中从0打造可观测性_java_37

通过Loki,我们可以通过label对监控日志进行过滤。

在Kubernetes中从0打造可观测性_jar_38

从日志中,我们可以看到trace信息。

在Kubernetes中从0打造可观测性_spring_39

然后,我们查看Grafana面板。

在Kubernetes中从0打造可观测性_jar_40

我们选择Spring Boot Demo,它是我们自己创建的面板。

在Kubernetes中从0打造可观测性_spring_41

我们可以看到应用的请求延迟,另外星星是由Exemplar生成。

在Kubernetes中从0打造可观测性_spring_42

在Kubernetes中从0打造可观测性_jar_43

用鼠标悬停在它上面,我们可以看到它是如何与一个trace_id相关联的,当点击它时,它将把我们重定向到Tempo。

在Kubernetes中从0打造可观测性_java_44

我们可以看到它产生的跟踪,我们也可以看到日志,因为它也是与Loki相关的,当点击时我们会看到具体信息:

在Kubernetes中从0打造可观测性_java_45

屏幕被分割,但是我们可以看到具体的日志了。

最后

我们实现了指标、日志和跟踪之间的可观察性关联。这可以帮助我们在微服务的故障排除过程中,识别瓶颈,看到我们的应用指标的行为,并能够获得特定的跟踪和日志。



最后,求关注。如果你还想看更多优质原创文章,欢迎关注我们的公众号「运维开发故事」。

我是 乔克,《运维开发故事》公众号团队中的一员,一线运维农民工,云原生实践者,这里不仅有硬核的技术干货,还有我们对技术的思考和感悟,欢迎关注我们的公众号,期待和你一起成长!



以上是关于在Kubernetes中从0打造可观测性的主要内容,如果未能解决你的问题,请参考以下文章

深度解析|基于 eBPF 的 Kubernetes 一站式可观测性系统

实践:Kubernetes 集群中 DNS 故障的可观测性与根因诊断

如何专业化监控一个Kubernetes集群?

Istio和Kubernetes帮助Trulia房产网站消除单体架构增强微服务的可观测性

一篇文章带你看懂计算机系统监控与可观测性发展史(干货)

如何专业化监控一个Kubernetes集群?