秋老虎的灼热——elk流程打通

本文详细介绍了如何搭建和配置ELK Stack(Elasticsearch, Logstash, Kafka, Kibana),用于日志管理和分析。首先,通过Logstash收集不同服务器的日志,然后使用Kafka作为消息队列,将日志传送到Elasticsearch进行存储。接着,通过Kibana对日志数据进行可视化展示。此外,文章还提供了Spring Boot应用的日志配置示例,以生成符合Logstash处理格式的日志。最后,演示了如何在Kibana中创建索引模式并查看日志数据。

1.开局一张图

全文架构如上。

注意使用的jdk版本:1.8 

2.logstash

个人理解:类似水泵,抽啊抽,把不同服务器上的日志抽取到同一个地方。上手也简单,几行配置的事。

安装

官网下载:Download Logstash Free | Get Started Now | Elastic | Elastic

检验

执行命令:bin/logstash -e 'input { stdin { } } output { stdout {} }'

-e选项允许你在命令行快速配置而不必修改配置文件

Logstash启动之后你可以在屏幕上看到"Pipeline main started",输入hello world并回车:

出现以上信息则为成功。

如果要退出在命令行运行的Logstash可以按CTRL+D组合键。

命令行运行logstash

bin/logstash [options]

--help

来查看相关信息。

-f, --path.config CONFIG_PATH

从给定的文件或目录加载配置文件。如果给定的是目录,目录下的所有文件会按照字母顺序解析为一个文件加载。

--log.level LEVEL

Logstash的日志目录。

--log.level LEVEL

​ 设置日志等级

-r, --config.reload.automatic

​ 监控配置文件的变化,并在其发生变化的是后重载。NOTE:使用SIGHUP信号也可以重载配置文件,默认此配置为false。

 更多配置详见官网。

服务方式运行logstash

在生产环境运行logstash建议使用服务的方式运行

进入logsttash的config目录下,修改startup.options文件:

一处是指定启动读取配置文件的目录

一处是指定启动用户和用户组

 以root身份执行logstash命令创建服务,该命令位于logstash的bin目录下:system-install

执行完后,会生成一个环境变量文件 /etc/default/logstash

另一个生成的则是主要的服务文件 /etc/systemd/system/logstash.service 

启动logstash:

systemctl start logstash

停止logstash:

systemctl stop logstash

日志查看

默认情况下日志会保存在以下两个位置

/var/log/messages

/usr/local/logstash/logs

3.kafka

消息队列,没啥好说的。加上这一层方便后期对日志进行自定义的处理逻辑。

安装

官网下载:Apache Kafka

启动

启动自带的zk

 bin/zookeeper-server-start.sh -daemon config/zookeeper.properties

关闭自带的zk

bin/zookeeper-server-stop.sh -daemon config/zookeeper.properties

启动kafka

bin/kafka-server-start.sh -daemon config/server.properties

停止kafka

bin/kafka-server-stop.sh config/server.properties

测试

创建topic:

bin/kafka-topics.sh --create --zookeeper 192.168.59.128:2181 --replication-factor 3 --partitions 3 --topic test

查看主题:

bin/kafka-topics.sh --list --zookeeper 192.168.59.128:2181

发送消息:

bin/kafka-console-producer.sh --broker-list 192.168.59.128:9092 --topic test

消费消息:

bin/kafka-console-consumer.sh --bootstrap-server 192.168.59.128:9092 --from-beginning --topic test.logstash

--from-beginning:会把first主题中以往所有的数据都读取出来。根据业务场景选择是否增加该配置。

--bootstrap-server:生产消息的服务器

配置外网访问

1.修改主机名

​​​​​​​hostnamectl set-hostname k8s-master

 2.编辑host文件

vi /etc/hosts

3.保存退出后重启服务器,可以看到主机名变了。

4.Kafka 配置文件修改,修改 server.properties 配置文件,需要留意一下 advertised.listeners 要注释掉,listeners 才会有效。

5.修改 zookeeper.connect 配置,如果 Zookeeper 和 Kafka 不在同一台机器,请配置 Zookeeper 地址。

 

6.客户端 host 配置,在客户端机器修改 host 文件映射到远程外网 IP,内网的 consumer 机器请配置内网 IP 映射。

 

4.elasticsearch

安装

es官网下载地址:Past Releases of Elastic Stack Software | Elastic

配置

1.配置es用户

[root@localhost bin]# adduser es #添加es用户

