前言
最近研究微服务的服务健康监控,发现大多是集成Spring Boot Admin
,优点是配置简单比较轻量化,缺点包含:(1) 界面比较简单,且不那么美观; (2) 它的实时查看日志功能是读取服务器上的日志文件,那当我的微服务与admin-server所在的服务不处于同一服务器上时就读不到了,换言之就是对分布式支持较差。 (3) 监控到的服务状态只能维持较短时间段,难以回溯较早的信息。
搜索比对市面上解决方案最终采用 Prometheus + Loki + Grafana
这套方案来集成一下到SpringCloud,实现更加美观且功能更全面的监控中心。看起来用到的东西比较多,但是他们是相辅相成的,简单来说:
Prometheus
和是一个开源的系统监控和警报工具,用于收集和存储应用程序和系统的时间序列数据。
Loki
是一个水平可扩展,高可用性,多租户的日志聚合系统,受到Prometheus
的启发。它的设计非常经济高效且易于操作,因为它不会为日志内容编制索引,而是为每个日志流编制一组标签。官方介绍说到:Like Prometheus, but for logs。
Grafana
是一款开源的数据可视化工具,使用Grafana可以非常轻松的将数据转成图表的展现形式来做到数据监控以及数据统计。且Grafana
内置了对Prometheus
和Loki
的支持,可以只需几步配置就把他们收集到的信息转化为好看的图表进行展现。
以下为集成后效果图:

集成过程中踩了不少坑,也搜索了很多文档,在此整合一下,希望需要用到的朋友能够少走些弯路。
环境说明
本次集成属于尝鲜性质,使用最新版本的开源组件集成到现有项目上。注册中心使用Nacos
。
raw
# 开源组件版本
grafana latest
loki latest
prometheus latest
# 项目相关
SpringCloud: 2021.0.3
SpringBoot: 2.7.1
JDK: 1.8
Nacos: 2.2.0
任务拆解
经过以上分析,本次集成过程将按照以下几步步骤进行:
-
安装部署
-
grafana
集成SpringCloud
实现监控服务信息,并配合Nacos
进行服务发现。 -
loki
集成微服务收集日志。 -
配置
grafana
,以图表方式展示服务信息和服务日志。
集成过程
安装部署
本次需要到的开源组件有三个:grafana
, loki
, prometheus
。每一个官网都提供了至少两种安装部署方式:二进制文件
安装和 docker
部署, 这里直接贴出笔者经过整合的docker-compose
配置文件,一步搞定,省去繁琐的配置过程,如果想要以其他方式安装部署,可以参考官方文档或者搜索其他博客。
grafana+loki 官方文档 | grafana.com/docs/loki/v...
prometheus 官方文档 | prometheus.io/docs/promet...
查看后不难发现官方也是推荐使用docker
方式进行部署,下面我们使用docker-compose
来快速部署。(需要服务器安装好docker
和docker-compose
,不会装的可以搜下其他博客)。
- 找一个路径,准备存放
docker-compose.yaml
配置文件,执行命令:
shell
vim docker-compose.yaml
打开vim
编辑器后粘贴一下内容进去:
yml
version: "3"
networks:
loki:
services:
loki:
image: grafana/loki:latest
ports:
- "3100:3100"
command: -config.file=/etc/loki/local-config.yaml
networks:
- loki
promtail:
image: grafana/promtail:latest
volumes:
- /var/log:/var/log
command: -config.file=/etc/promtail/config.yml
networks:
- loki
grafana:
environment:
- GF_PATHS_PROVISIONING=/etc/grafana/provisioning
- GF_AUTH_ANONYMOUS_ENABLED=true
- GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
entrypoint:
- sh
- -euc
- |
mkdir -p /etc/grafana/provisioning/datasources
cat <<EOF > /etc/grafana/provisioning/datasources/ds.yaml
apiVersion: 1
datasources:
- name: Loki
type: loki
access: proxy
orgId: 1
url: http://loki:3100
basicAuth: false
isDefault: true
version: 1
editable: false
EOF
/run.sh
image: grafana/grafana:latest
ports:
- "3000:3000"
networks:
- loki
prometheus:
image: prom/prometheus:latest
container_name: prometheus
hostname: prometheus
restart: always
volumes:
- /home/apps-docker/prometheus/config/prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
networks:
- loki
粘贴完后保存文件内容。 可以看到该compose文件内包含了四个服务的整合:loki, promtail, grafana, prometheus
,前三个是loki官网提供出来的配置文件里内置的,我只修改了版本号,需要注意promtail
和prometheus
容易造成混淆,promtail
是配合loki
使用的,其配置文件格式和prometheus
极其有很多相似的地方,他是一个为loki
主动收集日志的组件,其理念也类似prometheus
, 但是我们集成使用的方式是通过微服务调用loki
的日志上传接口,并不需要他去主动收集日志,所以这次是没有用到的,但可以先安装上备用;prometheus
部分的配置是我从其他博客参考后整理到里面的,简化了配置,但是其中用到了一个映射:

