Spring Boot整合EBLK日志分析平台

不知道你有没有注意到,这一节的标题是”EBLK日志分析平台”,而上一节的标题中是”ELK日志分析平台”。是的,你没有看错,这也不是笔误。

在ELK平台中,Logstash负责收集微服务的各种Log文件,发送给ElasticSearch。

当微服务数量少、副本数也不多的时候,Logstash是可以胜任的。随着微服务数量不断增多,副本数不断增长,Logstash的负载会越来越高,极易造成单点故障。

此外,在我们的微服务架构下,各个微服务进程是运行在Kubernetes集群上的,它们的日志文件可能分散在各个物理机上。如何让单一的Logstash收集这些遍布各处的日志文件,也是一个难题。

一个简单的想法就是启用边车模式,即每个微服务启动时,同时伴随部署一个Logstash,这样就可以解决单点故障和收集的问题。

想法是好的,但Logstash本身的结构较为复杂,同时具有监听文件、网络、批处理等各种复杂功能,此外Logstash需要JVM运行环境,内存占用较大。

为了更加轻量级级的收集日志,ElasticSearch推出了Beat,我们以边车模式伴随微服务进行部署。关于Beat与Logstash的对比,可以参考这篇文章

Beat负责收集日志,并将日志发送给Logstash。这样看起来还是没有解决Logstash的单点故障?

是的,但经过Beat转发后,我们实际上可以配置多个Logstah结点从而解决掉单点故障。

此外,Beat可以缓存日志,当Logstash挂掉后,会自动重试。Logstash恢复后,可以继续处理日志的发送。加上B这一层后,整EBLK的架构如下所示:

EBLK架构图

在本小节的前半部分,我们将在一个受限环境中,使用Beat收集日志,并发送给Logstash。后半部分,将讨论如何在Kubernetes中应用便车模式,让Beat伴随微服务一同启动。

使用Beat收集日志

Beats是一系列日志收集工具的统称,官方推出了多种Beat,如:Filebeat, Metricbeat, Packetbeat, Winlogbeat等等,详细可以参见官方介绍

在我们的场景下,需要解析微服务输出的日志文件,直接用Filebeat即可。

首先来看一下FileBeat的配置:

  1. apiVersion: v1
  2. data:
  3. filebeat.yml: |
  4. filebeat.inputs:
  5. - type: log
  6. enabled: true
  7. multiline.pattern: '^2'
  8. multiline.negate: true
  9. multiline.match: after
  10. name: filebeat-test
  11. paths:
  12. - /usr/share/filebeat/*.log
  13. output.logstash:
  14. hosts: ["logstash-0.ls:5555"]
  15. kind: ConfigMap
  16. metadata:
  17. name: filebeat-configmap

上述配置包含2个部分:

  • 输入监听/user/share/filebeat/下后缀为log的文件,这里只是限定环境下的测试,并非线上微服务的日志,支持多行自动合并为同一个事件(主要是异常时调用堆栈信息)。
  • 输出到logstash, logstash-0.ls:5555

然后我们看一下FileBeat的服务定义:

  1. apiVersion: v1
  2. kind: Service
  3. metadata:
  4. name: ls
  5. spec:
  6. ports:
  7. - name: p
  8. port: 5000
  9. selector:
  10. app: filebeat
  11. clusterIP: None
  12. ---
  13. apiVersion: apps/v1
  14. kind: StatefulSet
  15. metadata:
  16. name: filebeat
  17. spec:
  18. selector:
  19. matchLabels:
  20. app: filebeat
  21. serviceName: "ls"
  22. replicas: 1
  23. template:
  24. metadata:
  25. labels:
  26. app: filebeat
  27. spec:
  28. hostname: filebeat
  29. containers:
  30. - name: filebeat-ct
  31. image: docker.elastic.co/beats/filebeat:6.3.2
  32. env:
  33. - name: "ES_JAVA_OPTS"
  34. value: "-Xms384m -Xmx384m"
  35. - name: "XPACK_MONITORING_ENABLED"
  36. value: "false"
  37. - name: "XPACK_MONITORING_ELASTICSEARCH_URL"
  38. value: "http://elasticsearch-0.es:9200"
  39. volumeMounts:
  40. - name: filebeat-configmap
  41. mountPath: /usr/share/filebeat/filebeat.yml
  42. subPath: filebeat.yml
  43. volumes:
  44. - name: filebeat-configmap
  45. configMap:
  46. name: filebeat-configmap

如上所示,配置基本与之前的Logstash相同,并且加载了刚配置好的filebeat.yml。

Logstash汇总日志

对应地,logstash也需要做对应的调整:

  1. apiVersion: v1
  2. data:
  3. logstash.conf: |
  4. input {
  5. beats {
  6. port => 5555
  7. }
  8. }
  9. filter {
  10. grok {
  11. match => {"message" => "(?m)^(?<TIMESTAMP>%{YEAR}-%{MONTHNUM}-%{MONTHDAY} %{TIME}) \[%{LOGLEVEL:LEVEL}\] \[(?<THREAD>.*?)\] \[(?<LOGGER>.*?)\] \[tr=(?<TRACE_ID>.*?)\]\s+(?<MSG>.*)" }
  12. }
  13. }
  14. output {
  15. elasticsearch {
  16. hosts => [ 'elasticsearch-0.es' ]
  17. user => 'elastic'
  18. password => ''
  19. }
  20. }
  21. kind: ConfigMap
  22. metadata:
  23. name: logstash-configmap

如上所示,我们修改了logstash的配置:

  • input是beat格式,端口5555,与上面filebeat的配置对应
  • filter对输入的beat事件进行解析。这里使用了grok插件,具体的语法可以参考官方grok插件介绍
  • output输出到elasticsearch,这里没有变化

我们重启Logstash和FileBeat后,尝试向FileBeat的Docker中写入几行日志,稍等几秒,打开Kibana,可以发现,日志已经可以检索到了。

接入了FileBeat后的Kibana

将FileBeat与Spring Boot进行整合

前面已经提到,微服务数量、副本数众多、遍布在集群的各个物理机上,日志收集、汇总起来非常麻烦,所以一般来说,需要使用边车模式,即一个微服务伴随一个日志收集器(FileBeat)。

上述模式的实现,有两个技术选择:

  • 使用Kubernetes的Pod多容器模式
  • 手动将FileBeat打包进微服务的镜像内。

方案二比较传统,也易于理解,可以参考[这篇文章](https://stackoverflow.com/questions/47811121/dockerfile-springboot-app-with-filebeat)

而方案一,则是利用了Kubernetes的原生支持特性。

Kubernets中的最小操作单位是Pod,Pod中可以启动多个Docker容器,且同他们之间共享同样的磁盘、端口。

关于微服务的服务定义、FileBeat定义,我们前面已经分别介绍过了,所需要做的,就是将他们“粘贴”到同一个Pod里面。

这里,我不再赘述具体描述,而是作为一个思考题留给你来实现。如果实现起来有困难,可以参考[Multi-Container Pods in Kubernetes](https://linchpiner.github.io/k8s-multi-container-pods.html)。