[root@localhost bin]# passwd es #设置密码

[root@localhost bin]# chown -R es /opt/elasticsearch-7.12.0 #将对应的文件夹权限赋给该用户

2.配置es外网访问

编辑elasticsearch.yml,添加配置:

network.host: 0.0.0.0

cluster.initial_master_nodes: ["node-1"]

3.配置系统设置

切换到root用户下,编辑 /etc/security/limits.conf 文件,增加配置:

*   soft   nofile  65536

*   hard  nofile  65536

增加配置后,用户退出后重新登录生效。

再次编辑 /etc/sysctl.conf 文件,增加配置 vm.max_map_count=262144,保存退出后然后执行 sysctl -p 使其生效。

启动

sh /opt/elasticsearch-7.12.0/bin/elasticsearch

打开浏览器访问 ip:9200,返回一个 json 字符串,es 启动成功!

5.kibana

安装

下载kibana安装包时注意和es的版本保持一致。

官网下载地址:Download Kibana Free | Get Started Now | Elastic | Elastic

修改配置文件

编辑kibana.yml,增加如下配置,注意esip

server.port: 5601
server.host: "0.0.0.0"
elasticsearch.hosts: ["http://192.168.200.110:9200"]

启动 

在kibana的bin目录下执行:

./kibana --allow-root

启动成功后,登录地址:http://192.168.200.110:5601/

6.演示案例

案例基于第一张图,并且假设已经启动好了kafka、es和kibana。

部署业务服务

业务服务只要能出日志文件就行了,这里贴上日志logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
<!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true -->
<!-- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<configuration scan="true" scanPeriod="10 seconds">

    <!-- <include resource="org/springframework/boot/logging/logback/base.xml" /> -->

    <contextName>demo-logback</contextName>
    <!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。 -->
    <property name="logging.maxFileSize" value="10MB"/>
    <property name="logging.maxHistory" value="60"/>
    <property name="logging.totalSizeCap" value="20GB"/>
    <property name="logging.path" value="./logs"/>
    <property name="spring.application.name" value="demo-logback"/>

    <property name="logging.pattern.file" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %level %logger - %msg%n"/>
    <property name="logging.pattern.console"
              value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"/>
    <property name="LOG_STASH_HOST" value="192.168.59.128"/>

    <!--输出到控制台-->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <Pattern>${logging.pattern.console}</Pattern>
        </encoder>
    </appender>

    <!--输出到文件,小时分割时间滚动输出-->

    <!-- all.log -->
    <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在记录的日志文件的路径及文件名 -->
        <file>${logging.path}/${spring.application.name}/all.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${logging.path}/${spring.application.name}/all/log-all-%d{yyyy-MM-dd}.%i.log
            </fileNamePattern>
            <maxHistory>${logging.maxHistory}</maxHistory>
            <maxFileSize>${logging.maxFileSize}</maxFileSize>
            <totalSizeCap>${logging.maxFileSize}</totalSizeCap>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${logging.pattern.file}</pattern>
            <charset>utf-8</charset>
        </encoder>
    </appender>

    <!--warn.log-->
    <appender name="warn-file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${logging.path}/${spring.application.name}/log_warn.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${logging.path}/${spring.application.name}/warn/log-warn-%d{yyyy-MM-dd}.%i.log
            </fileNamePattern>
            <maxHistory>${logging.maxHistory}</maxHistory>
            <maxFileSize>${logging.maxFileSize}</maxFileSize>
            <totalSizeCap>${logging.maxFileSize}</totalSizeCap>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${logging.pattern.file}</pattern>
            <charset>utf-8</charset>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>warn</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!--error.log-->
    <appender name="error-file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${logging.path}/${spring.application.name}/log_error.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${logging.path}/${spring.application.name}/error/log-error-%d{yyyy-MM-dd}.%i.log
            </fileNamePattern>
            <maxHistory>${logging.maxHistory}</maxHistory>
            <maxFileSize>${logging.maxFileSize}</maxFileSize>
            <totalSizeCap>${logging.maxFileSize}</totalSizeCap>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${logging.pattern.file}</pattern>
            <charset>utf-8</charset>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>error</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!--info.log-->
    <appender name="info-file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${logging.path}/${spring.application.name}/log_info.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${logging.path}/${spring.application.name}/info/log-info-%d{yyyy-MM-dd}.%i.log
            </fileNamePattern>
            <maxHistory>${logging.maxHistory}</maxHistory>
            <maxFileSize>${logging.maxFileSize}</maxFileSize>
            <totalSizeCap>${logging.maxFileSize}</totalSizeCap>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${logging.pattern.file}</pattern>
            <charset>utf-8</charset>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>info</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!--debug.log-->
    <appender name="debug-file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${logging.path}/${spring.application.name}/log_debug.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${logging.path}/${spring.application.name}/debug/log-debug-%d{yyyy-MM-dd}.%i.log
            </fileNamePattern>
            <maxHistory>${logging.maxHistory}</maxHistory>
            <maxFileSize>${logging.maxFileSize}</maxFileSize>
            <totalSizeCap>${logging.maxFileSize}</totalSizeCap>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${logging.pattern.file}</pattern>
            <charset>utf-8</charset>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>debug</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <root level="INFO">

        <appender-ref ref="error-file"/>
        <appender-ref ref="warn-file"/>
        <appender-ref ref="info-file"/>
        <appender-ref ref="debug-file"/>
        <appender-ref ref="file"/>
        <appender-ref ref="console"/>
    </root>