这是因为我们需要在安装prometheus
后修改一部分配置文件,映射到服务器上比较方便,这个路径要改成你自己存放该配置文件的路径。也就是接下来要做的事:
找一个路径,预备存放prometheus.yml
配置文件,然后输入:
shell
vim prometheus.yml
粘贴如下内容进去:
vim
# my global config
global:
scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
# scrape_timeout is set to the global default (10s).
# Alertmanager configuration
alerting:
alertmanagers:
- static_configs:
- targets:
# - alertmanager:9093
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
# - "first_rules.yml"
# - "second_rules.yml"
# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
- job_name: "prometheus"
# metrics_path defaults to '/metrics'
# scheme defaults to 'http'.
static_configs:
- targets: ["localhost:9090"]
保存后修改上面的docker-compose.yaml
内的映射路径为该文件的路径。
然后回到docker-compose.yaml
所在目录,执行:
shell
docker-compose -f docker-compose.yaml up
即可一键安装loki + promtail + grafana + prometheus
,看到有日志打印等待片刻后可以按ctrl + Z
退出,然后输入以下命令查看服务情况:
shell
docker ps
观察到四个容器在运行即为安装部署成功,且服务都已经成功启动。

其中promtail
的容器如果不使用可以把它停掉。
Prometheus
集成 SpringCloud
监控服务信息
SpringBoot开放监控信息查询接口
- 选择一个测试用的
SpringBoot
服务,在pom里添加如下依赖:
yml
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<version>1.3.1</version>
</dependency>
- 然后配置yml文件添加如下内容:
yml
management:
endpoints:
web:
exposure:
include: '*'
metrics:
tags:
application: ${spring.application.name}
启动服务,访问如下路径:
raw
http://ip:端口/actuator/prometheus
如果能看到类似如下结果,说明接口开放成功:

Prometheus配置收集服务信息
- 在
prometheus.yml
配置文件最下面添加如下配置:
yml
- job_name: "springboot"
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ["ip:port"]
ip和端口处改为你实际启动的SpringBoot服务的信息,即可使prometheus定时去采集服务信息。
- 访问
http://{prometheus所在的服务器的ip}:9090/targets
,在列表内看到自己启动的服务即为成功,点击列表里的链接同样可以看到SpringBoot开放的接口返回的内容。

优化配置,集成Nacos服务发现
通过上面简单两步已经实现了prometheus与SpringBoot的集成,但是我们的目标是监控微服务集群,如果采用上面的配法,需要在static_configs
下的targets
数组配置里手动添加各个微服务节点的ip和端口,每次有新的微服务添加都需要手动修改配置文件然后重启prometheus,这显然不靠谱。那么区别于静态服务注册,prometheus还提供了数种服务发现
方案,包含通过文件、DNS、k8s、consul注册中心来动态发现服务的方式,但是遗憾的是,我们项目中所采用的Eruka
及Nacos
不在其列,而好消息是,Eruka和Nacos本身有和consul
注册中心类似的接口,只要对Eruka或Nacos获取的数据进行改造,就可以伪装成 consul
与prometheus对接。这部分内容已有大佬研究实现,可参考博客:
CSDN博客| blog.csdn.net/LCBUSHIHAHA...
github:nacos适配 | github.com/weixiaohui-...
github:eureka适配 | github.com/twinformati...
下面以使用Nacos
为例,介绍接入方法:
在以上步骤的基础上,在gateway
的pom里添加如下引用:
xml
<dependency>
<groupId>io.github.chen-gliu</groupId>
<artifactId>nacos-consul-adapter</artifactId>
<version>version</version>
</dependency>
然后修改prometheus.yml
配置文件,将之前配置的静态job
部分修改为基于consul
服务发现的如下配置:
yml
- job_name: 'consul-service-discovery'
metrics_path: '/actuator/prometheus'
consul_sd_configs:
- server: gateway服务ip:端口
services: []
server
部分需要改成你的项目中的网关服务的运行ip和端口,配置完成后重启
prometheus服务即可。之后可以多添加几个SpringBoot
节点上来,通过http://{prometheus所在的服务器的ip}:9090/targets
查看是否注册成功。
以下为prometheus.yml
配置文件完整示例,仅供参考:
yaml
# my global config
global:
scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
# scrape_timeout is set to the global default (10s).
# Alertmanager configuration
alerting:
alertmanagers:
- static_configs:
- targets:
# - alertmanager:9093
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
# - "first_rules.yml"
# - "second_rules.yml"
# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
- job_name: "prometheus"
# metrics_path defaults to '/metrics'
# scheme defaults to 'http'.
static_configs:
- targets: ["localhost:9090"]
# consul service discovery
- job_name: "consul-service-discovery"
metrics_path: '/actuator/prometheus'
consul_sd_configs:
- server: 192.168.20.80:8080
services: []
SpringCloud集成Loki上传日志
SpringCloud集成Loki实质上是将Loki
与Logback
进行集成,在产生日志时调用Loki的push
接口,将微服务日志上传
到Loki服务器,故而可以实现分布式日志监控。
SpringBoot引入loki-logback-appender
这个包的引入和配置方式根据Java环境有所不同,可参考官方文档:
loki4j logback | loki4j.github.io/loki-logbac...
以下以Java8为例,介绍引入步骤。
- 选择一个测试微服务,在
pom
内引入如下依赖:
xml
<dependency>
<groupId>com.github.loki4j</groupId>
<artifactId>loki-logback-appender-jdk8</artifactId>
<version>1.4.2</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.14</version>
</dependency>
- 修改或创建项目的
logback**.xml
文件, 添加如下片段:

