k8s部署elk+filebeat;springCloud集成elk+filebeat+kafka+zipkin实现多个服务日志链路追踪聚合到es

一、目的

如今2023了,大多数javaweb架构都是springboot微服务,一个前端功能请求后台可能是多个不同的服务共同协做完成的。例如用户下单功能,js转发到后台网关gateway服务,然后到鉴权spring-sercurity服务,然后到业务订单服务,然后到支付服务,后续还有发货、客户标签等等服务。
其中每个服务会启动多个实例做负载均衡,这样一来我们想看这个功能的完成流程日志,需要找到对应的服务器ip,日志文件在哪,其中又要确定具体负载转发到哪些台服务器上了。 如果是生产问题想要快速定位原因,需要一套解决方案!
在这里插入图片描述

二、涉及技术栈

  1. 基本架构:spring cloud(springBoot+服务发现+网关+负载熔断等netflex)。本人目前使用的是springboot+eureka+gateway+springSercurity+openfeign+springConfig 配合业务功能涉及中间件redis、quartz、kafka、mysql、elasticsearch
  2. 日志采集处理展现:ELK
    • elasticsearch:海量json数据存储即席查询
    • logstash: 源头采集数据(tcp、file、redis、mq)、格式化处理、推送es存储
    • kibana: 官方es可视化交互curd工具
  3. 高效轻量数据采集工具: filebeat。 监控日志文件实时获取,可以推送到kafka
  4. kafka:接收filebeat数据,供logstash消费
  5. 多服务链路追踪:sleuth-zipkin。无代码侵入。简单来说就是打印的日志内容新增了tranceId、spanId。例如 在这里插入图片描述

三、流程

  1. js发起ajax请求后台网关服务

  2. 网关服务集成了maven<artifactId>spring-cloud-starter-zipkin</artifactId>依赖,会自动给当前的请求header中添加tranceId字段和spanId字段。这两个字段值随机生成。其中tranceId等于spanId在header中没有这两个字段的时候:例如tranceId=123a,spanId=123a 并添加到header中。并且打印日志的时候会把这个信息打印出来

  3. 之后网关根据请求路径转发到业务服务A,A服务的zipkin发现header中有tranceId信息,就只生成spanId,例如tranceId=123a,spanId=231b 并添加到header中。并且打印日志的时候会把这个信息打印出来。

  4. A服务又rpc调用了B服务。B服务的zipkin发现header中有tranceId信息,就只生成spanId,例如tranceId=123a,spanId=342h 并添加到header中。并且打印日志的时候会把这个信息打印出来。

  5. 调用完结返回前端响应。

  6. 到此服务器的日志文件就会新增上述的日志。然后filebeat工具监听到了各个服务的新日志,读取并推送到kafka

  7. 消息队列的topic下生产新数据,logstash工具提前配置并启动消费kafka, 处理并保存数据到elasticsearch。这里好奇为什么不直接通过filebeat直接推送es,或者springboot的log框架直接通过appender直接推送es呢?

    • 使用filebeat解耦,不影响springboot性能。并且轻量
    • 使用kafka是应对大量并发数据,减少logstash压力
    • 最终经过logstash推送es是为了加工格式化源数据,再保存到es,这样更加方便es查询日志
  8. 持久化es之后,通过kibana查询日志,查询条件是tranceId=123a即可查询出完整的日志。

四、整合配置filebeat、kafka、logstash例子

我分了两部分,有些是部署在服务器上的jar,我就通过filebeat采集;有些是部署到本地笔记本上的服务,直接在logback.xml配置一个appender输出到kafka,不经过filebeat。

  1. java的pom引入依赖
<dependency>
    <groupId>net.logstash.logback</groupId>
    <artifactId>logstash-logback-encoder</artifactId>
    <version>6.6</version>
</dependency>
  1. logback-spring.xml文件配置输出格式(部分内容)
        <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"]
      # 适用于日志中每一条日志占据多行的情况,比如各种语言的报错信息调用栈
  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]}'

