在这篇文章中,我们将在Kubernetes中使用Grafana、Prometheus、Loki、Tempo、OpenTelemetry来搭建可观测性平台。其中Grafana作为操作面板,Prometheus、Loki、Tempo作为数据源,分别用来获取指标、日志以及跟踪数据。同时,我们还将使用Exemplars将trace_id与Java指标相关联,使用OpenTelemetry对应用进行检测。在开始之前,先简单介绍一下这些开源工具。
其中:
我们先配置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'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!";
}
}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();
}
})
);
}
}# 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(%mdc{trace_id:-0}) %5p'
然后使用以下命令启动:java -javaagent:opentelemetry-javaagent.jar -Dspring.config.locatinotallow=src/main/resources/application.yml -jar build/libs/observability-0.0.1-SNAPSHOT.jar
然后可以使用htttp://localhost:8080/fail和htttp://localhost:8080/success进行访问测试。
再来使用localhost:8080/actuator/prometheus来验证Prometheus指标是否有效。
最后,验证metrice和trace_id的关联情况。curl -H 'Accept: application/openmetrics-text; versinotallow=1.0.0; charset=utf-8' http://localhost:8080/actuator/prometheus | grep trace_id
我们的Spring Boot应用程序已经准备好了,现在我们需要安装观察性工具。在此之前,我们会在本地创建一个K3s集群,所有的软件都将部署到里面。# 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.locatinotallow=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$ multipass launch --name demo --mem 4G --disk 20G
然后登录实例:$ multipass shell demo
可以通过sudo su命令验证是否正确进去Ubuntu实例。其次,使用以下命令安装K3s:$ curl -sfL https://get.k3s.io | sh -$ export KUBECONFIG=/etc/rancher/k3s/k3s.yaml$ kubectl cluster-info
然后,安装Helm,后续都将使用它来安装应用软件。$ snap install helm --classic$ kubectl config view --raw > ~/.kube/config$ helm
以上应用都将部署到K3s中。首先,从仓库把需要的manifests克隆下来。
$ git clone https://github.com/stazdx/otel-springboot-grafana-tools.git
$ cd otel-springboot-grafana-tools/kubernetes$ helm repo add grafana https://grafana.github.io/helm-charts
$ helm repo update
最后,创建一个namespace,所有应用都部署到该namespace下。$ kubectl create ns observability
$ cd promtail
$ helm upgrade --install promtail grafana/promtail -n observability -f promtail.yaml
注意检查Promtail所指向的Loki地址。$ helm upgrade --install loki grafana/loki-distributed -n observability
loki-loki-distributed-gateway这个Service非常重要,Promtail将向它发送数据,Grafana将通过它获取数据。$ cd ../tempo$ kubectl apply -f minio.yaml
Minio被部署在default命名空间中,因为它是一个更通用的对象存储工具,而不是直接用于观察性。现在,使用以下命令部署Tempo:
$ helm upgrade --install tempo grafana/tempo-distributed -n observability -f tempo.yaml
!! Grafana将通过_tempo-tempo-distributed-query-frontend:3100_来获取数据。
$ 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 .
$ helm ls -n observability
然后,使用kubectl检查应用是否都启动成功。$ kubectl get po -n observability
检查Service是否正常。$ kubectl get svc -n observability
我们看到所有应用都正常部署完成。$ cd ../springboot-appAnnotations。annotations:
# Annotations for Prometheus - scrape config
prometheus.io/path: '/actuator/prometheus'
prometheus.io/port: 'actuator'
prometheus.io/scrape: 'true'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$ kubectl apply -f springboot-app.yaml
检查应用是否部署成功。$ kubectl get deploy,svc,cm -l app=springboot-app
然后,测试/success接口:http://{external-ip}:8080/success。
最后,测试/actuator/prometheus接口:http://{external-ip}:8081/actuator/prometheus。
可以看到所有接口返回正常。$ kubectl get svc -n observability
在浏览器输入地址http://{external-ip}:32656。
然后,添加数据源。
我们把Prometheus、Loki以及Tempo数据源都添加上。
其中,Prometheus的配置如下:
!! 可以看到Prometheus和Tempo通过Exemplars进行关联了。Loki的配置如下:

!! 可以看到Loki和Tempo通过trace_id进行关联了。Tempo的配置如下:

