一、目的
如今2023了,大多數(shù)javaweb架構(gòu)都是springboot微服務(wù),一個前端功能請求后臺可能是多個不同的服務(wù)共同協(xié)做完成的。例如用戶下單功能,js轉(zhuǎn)發(fā)到后臺網(wǎng)關(guān)gateway服務(wù),然后到鑒權(quán)spring-sercurity服務(wù),然后到業(yè)務(wù)訂單服務(wù),然后到支付服務(wù),后續(xù)還有發(fā)貨、客戶標(biāo)簽等等服務(wù)。
其中每個服務(wù)會啟動多個實例做負(fù)載均衡,這樣一來我們想看這個功能的完成流程日志,需要找到對應(yīng)的服務(wù)器ip,日志文件在哪,其中又要確定具體負(fù)載轉(zhuǎn)發(fā)到哪些臺服務(wù)器上了。 如果是生產(chǎn)問題想要快速定位原因,需要一套解決方案!
二、涉及技術(shù)棧
- 基本架構(gòu):
spring cloud
(springBoot+服務(wù)發(fā)現(xiàn)+網(wǎng)關(guān)+負(fù)載熔斷等netflex)。本人目前使用的是springboot+eureka+gateway+springSercurity+openfeign+springConfig 配合業(yè)務(wù)功能涉及中間件redis、quartz、kafka、mysql、elasticsearch - 日志采集處理展現(xiàn):ELK
- elasticsearch:海量json數(shù)據(jù)存儲即席查詢
- logstash: 源頭采集數(shù)據(jù)(tcp、file、redis、mq)、格式化處理、推送es存儲
- kibana: 官方es可視化交互curd工具
- 高效輕量數(shù)據(jù)采集工具: filebeat。 監(jiān)控日志文件實時獲取,可以推送到kafka
- kafka:接收filebeat數(shù)據(jù),供logstash消費
- 多服務(wù)鏈路追蹤:sleuth-zipkin。無代碼侵入。簡單來說就是打印的日志內(nèi)容新增了tranceId、spanId。例如
三、流程
-
js發(fā)起ajax請求后臺網(wǎng)關(guān)服務(wù)
-
網(wǎng)關(guān)服務(wù)集成了maven
<artifactId>spring-cloud-starter-zipkin</artifactId>
依賴,會自動給當(dāng)前的請求header中添加tranceId字段和spanId字段。這兩個字段值隨機(jī)生成。其中tranceId等于spanId在header中沒有這兩個字段的時候:例如tranceId=123a,spanId=123a 并添加到header中。并且打印日志的時候會把這個信息打印出來 -
之后網(wǎng)關(guān)根據(jù)請求路徑轉(zhuǎn)發(fā)到業(yè)務(wù)服務(wù)A,A服務(wù)的zipkin發(fā)現(xiàn)header中有tranceId信息,就只生成spanId,例如tranceId=123a,spanId=231b 并添加到header中。并且打印日志的時候會把這個信息打印出來。
-
A服務(wù)又rpc調(diào)用了B服務(wù)。B服務(wù)的zipkin發(fā)現(xiàn)header中有tranceId信息,就只生成spanId,例如tranceId=123a,spanId=342h 并添加到header中。并且打印日志的時候會把這個信息打印出來。
-
調(diào)用完結(jié)返回前端響應(yīng)。
-
到此服務(wù)器的日志文件就會新增上述的日志。然后
filebeat
工具監(jiān)聽到了各個服務(wù)的新日志,讀取并推送到kafka
-
消息隊列的topic下生產(chǎn)新數(shù)據(jù),
logstash
工具提前配置并啟動消費kafka, 處理并保存數(shù)據(jù)到elasticsearch
。這里好奇為什么不直接通過filebeat
直接推送es,或者springboot的log框架直接通過appender直接推送es呢?- 使用filebeat解耦,不影響springboot性能。并且輕量
- 使用kafka是應(yīng)對大量并發(fā)數(shù)據(jù),減少logstash壓力
- 最終經(jīng)過logstash推送es是為了加工格式化源數(shù)據(jù),再保存到es,這樣更加方便es查詢?nèi)罩?/li>
-
持久化es之后,通過
kibana
查詢?nèi)罩荆樵儣l件是tranceId=123a
即可查詢出完整的日志。
四、整合配置filebeat、kafka、logstash例子
我分了兩部分,有些是部署在服務(wù)器上的jar,我就通過filebeat采集;有些是部署到本地筆記本上的服務(wù),直接在logback.xml配置一個appender輸出到kafka,不經(jīng)過filebeat。文章來源:http://www.zghlxwxcb.cn/news/detail-563735.html
- java的pom引入依賴
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>6.6</version>
</dependency>
- logback-spring.xml文件配置輸出格式(部分內(nèi)容)
<appender name="fileUserLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 配置我們自己寫的包的日志信息,目的是為了方便查看自己的類日志,此日志文件只有我們自己的的log -->
<File>${logdir}/user.${appname}.${serverport}.${KPHOSTNAME}.log</File>
<!--滾動策略,按照時間滾動 TimeBasedRollingPolicy-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--文件路徑,定義了日志的切分方式——把每一天的日志歸檔到一個文件中,以防止日志填滿整個磁盤空間-->
<FileNamePattern>${logdir}/history/user.${appname}.${serverport}.%d{yyyy-MM-dd}.${KPHOSTNAME}.log</FileNamePattern>
<!--只保留最近90天的日志-->
<maxHistory>90</maxHistory>
<!--用來指定日志文件的上限大小,那么到了這個值,就會刪除舊的日志-->
<totalSizeCap>1GB</totalSizeCap>
</rollingPolicy>
<!--日志輸出編碼格式化-->
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<pattern>
<pattern>
{
"dateTime": "%d{yyyy-MM-dd HH:mm:ss.SSS}",
"message": "%message",
"stackTrace": "%exception",
"level": "%level",
"traceId": "%X{X-B3-TraceId:-}",
"spanId": "%X{X-B3-SpanId:-}",
"service": "${appname}",
"thread": "%thread",
"class": "%logger.%method[%line]"
}
</pattern>
</pattern>
<timestamp>
<timeZone>GMT+8</timeZone>
</timestamp>
</providers>
</encoder>
</appender>
logstash.conf
input {
kafka{
bootstrap_servers => "ssx-kafka-dmsv.ssx:9092"
client_id => "logstash_kafka_consumer_id"
group_id => "logstash_kafka_consumer_group"
auto_offset_reset => "latest"
consumer_threads => 1
decorate_events => true
topics => ["logstash"]
}
}
filter {
json {
source => "message"
}
}
output{
elasticsearch{
hosts => ["ssx-elk-dmsv.ssx:9200"]
index => "logstash-%{+YYYY.MM.dd}"
}
}
kibana
$ cat kibana.yml
server.host: "0.0.0.0"
server.shutdownTimeout: "5s"
elasticsearch.hosts: [ "http://localhost:9200" ]
monitoring.ui.container.elasticsearch.enabled: true
i18n.locale: "zh-CN"
$ cat node.options
--unhandled-rejections=warn
--dns-result-order=ipv4first
## enable OpenSSL 3 legacy provider
#--openssl-legacy-provider
filebeat.yml
filebeat.modules:
filebeat.inputs:
- type: log
enabled: true
paths:
- /spring-boot-logs/*/user.*.log
#include_lines: ["^ERR", "^WARN"]
# 適用于日志中每一條日志占據(jù)多行的情況,比如各種語言的報錯信息調(diào)用棧
multiline:
pattern: '^[[:space:]]'
negate: false
match: after
processors:
- drop_fields:
fields: ["metadata", "prospector", "offset", "beat", "source","type"]
output.kafka:
hosts: ["ssx-kafka-dmsv.ssx:9092"]
topic: logstash
codec.format:
string: '%{[message]}'
日志文件內(nèi)容,我想看這個請求完整的流程文章來源地址http://www.zghlxwxcb.cn/news/detail-563735.html
k8s的yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: ssx-elk-dmsv
namespace: ssx
spec:
replicas: 1
selector:
matchLabels:
app: ssx-elk-dmsv
template:
metadata:
labels:
app: ssx-elk-dmsv
spec:
hostAliases:
- ip: "192.168.0.101"
hostnames:
- "node101"
- ip: "192.168.0.102"
hostnames:
- "node102"
- ip: "192.168.0.103"
hostnames:
- "node103"
- ip: "127.0.0.1"
hostnames:
- "elasticsearch"
containers:
- name: ssx-elasticsearch8-c
image: docker.elastic.co/elasticsearch/elasticsearch:8.10.2
imagePullPolicy: IfNotPresent
ports:
- containerPort: 9200
env: #容器運行前需設(shè)置的環(huán)境變量列表
- name: discovery.type #環(huán)境變量名稱
value: "single-node" #環(huán)境變量的值 這是mysqlroot的密碼 因為是純數(shù)字,需要添加雙引號 不然編譯報錯
- name: xpack.security.enabled #禁用登錄驗證
value: "false" #環(huán)境變量的值 這是mysqlroot的密碼 因為是純數(shù)字,需要添加雙引號 不然編譯報錯
- name: ES_JAVA_OPTS
value: -Xms512m -Xmx512m
volumeMounts:
- mountPath: /usr/share/elasticsearch/data #這是mysql容器內(nèi)保存數(shù)據(jù)的默認(rèn)路徑
name: c-v-path-elasticsearch8-data
- mountPath: /usr/share/elasticsearch/logs #這是mysql容器內(nèi)保存數(shù)據(jù)的默認(rèn)路徑
name: c-v-path-elasticsearch8-logs
- mountPath: /usr/share/elasticsearch/.cache #這是mysql容器內(nèi)保存數(shù)據(jù)的默認(rèn)路徑
name: c-v-path-elasticsearch8-cache
- mountPath: /etc/localtime #時間同步
name: c-v-path-lt
- name: ssx-kibana-c
image: docker.elastic.co/kibana/kibana:8.10.2
imagePullPolicy: IfNotPresent
ports:
- containerPort: 5601 # 開啟本容器的80端口可訪問
volumeMounts:
- mountPath: /usr/share/kibana/data #無用,我先看看那些掛載需要
name: c-v-path-kibana8-data
- mountPath: /usr/share/kibana/config
name: c-v-path-kibana8-config
- mountPath: /etc/localtime #時間同步
name: c-v-path-lt
- name: ssx-logstash-c
image: docker.elastic.co/logstash/logstash:8.10.2
imagePullPolicy: IfNotPresent
env: #容器運行前需設(shè)置的環(huán)境變量列表
- name: xpack.security.enabled #禁用登錄驗證
value: "false" #環(huán)境變量的值 這是mysqlroot的密碼 因為是純數(shù)字,需要添加雙引號 不然編譯報錯
- name: LOG_LEVEL #禁用登錄驗證
value: "info" #環(huán)境變量的值 這是mysqlroot的密碼 因為是純數(shù)字,需要添加雙引號 不然編譯報錯
- name: MONITORING_ENABLED #禁用登錄驗證
value: "false" #環(huán)境變量的值 這是mysqlroot的密碼 因為是純數(shù)字,需要添加雙引號 不然編譯報錯
args: ["-f","/myconf/logstash.conf"]
volumeMounts:
- mountPath: /myconf #配置
name: c-v-path-logstash8-conf
- mountPath: /usr/share/logstash/data #data
name: c-v-path-logstash8-data
- mountPath: /etc/localtime #時間同步
name: c-v-path-lt
- name: ssx-filebeat-c
image: docker.elastic.co/beats/filebeat:8.10.2
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /usr/share/filebeat/filebeat.yml #配置
name: c-v-path-filebeat8-conf
- mountPath: /usr/share/filebeat/data #配置
name: c-v-path-filebeat8-data
- mountPath: /spring-boot-logs #data
name: c-v-path-filebeat8-spring-logs
- mountPath: /etc/localtime #時間同步
name: c-v-path-lt
volumes:
- name: c-v-path-elasticsearch8-data #和上面保持一致 這是本地的文件路徑,上面是容器內(nèi)部的路徑
hostPath:
path: /home/app/apps/k8s/for_docker_volume/elk/elasticsearch8/data #此路徑需要實現(xiàn)創(chuàng)建 注意要給此路徑授權(quán)777權(quán)限 不然pod訪問不到
- name: c-v-path-elasticsearch8-logs #和上面保持一致 這是本地的文件路徑,上面是容器內(nèi)部的路徑
hostPath:
path: /home/app/apps/k8s/for_docker_volume/elk/elasticsearch8/logs #此路徑需要實現(xiàn)創(chuàng)建 注意要給此路徑授權(quán)777權(quán)限 不然pod訪問不到
- name: c-v-path-elasticsearch8-cache #和上面保持一致 這是本地的文件路徑,上面是容器內(nèi)部的路徑
hostPath:
path: /home/app/apps/k8s/for_docker_volume/elk/elasticsearch8/.cache #此路徑需要實現(xiàn)創(chuàng)建 注意要給此路徑授權(quán)777權(quán)限 不然pod訪問不到
- name: c-v-path-kibana8-data #和上面保持一致 這是本地的文件路徑,上面是容器內(nèi)部的路徑
hostPath:
path: /home/app/apps/k8s/for_docker_volume/elk/kibana8/data #此路徑需要實現(xiàn)創(chuàng)建 注意要給此路徑授權(quán)777權(quán)限 不然pod訪問不到
- name: c-v-path-kibana8-config #和上面保持一致 這是本地的文件路徑,上面是容器內(nèi)部的路徑
hostPath:
path: /home/app/apps/k8s/for_docker_volume/elk/kibana8/config #此路徑需要實現(xiàn)創(chuàng)建 注意要給此路徑授權(quán)777權(quán)限 不然pod訪問不到
- name: c-v-path-logstash8-conf
hostPath:
path: /home/app/apps/k8s/for_docker_volume/elk/logstash8/myconf
- name: c-v-path-logstash8-data
hostPath:
path: /home/app/apps/k8s/for_docker_volume/elk/logstash8/data
- name: c-v-path-lt
hostPath:
path: /etc/localtime #時間同步
- name: c-v-path-filebeat8-conf
hostPath:
path: /home/app/apps/k8s/for_docker_volume/elk/filebeat8/myconf/filebeat.yml
- name: c-v-path-filebeat8-data
hostPath:
path: /home/app/apps/k8s/for_docker_volume/elk/filebeat8/data
- name: c-v-path-filebeat8-spring-logs
hostPath:
path: /home/ssx/appdata/ssx-log/docker-log
nodeSelector: #把此pod部署到指定的node標(biāo)簽上
kubernetes.io/hostname: node101
---
apiVersion: v1
kind: Service
metadata:
name: ssx-elk-dmsv
namespace: ssx
spec:
ports:
- port: 9200
name: ssx-elk8-9200
protocol: TCP
targetPort: 9200
- port: 5601 #我暫時不理解,這個設(shè)置 明明沒用到?
name: ssx-kibana8
protocol: TCP
targetPort: 5601 # 容器nginx對外開放的端口 上面的dm已經(jīng)指定了
selector:
app: ssx-elk-dmsv
type: ClusterIP
到了這里,關(guān)于k8s部署elk+filebeat;springCloud集成elk+filebeat+kafka+zipkin實現(xiàn)多個服務(wù)日志鏈路追蹤聚合到es的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!