日志文件内容,我想看这个请求完整的流程
在这里插入图片描述
在这里插入图片描述

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:   #容器运行前需设置的环境变量列表
            - name: discovery.type  #环境变量名称
              value: "single-node" #环境变量的值 这是mysqlroot的密码 因为是纯数字,需要添加双引号 不然编译报错
            - name: xpack.security.enabled  #禁用登录验证
              value: "false" #环境变量的值 这是mysqlroot的密码 因为是纯数字,需要添加双引号 不然编译报错
            - name: ES_JAVA_OPTS
              value: -Xms512m -Xmx512m
          volumeMounts:
            - mountPath: /usr/share/elasticsearch/data   #这是mysql容器内保存数据的默认路径
              name: c-v-path-elasticsearch8-data
            - mountPath: /usr/share/elasticsearch/logs   #这是mysql容器内保存数据的默认路径
              name: c-v-path-elasticsearch8-logs
            - mountPath: /usr/share/elasticsearch/.cache   #这是mysql容器内保存数据的默认路径
              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:   #容器运行前需设置的环境变量列表
            - name: xpack.security.enabled  #禁用登录验证
              value: "false" #环境变量的值 这是mysqlroot的密码 因为是纯数字,需要添加双引号 不然编译报错
            - name: LOG_LEVEL  #禁用登录验证
              value: "info" #环境变量的值 这是mysqlroot的密码 因为是纯数字,需要添加双引号 不然编译报错
            - name: MONITORING_ENABLED  #禁用登录验证
              value: "false" #环境变量的值 这是mysqlroot的密码 因为是纯数字,需要添加双引号 不然编译报错
          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 #和上面保持一致 这是本地的文件路径,上面是容器内部的路径
          hostPath:
            path: /home/app/apps/k8s/for_docker_volume/elk/elasticsearch8/data  #此路径需要实现创建 注意要给此路径授权777权限 不然pod访问不到
        - name: c-v-path-elasticsearch8-logs #和上面保持一致 这是本地的文件路径,上面是容器内部的路径
          hostPath:
            path: /home/app/apps/k8s/for_docker_volume/elk/elasticsearch8/logs  #此路径需要实现创建 注意要给此路径授权777权限 不然pod访问不到
        - name: c-v-path-elasticsearch8-cache #和上面保持一致 这是本地的文件路径,上面是容器内部的路径
          hostPath:
            path: /home/app/apps/k8s/for_docker_volume/elk/elasticsearch8/.cache  #此路径需要实现创建 注意要给此路径授权777权限 不然pod访问不到
        - name: c-v-path-kibana8-data #和上面保持一致 这是本地的文件路径,上面是容器内部的路径
          hostPath:
            path: /home/app/apps/k8s/for_docker_volume/elk/kibana8/data  #此路径需要实现创建 注意要给此路径授权777权限 不然pod访问不到
        - name: c-v-path-kibana8-config #和上面保持一致 这是本地的文件路径,上面是容器内部的路径
          hostPath:
            path: /home/app/apps/k8s/for_docker_volume/elk/kibana8/config  #此路径需要实现创建 注意要给此路径授权777权限 不然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标签上
        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 #我暂时不理解,这个设置 明明没用到?
      name: ssx-kibana8
      protocol: TCP
      targetPort: 5601 # 容器nginx对外开放的端口 上面的dm已经指定了
  selector:
    app: ssx-elk-dmsv
  type: ClusterIP

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ELK(Elasticsearch、Logstash、Kibana)是一个开源的日志管理和分析平台,能够帮助企业收集、存储、搜索、分析和可视化各种类型的日志数据。而Kafka是一个高吞吐量的分布式消息队列系统,可以用来收集和传输大规模的日志数据。Reyslog是一个开源的日志收集器,可以帮助企业从各种不同的数据源中收集日志数据。Filebeat是一个轻量级的日志收集工具,可以帮助企业从各种不同的文件中收集日志数据。 以下是ELK+kafka+reyslog+filebeat企业级部署的步骤: 1. 安装和配置Elasticsearch、Logstash和Kibana,并确保它们能够正常运行。可以使用docker-compose等工具来简化部署过程。 2. 安装和配置Kafka,并创建一个主题(topic)用于存储日志数据。 3. 安装和配置Reyslog,并将其配置为从各种不同的数据源中收集日志数据,并将其发送到Kafka主题(topic)中。 4. 安装和配置Filebeat,并将其配置为从各种不同的文件中收集日志数据,并将其发送到Kafka主题(topic)中。 5. 在Kibana中创建一个索引(index),并定义一个包含所有必需字段的映射(mapping)。然后,使用Logstash来将从Kafka主题(topic)中接收到的日志数据转换为适合索引(index)的格式,并将其存储在Elasticsearch中。 6. 在Kibana中创建一个仪表板(dashboard),并使用其可视化功能来呈现和分析日志数据。可以使用各种不同的可视化插件来创建自定义可视化效果。 7. 部署整个系统,并定期监控其性能和可用性,以确保其正常运行。 总之,ELK+kafka+reyslog+filebeat企业级部署需要进行一系列复杂的配置和设置,需要具备一定的技术知识和实践经验。建议企业可以考虑使用专业的日志管理和分析平台,如Splunk等,以简化部署和管理过程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值