!! 在这里我们将Tempo与Loki相关联,并映射我们在微服务中配置的应用标签。
选择Loki数据源。
通过Loki,我们可以通过label对监控日志进行过滤。
从日志中,我们可以看到trace信息。
然后,我们查看Grafana面板。
我们选择Spring Boot Demo,它是我们自己创建的面板。
我们可以看到应用的请求延迟,另外星星是由Exemplar生成。
用鼠标悬停在它上面,我们可以看到它是如何与一个trace_id相关联的,当点击它时,它将把我们重定向到Tempo。
我们可以看到它产生的跟踪,我们也可以看到日志,因为它也是与Loki相关的,当点击时我们会看到具体信息:
屏幕被分割,但是我们可以看到具体的日志了。这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/
我想上传我在运行时用Ruby生成的数据,就像从block中提供上传数据一样。我找到的所有示例仅展示了如何流式传输必须在请求之前位于磁盘上的文件,但我不想缓冲该文件。除了滚动我自己的套接字连接之外,最好的解决方案是什么?这是一个伪代码示例:post_stream('127.0.0.1','/stream/')do|body|generate_xmldo|segment|body 最佳答案 有效的代码。require'thread'require'net/http'require'base64'require'openssl'class
我目前从prototype切换到jquery主要是为了支持简单的ajax文件上传。我使用:https://github.com/indirect/jquery-rails95%的javascript代码是由railshelper编写的,例如:-remote_function-render:updatedo|page|-page.replace_html'id',:partial=>'content'-page['form']['name']=something-page.visual_effect:highlight,'head_success'...我知道我必须为Jquery重写5%
我正在编写一个可配置的Rails引擎。我有一个authentication_helper配置选项来定义在所有需要身份验证的Controller的before_action中应该调用哪个助手。问题是我无法从引擎的Controller访问父应用程序的助手。Myunderstanding是因为引擎是隔离的。我考虑过使用block而不是方法名称,但我不确定这是否可行,或者我是否能够从我的Controller外部干净地访问授权逻辑。ActiveAdmin,我过去用过,有一个类似的配置选项。我注意到他们的引擎不是隔离的,所以我可能高估了引擎隔离的重要性?有没有一种优雅的方式既可以享受引擎隔离的好处
文章目录🔥Linux系统目录结构🔥Linux用户和用户组🔥Linux用户管理🔥Linux系统目录结构文件系统组织结构⭐ /lib系统开机所需要最基本的动态链接共享库,其作用类似于Windows里的DLL文件。几乎所有的应用程序都需要用到这些共享库。⭐ /lost+found一般情况下是空的,当系统非法关机后,这里就存放了一些文件。⭐ /etc所有系统管理所需要的配置文件和子目录my.conf⭐ /usr用户的很多应用程序和文件都放在这个目录下。⭐ /bin是Binary的缩写,这个目录存放着经常使用的命令⭐ /sbin(usr/sbin、/usr/local/sbin)sbin就是peruse
我创建了一个包含自定义gemset的项目特定.rvmrc文件(使用命令rvm--rvmrc--create1.9.2@registration):#excerptof.rvmrc...environment_id="ruby-1.9.2@registration"if[[-d"${rvm_path:-$HOME/.rvm}/environments"\&&-s"${rvm_path:-$HOME/.rvm}/environments/$environment_id"]]then\."${rvm_path:-$HOME/.rvm}/environments/$environment_id
我想在ruby中用0.0001步从0.0001数到1。我写了这段代码,但它进入了无限循环。为什么解释器做了错误的求和。x=0.0001whilex!=1.0putsxx=x+0.0001end这是它给出的前10个值:0.00010.00020.000300000000000000030.00040.00050.00060000000000000010.00070000000000000010.00080000000000000010.00090000000000000020.0010000000000000002应该是0.0001、0.0002、0.0003等等……我怎样才能让它工
第一章Selenium+WebDriver环境搭建第二章Selenium定位方式第三章元素常用属性第四章自动化中的三种等待第五章自动化浏览器设置及句柄、窗口切换操作第六章鼠标、键盘操作第七章javascript在自动化中的应用第八章unittest&断言第九章ddt数据驱动第十章测试框架搭建过程Python+Selenium+BeautifulReport文章目录一、鼠标操作二、键盘操作一、鼠标操作1、在web测试中,鼠标的操作包含在ActionChains类中,经常用到的有单击、双击、右击、拖动等操作。2、在使用鼠标操作前需要先导入ActionChains类包:fromselenium.we
Kubernetes(K8s)是一个用于管理容器化应用程序的开源平台,可以帮助开发人员更轻松地部署、管理和扩展应用程序。在Kubernetes中,集群划分是一种重要的概念,可以帮助我们更好地组织和管理集群中的节点和资源。本文将介绍如何使用Kubernetes对集群进行划分,并提供详细的操作示例,希望能够帮助读者更好地了解和使用Kubernetes平台。Node划分Node划分是将集群中的节点按照一定的规则进行划分。在Kubernetes中,可以使用NodeSelector和Affinity机制来实现Node划分。NodeSelectorNodeSelector是一种将Pod调度到符合特定节点标
文章目录Kubernetes(k8s)工作负载一、Workloads二、Pod三、Deployment四、RC、RS、DaemonSet、StatefulSet五、Job、CronJob1、Job2、CronJob六、GCKubernetes(k8s)工作负载一、Workloads什么是工作负载(Workloads)工作负载是运行在Kubernetes上的一个应用程序。一个应用很复杂,可能由单个组件或者多个组件共同完成。无论怎样我们可以用一组Pod来表示一个应用,也就是一个工作负载Pod又是一组容器(Containers)所以关系又像是这样工作负载(Workloads)控制一组PodPod控制