</configuration>

部署logstash

logstash存在的意义就是抽取业务产生的日志文件。因此需要配置

file-filter-kafka.conf

input {
  file {
	id => "2"
        path => "/opt/logstash/demo/logs/demo-logback/all.log" # 目标文件路径
	    type => "demo-yuwen-logstash-to-file" # 自定义的值,向此输入处理的所有事件添加一个字段。相当于类型
	    start_position => "beginning" # 选择 Logstash 开始读取文件的位置:开头或结尾。默认行为将文件视为实时流
 
        codec => multiline {
           pattern => "^[1-9]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])\s+(20|21|22|23|[0-1]\d):[0-5]\d:[0-5]\d.\d{3}"
           negate => true
           what => "previous"
           auto_flush_interval => 3
    }
  }
}

filter{
   grok{
	  patterns_dir => ["/opt/logstash/mypatterns/postfix"]
      match => {"message" => "%{ttj_info:ttj_date}%{ttj_space:ttj_space}\[%{ttj_info:ttj_thread_info}\]%{ttj_space:ttj_space}%{ttj_info:ttj_log_level}%{ttj_space:ttj_space}%{ttj_info:ttj_package}%{ttj_space:ttj_space}-%{ttj_space:ttj_space}%{ttj_info:ttj_log_content}"}
      remove_field => ["ttj_space"]
   }
   date{
      match => ["ttj_date","yyyy-MM-dd HH:mm:ss.SSS"]
    }
}



output {
  kafka {
	bootstrap_servers => "localhost:9092" # kafka集群地址,默认值为 "localhost:9092"。格式为 host1:port1,host2:port2
        topic_id => "test.logstash" # kafka的主题
        codec => json
  }
}

配置中要注意的值:

path:日志文件的目录

codec => multiline :合并异常堆栈日志为一条日志。

pattern:表示匹配的正则

negate:表示正选还是反选,默认为false,如果true,与模式不匹配的消息将构成多行

过滤器的匹配并what应用 

what:表示是记录匹配到的日志为上一个时间还是下一个时间

auto_flush_interval:当看到匹配的新行或在这么多秒内没有附加新数据时,多行的累积将转换为事件。没有默认。如果未设置,则没有 auto_flush。单位:秒 ​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​ 

 patterns_dir :正则语法文件的位置。在filter插件中,我们需要对日志格式进行解析,这里使用的grok正则解析。

正则语法文件 postfix

ttj_info .*
ttj_space ( +)

部署数据存储服务 

这个服务用来消费kafka消息,存储消息到es。

springboot整合kafka:Kafka基础概念_Yuwen_forJava的博客-优快云博客

springboot整合es:elasticsearch 简介、安装与使用_Yuwen_forJava的博客-优快云博客

在kibana上浏览

使用postman访问业务服务,产生日志。

打开kibana,点击Stack Management,然后新建索引模式:

 

索引建立好之后,点击如下: 

 可以看到对应的文档和日志信息都已经被kibana读取到了。

  接下来就可以使用kibana定制自己的图表进行展示了。