红框圈出的是与Loki
相关的配置,需要添加到本来的配置文件代码中,这里除常规内容外,我多配置打印了服务器的IP和端口,以及服务名称,为的是与prometheus
保持一致,使得通过服务名称
和IP:端口
两个指标,可以找到指定微服务节点的服务信息和日志,其中服务名称
通过spring.application.name
配置项获取,端口
通过server.port
项获取,如果原服务缺失这俩配置项,要记得规范配置。此外,还添加了通过loki.enable
和loki.url
参数来动态控制
是否启用Loki上传以及根据不同环境可配置不同的Loki地址的功能。由于这部分使用了if标签
,需要添加额外的pom
引用:
xml
<!--logback语法扩展-->
<dependency>
<groupId>org.codehaus.janino</groupId>
<artifactId>janino</artifactId>
<version>3.0.6</version>
</dependency>
logback配置信息(按图示添加):
xml
<!-- loki日志上传 -->
<springProperty name="serverIP" scope="context" source="spring.cloud.client.ip-address" defaultValue="0.0.0.0"/>
<springProperty name="serverPort" scope="context" source="server.port" defaultValue="0000"/>
<springProperty name="appName" scope="context" source="spring.application.name"/>
<springProperty name="lokiEnable" scope="context" source="loki.enable"/>
<springProperty name="lokiUrl" scope="context" source="loki.url"/>
<property name="APP_NAME" value="${appName}"/>
<property name="LOKI_URL" value="${lokiUrl}"/>
<if condition='Boolean.valueOf(property("lokiEnable"))'>
<then>
<appender name="LOKI" class="com.github.loki4j.logback.Loki4jAppender">
<http class="com.github.loki4j.logback.ApacheHttpSender">
<url>${LOKI_URL}/loki/api/v1/push</url>
</http>
<format>
<label>
<pattern>app=${APP_NAME},host=${HOSTNAME},instance=${serverIP}:${serverPort}, level=%level</pattern>
</label>
<message>
<pattern>l=%level h=${HOSTNAME} i=${serverIP}:${serverPort} c=%logger{20} t=%thread | %msg %ex</pattern>
</message>
<sortByTime>true</sortByTime>
</format>
</appender>
</then>
</if>
<!-- 日志输出级别 -->
<root level="INFO">
<appender-ref ref="STDOUT"/>
<appender-ref ref="INFO"/>
<appender-ref ref="ERROR"/>
<if condition='Boolean.valueOf(property("lokiEnable"))'>
<then>
<appender-ref ref="LOKI"/>
</then>
</if>
</root>
- 在
SpringBoot
服务的yml
配置文件里添加如下配置项:
yml
# loki配置
loki:
url: http://192.168.20.84:3100
enable: true
url部分的IP改为你自己的loki服务所在的服务器IP,然后启动服务。正常上传的话是不会打印成功日志的,但是我测试发现,当在自己本机启动SpringBoot服务时,只上传了几行后就会报错上传失败,目前还不知道原因:

所以当你看到以上报错时,尽快将服务打包到linux服务器
上,等待完成后续步骤。
Grafana展示监控信息
前置工作已准备完毕,接下来我们使用Grafana
来配置展示服务信息和日志信息。
访问http://grafana服务器ip:3000/login
,进入登录页,默认用户名密码都是admin
,首次登录会引导进行密码修改。

Grafana接入Prometheus,展示服务信息
- 点击设置处的
Datasource
,添加数据源。




修改IP为你的prometheus
服务器IP,点击最底下的Save & Test
,出现下面提示就成功了。

- 导入模板展示JVM信息。
Grafana
提供允许开发者上传优秀模板,在官网可以挑选模板:
arduino
Grafana 模板 | https://grafana.com/grafana/dashboards/
记录你中意的模板ID
,本文按理使用的模板ID是12856
。在Grafana
内进行导入。


修改你想要的面板名称,数据源选择刚才添加的Prometheus
数据源,点击Import
即可导入。

导入后大致长这样,有很全的JVM监控信息,可以手动调整每个区域位置和大小。

但是还没有服务日志,下面我们引入Loki进行展示。
Grafana接入Loki,打印服务日志
- 添加Loki数据源
添加方式类似上面的,不多做赘述。

这里内置一个Loki数据源了,我们直接修改他。



- Loki日志调试
测试成功后,我们点击Explore
调试一下:

如图为调试面板,选择一个比较久的时间范围,然后查询语句按照示例查询下你注册的服务,点击Run query
能查出来对应日志即为成功。
这里页面有个BUG,这个查询语句框输入字符时经常失焦,建议在外面编辑器编辑好后粘贴进来。

- 以Panel形式添加到监控面板。
返回首页,找到之前添加的JVM监控面板。

先添加一个折叠行。




然后把这一个Row拖到你想要展示日志的地方去。然后再添加一个日志Panel
:

参考下面图片对该Panel进行配置后,点击右上角Apply
应用。

然后把改日志面板调整大小,拖动到刚才添加的折叠行下面,刷新页面就可以整合进去。整体效果如图:

- Loki配置调整
如果你的日志量比较大,可能会触发Loki的数据传输限制
,会上传日志出错以及查不到日志,这个时候需要修改下Loki的配置后重启服务。但是我们部署的时候,loki配置是在容器内部的,路径如下:

这里可以先把配置文件从容器内部复制出来,修改后再复制回去。
(1) docker ps
:查看docker容器id

(2) 复制到服务器
sudo docker cp 容器id:/etc/loki/local-config.yml /home/app-dockers/loki/loki-config.yml
(3) 编辑配置文件

yml
grpc_server_max_recv_msg_size: 104857600
grpc_server_max_send_msg_size: 104857600
(4) 复制回服务器
sudo docker cp /home/app-dockers/loki/loki-config.yml 容器id:/etc/loki/local-config.yml
(5) 重启容器
docker restart 容器id
配置项整合优化
看到效果以后,回顾一下集成过程还有哪些可优化之处。在集成Promethus及Loki时,需要每个微服务都引入一些jar包,可以统一抽到项目上层pom中;对于每个微服务都需要添加的配置,可以在nacos
内创建一个公共配置文件存储,诸如此类优化,都会使你的项目在一次次集成后不至于臃肿。
结语
到这里整套监控中心就算集成完成了,在面对分布式集群部署时,可以通过服务名称
和实例的IP端口
来监控到具体机器节点上的JVM信息和日志。

而这只是Grafana
这个组件的冰山一角,你可以学习Grafana
与Loki
的查询语句语法以及配置方式实现更加炫酷易用的Dashbord
,还可以上传到官方模板供他人使用, 总之扩展上限还是比较高的; 不仅Grafana还有待探索,Prometheus
除了监控服务外,还可配置服务异常告警
; Loki
除了收集SpringBoot日志,还可以收集docker
日志,以及通过多种查询语句快速定位日志。
这些工具功能虽强大,在初步使用过程也发现了一些不足,如Loki编写查询语句失焦很影响体验,日志量较大时查询反应较慢等。同时我们也应当思考,这套组合在带来便利的同时,代价是什么。定时上传日志会不会影响线上服务性能;Loki服务器应当采取怎样的配置,保留多长时间的日志文件为佳,等等。本文所介绍的只是入门级别的简单配置,如果想要上线使用,应该是需要钻研下每个组件更多的配置项,进行更多的配置优化的。
全文较长,图片也比较多,耐心看完的朋友相信会有所收获,同时也欢迎大家分享交流使用过程中发生的问题及解决方案。我本人也是刚接触到这些开源方案,文章中错误及不足之处也请不吝赐教。