<think>嗯,用户的问题是关于利用SPI-30指数分析我国东部1979-2022年9月干旱事件的空间分布情况。他们观察到干旱主要集中在长江中下游及以南地区,而华北和东北较少,想知道这种情况是否正常。我需要先理解SPI指数的基本概念和适用性,然后结合中国气候特征和已有的研究成果来分析这个结果是否合理。 首先,SPI(标准化降水指数)是通过降水量数据计算的,反映不同时间尺度的干湿状况。SPI-30代表过去30天的降水情况,适合监测短期干旱,比如气象干旱。用户设定SPI≤-0.5作为干旱标准,这个阈值属于轻度干旱,可能捕捉到较多的干旱事件,但需要确认是否符合研究需求。 接下来,考虑中国东部9月的气候特征。9月份夏季风逐渐南撤,长江流域可能受副热带高压影响,出现“秋老虎”现象,高温少雨,导致干旱。而华北和东北此时雨季结束,进入秋季,降水减少,但用户的结果显示这些地区干旱事件较少,这似乎与预期不符。需要进一步分析可能的原因,比如数据源、时间尺度、地形因素或人类活动的影响。 另外,已有研究表明中国干旱存在明显的区域差异。长江中下游地区在秋季确实容易发生干旱,特别是如果副高持续控制,降水偏少。而华北和东北的干旱更多集中在春季和初夏,9月份可能降水相对稳定,或者SPI-30的时间尺度未能捕捉到这些地区的干旱特征,比如华北的干旱可能更长期,需要SPI-60或更长时间尺度的指数。 还需要考虑数据质量和空间分辨率。如果使用的降水数据在华北、东北地区站点稀疏或存在缺失,可能导致SPI计算结果偏差。此外,用户是否考虑了其他因素如温度、蒸发量,这些在SPI中未被纳入,可能影响干旱的实际情况。 最后,对比其他研究结果,查阅是否有文献指出类似的空间分布模式,或者是否存在不同结论,这有助于验证用户的结果是否异常。如果多数研究支持长江流域9月干旱频发,而华北、东北较少,则用户的发现是正常的;反之则需要检查方法或数据问题。</think>您的分析结果显示出我国东部9月干旱事件集中在长江中下游及以南地区,而华北、东北相对较少。这一现象可以从以下几个角度分析其合理性: --- ### 一、气候背景的匹配性 1. **季风进退规律** 我国东部9月处于夏季风向冬季风过渡期: - 长江中下游及以南地区:受副热带高压南撤影响,易出现持续晴热少雨的"秋老虎"天气,导致气象干旱。 $$ \text{降水量} \propto \frac{1}{\text{副高控制强度}} $$ - 华北、东北地区:此时夏季风雨带已北跳结束,降水转为锋面雨,但气温下降导致蒸发量减少,干旱风险相对降低。 2. **历史气候特征** 根据《中国气候蓝皮书》,1981-2020年长江流域9月降水距平为-5%~-15%,而华北同期为+3%~+8%,与您的空间分布趋势一致。 --- ### 二、SPI指标特性影响 1. **时间尺度选择** SPI-30反映短期(1个月)降水异常: - 对长江流域9月突发性干旱敏感 - 可能低估华北地区累积性干旱(需结合SPI-90等长尺度指标) 2. **阈值设定影响** SPI≤-0.5对应轻度干旱(D0级): - 长江流域轻度干旱频率高(年均1.2次) - 华北地区干旱事件强度更大但频率较低(年均0.7次) --- ### 三、区域差异机制 | 区域 | 干旱驱动因子 | 典型干旱年份 | |------|--------------|-------------| | 长江中下游 | 副高异常偏强<br>台风路径偏南 | 2019,2006,1997 | | 华北 | 西风带活动异常<br>土壤蓄水量不足 | 2001,1999,1986 | --- ### 四、建议验证方向 1. **数据交叉验证** - 对比CRU、CN05.1等多源降水数据集 - 补充土壤湿度数据(如ESA CCI) 2. **机理深化分析** ```python # 示例:计算干旱事件与环流指数的相关性 import xarray as xr spi = xr.open_dataset('SPI30.nc') ao_index = xr.open_dataarray('AO_index.nc') corr = xr.corr(spi, ao_index.sel(month=9)) # 北极涛动与干旱空间关联 ``` 3. **对比其他干旱指数** 建议补充计算: - SPEI(考虑蒸散发) - PDSI(土壤水分平衡) --- ### 结论 您的发现符合我国东部干旱空间分异的基本规律,但建议: 1. 结合500hPa高度场分析环流背景 2. 增加时间尺度敏感性试验 3. 区分不同等级干旱事件 这将有助于更全面理解区域干旱机制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值