Elastic Stack 7.2.0 入门及实战

0.开篇

0.1.环境

本篇所有的案例使用的环境基于以下版本:

  • Linux操作系统:CentOS-7-x86_64
  • jdk:jdk-8u211-linux-x64.tar.gz
  • Elastic Stack:7.2.0

1.认识Elastic Stack

1.1.ELK & ELK Stack & Elastic Stack

在当前互联网时代,每天会有超大数量的日志产生,在很多情况下,我们需要收集这些杂乱无章的日志进行处理并保存起来,最终以可视化的方式展示出来,让它们变成有价值的数据,这个过程也可以称之为数据探索。

现如今比较大的互联网应用可能会部署到成千上万台服务器上运行,我们要想将庞大的日志数据变为有价值的数据,通常面临几个问题:

  • 如此多的服务器日志,如何去实时采集?
  • 各个服务的日志格式复杂且可能互不相同,如何将它们转化成结构化数据?
  • 日志数据过于庞大,应当使用什么去存储?又应当如何去在庞大的数据中进行搜索?
  • 如何将存储好的数据人性化的展示出来?

ELK的出现就是为了解决以上问题。

ELK是由三个开源项目组成:ElasticSearchLogstashKibana

按照 Elastic 官方的说法,ELK的一切都从ElasticSearch开始。ElasticSearch是Shay Banon( Elastic 公司的创始人)开发的,第一个版本叫Compass。后来ElasticSearch的影响不断扩大,催生了LogstashKibana两个开源项目,这三个开源项目组成了ELK,其中Logstash负责日志的采集、过滤,ElasticSearch负责日志数据的存储以及检索,Kibana负责日志数据的图形化展示。

图片

在2015年,一个名为Packbeat的开源项目引起了Elastic公司的重视,它是一个以一种轻量级方式将网络数据发送到ElasticSearch的开源项目。Elastic团队由此引申,开发出了一组专门用于数据传送的轻量级组件,可以将网络、日志、指标、审计等各种数据从不同的数据源头发送到LogstashKibana。Elastic公司给这种组件起了一个统一的名字,这就是Beats组件。

有了Beats组件的加入,使得ELK这个名称不能再概括Elastic的所有开源项目了,于是ELK就自然而然的更名为ELK Stack。但是这个名称本身又是缩写含义,依然容易引起误会,所以最终它们被统称为Elastic Stack

1.2.版本演变

早期,由于Elastic Stack包含的开源项目“各自为政”,每个项目都有一套自己管理版本的颁发,使得用户必须要了解并处理不同版本之间的兼容问题。

2015年ELK 2.0作为一个整体同时发布,解决了版本协调同步与兼容问题。自此以后,用户不管使用哪个版本的Elastic Stack,只要使所有组件的版本统一即可。

1.3.经典架构

Beats组件加入后,日志系统的架构发生了改变。大多数情况下,可以使用Beats中的filebeat对应用服务日志进行采集,而logstash则只负责日志的传输和过滤。

图片

如上图,filebeat采集服务日志,发送给logstash进行处理,然后将处理好的数据存入ElasticSearch中,最终由Kibana进行展示出来。

这个架构并不是完美的架构,logstash如果处理日志的速度没有filebeat采集日志的速度快,那么可能会发生采集卡顿的现象,为了解决这一问题,我们可以在filebeatlogstash中加一层缓冲,这个缓冲可以用Redis来做,但是更推荐使用Kafka消息队列。

图片

有了消息队列的存在,我们就不用担心filebeatlogstash速率不一致的问题了,但是在生产环境,往往还会遇到其它问题导致采集卡顿,并且这个问题大多是因为在logstash处理日志时候处理慢造成的,遇到这种情况,就需要开发者去耐心的调试处理日志的逻辑了。

2.ElasticSearch

2.1.ElasticSearch简介

ElasticSearch是一个分布式、可扩展、近实时的高性能搜索与数据分析引擎。

ElasticSearch提供了搜集、分析、存储数据三大功能,其主要特点有:分布式、零配置、易装易用、自动发现、索引自动分片、索引副本机制、RESTful风格接口、多数据源和自动搜索负载等。

ElasticSearch基于Java编写,其内部使用Lucene做索引与搜索。它将Lucene的复杂性封装了起来,对外暴露了简单易操作的接口。

2.2.Lucene简介

Lucene是一个免费、开源、高性能、纯Java编写的全文检索引擎。

在业务开发场景中,Lucene几乎适用任何需要全文检索的场景。

2005年,Lucene升级成为Apache顶级项目。

Lucene仅仅是一个工具包,主要提供倒排索引的查询结构,以方便软件开发人员在其业务系统中实现全文检索的功能。

Lucene作为一个全文检索引擎工具包,具有如下突出优点:

  • 索引文件格式独立于应用平台:Lucene定义了一套以8位字节为基础的索引文件格式,使得兼容系统或者不同平台的应用能够共享建立的索引文件。
  • 索引速度快:在传统全文检索引擎的倒排索引的基础上,实现了分块索引,能够针对新的文件建立小文件索引,提升索引速度。然后通过与原有索引的合并,达到优化的目的。
  • 简单易学:优秀的面向对象的系统架构,降低了Lucene扩展的学习难度,方便扩充新功能。
  • 跨语言:设计了独立于语言和文件格式的文本分析接口,索引器通过接收Token流完成索引文件的创立,用户扩展新的语言和文件格式,只需实现文本分析的接口即可。
  • 强大的查询引擎:
    • Lucene默认实现了一套强大的查询引擎,用户无需自己编写代码即可通过系统获得强大的查询能力。
    • Lucene默认实现了布尔操作、模糊查询、分组查询等。
    • Lucene的主要模块有Analysis模块、Index模块、Store模块、QueryParser模块、Search模块和Similarity模块,各模块的功能分别汇总如下:
      • Analysis模块:主要负责词法分析及语言处理,也就是我们常说的分词,通过该模块可最终形成存储或者搜索的最小单元Term。
      • Index模块:主要负责索引的创建工作。
      • Store模块:主要负责索引的读和写,主要是对文件的一些操作,其主要目的是抽象出和平台文件系统无关的存储。
      • QueryParser模块:主要负责语法分析,把查询语句生成Lucene底层可以识别的条件。
      • Search模块:主要负责对索引的搜索工作。
      • Similarity模块:主要负责相关性打分和排序实现。

Lucene中,还有一些核心术语,主要涉及Term、词典(Term Dictionary,也叫做字典)、倒排表(Posting List)、正向信息和段(Segment),这些属于的含义汇总如下:

  • Term:索引中最小的存储和查询单元。对于英文语境而言,一般是指一个单词;对于中文词境而言,一般是指一个分词后的词。
  • 词典:是Term的集合。词典的数据结构有很多种,各有优缺点。如可以通过排序数据(通过二分查找来检索数据)、HashMap(哈希表,检索速度更快,属于空间换时间的模式)、FST(FiniteState Transducer,有很好的压缩率)等来实现。
  • 倒排表:一篇文章通常由多个词组成,倒排表记录的是某个词在哪些文章中出现过。
  • 正向信息:原始的文档信息,可以用来做排序、聚合、展示等。
  • 段:索引中最小的独立存储单元。一个索引文件由一个或者多个段组成。在Lucence中,段有不变性,段一旦生成,在段上只能读取、不可写入。

2.3安装ElasticSearch

我们可以使用docker-compose部署ElasticSearch集群 在这之前需要先安装docker环境,参考附录2 安装docker环境。

为了方便操作ElasticSearch我们在安装ElasticSearch集群的同时安装Kibana, 这里只是简单的使用Kibana操作ElasticSearch, 并未对Kibana进行详细讲解

2.3.1.集群规划

node1node2node3
ElasticSearchokokok
Kibanaok

2.3.2.修改开辟虚拟内存

ElasticSearch需要开辟一个65536字节空间以上的虚拟内存, 而Linux默认不存续任何用户和应用开启虚拟内存

修改/etc/sysctl.conf

vim /etc/sysctl.conf
# 在文件中追加一行
vm.max_map_count=262144
# 使用此命令使配置生效
sysctl -p

2.3.3.编写docker-compose.yml

2.3.3.1.node1

图片

version: '3'
services:
  elasticsearch_n0:
    image: elasticsearch:7.2.0
    container_name: elasticsearch_n0
    privileged: true
    environment:
      - cluster.name=elasticsearch-cluster
      - node.name=node0
      - node.master=true
      - search.max_buckets=100000000
      - node.data=true
      - bootstrap.memory_lock=true
      - http.cors.enabled=true
      - http.cors.allow-origin=*
      - cluster.initial_master_nodes=node0
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
      - discovery.zen.ping.unicast.hosts=192.168.133.11,192.168.133.12,192.168.133.13
      - discovery.zen.minimum_master_nodes=2
      - discovery.zen.ping_timeout=120s
      - client.transport.ping_timeout=60s
      - network.publish_host=192.168.133.11
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - /etc/localtime:/etc/localtime
      - ./data:/usr/share/elasticsearch/data
      - ./logs:/usr/share/elasticsearch/logs
    ports:
      - 9200:9200
      - 9300:9300
    restart: always
    networks:
      - es_cluster_net
  kibana:
    image: kibana:7.2.0
    container_name: kibana
    ports:
      - 5601:5601
    volumes:
      - /etc/localtime:/etc/localtime
      - ./kibana/kibana.yml:/usr/share/kibana/config/kibana.yml:rw
    depends_on:
      - elasticsearch_n0
    restart: always
    networks:
      - es_cluster_net
networks:
  es_cluster_net:
    external: true

在docker-compose.yml的同级目录创建一个kibana文件夹, 进入这个文件夹, 创建kibana.yml配置文件:

vim kibana.yml
server.name: kibana
server.host: "0"
elasticsearch.hosts: [ "http://192.168.144.11:9200" ]
xpack.monitoring.ui.container.elasticsearch.enabled: true
i18n.locale: zh-CN

使用普通用户创建好data目录和logs目录

mkdir data
mkdir logs
2.3.3.2.node2

图片

version: '3'
services:
  elasticsearch_n1:
    image: elasticsearch:7.2.0
    container_name: elasticsearch_n1
    privileged: true
    environment:
      - cluster.name=elasticsearch-cluster
      - node.name=node1
      - node.master=true
      - search.max_buckets=100000000
      - node.data=true
      - bootstrap.memory_lock=true
      - http.cors.enabled=true
      - http.cors.allow-origin=*
      - cluster.initial_master_nodes=node0
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
      - discovery.zen.ping.unicast.hosts=192.168.133.11,192.168.133.12,192.168.133.13
      - discovery.zen.minimum_master_nodes=2
      - discovery.zen.ping_timeout=120s
      - client.transport.ping_timeout=60s
      - network.publish_host=192.168.133.12
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - /etc/localtime:/etc/localtime
      - ./data:/usr/share/elasticsearch/data
      - ./logs:/usr/share/elasticsearch/logs
    ports:
      - 9200:9200
      - 9300:9300
    restart: always
    networks:
      - es_cluster_net
networks:
  es_cluster_net:
    external: true

使用普通用户创建好data目录和logs目录

mkdir data
mkdir logs
2.3.3.3.node3

图片

version: '3'
services:
  elasticsearch_n2:
    image: elasticsearch:7.2.0
    container_name: elasticsearch_n2
    privileged: true
    environment:
      - cluster.name=elasticsearch-cluster
      - node.name=node2
      - node.master=true
      - search.max_buckets=100000000
      - node.data=true
      - bootstrap.memory_lock=true
      - http.cors.enabled=true
      - http.cors.allow-origin=*
      - cluster.initial_master_nodes=node0
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
      - discovery.zen.ping.unicast.hosts=192.168.133.11,192.168.133.12,192.168.133.13
      - discovery.zen.minimum_master_nodes=2
      - discovery.zen.ping_timeout=120s
      - client.transport.ping_timeout=60s
      - network.publish_host=192.168.133.13
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - /etc/localtime:/etc/localtime
      - ./data:/usr/share/elasticsearch/data
      - ./logs:/usr/share/elasticsearch/logs
    ports:
      - 9200:9200
      - 9300:9300
    restart: always
    networks:
      - es_cluster_net
networks:
  es_cluster_net:
    external: true

使用普通用户创建好data目录和logs目录

mkdir data
mkdir logs

2.3.4.启动 & 访问

在三个节点使用docker-compose up -d运行即可

然后可以访问: http://{node1}:5601 可以看到Kibana页面

图片

访问: http://{node1}:9200 可以获取ElasticSearch集群信息

图片

接下来我们在Kibana的工作台进行操作

图片

2.4.索引操作

2.4.1.最基本的操作索引

2.4.1.1.添加索引

添加一个名为test的索引

PUT test

{
“acknowledged” : true,
“shards_acknowledged” : true,
“index” : “test”
}

2.4.1.2.获取索引

获取名为test的索引

GET test

{
“test” : {
“aliases” : { },
“mappings” : { },
“settings” : {
“index” : {
“creation_date” : “1608192737117”,
“number_of_shards” : “1”,
“number_of_replicas” : “1”,
“uuid” : “lU6bLKm4RQGZ6Os-i8_Wsw”,
“version” : {
“created” : “7020099”
},
“provided_name” : “test”
}
}
}
}

2.4.1.3.查看索引是否存在

查看test索引是否存在

HEAD test

200 - OK

说明: 返回200说明索引存在, 如果索引不存在会返回404

2.4.1.4.删除索引

删除test索引

DELETE test

{
“acknowledged” : true
}

2.4.2.索引配置

2.4.2.1.创建索引的同时指定分片和副本数
PUT test1
{
  "settings": {
    "index": {
      "number_of_shards": 3,
      "number_of_replicas": 2
    }
  }
}

{
“acknowledged” : true,
“shards_acknowledged” : true,
“index” : “test1”
}

也可以使用下面的方式, 效果一样

PUT test2
{
  "settings": {
    "index.number_of_shards": 3,
    "index.number_of_replicas": 2
  }
}

{
“acknowledged” : true,
“shards_acknowledged” : true,
“index” : “test2”
}

索引配置包括静态配置和动态配置两种, 静态配置只能在索引创建时或索引关闭时设置, 而动态配置没有这个限制

2.4.2.2.索引关闭与打开

索引可以被关闭,关闭后的索引除了维护自身元数据信息以外,基本上不会再占用集群资源,同时也不能再被用户读写。索引关闭后可以再次打开,所以通过关闭索引可以实现索引存档的目的。

关闭

POST /test/_close

{
“acknowledged” : true,
“shards_acknowledged” : true
}

打开

POST /test/_open

{
“acknowledged” : true,
“shards_acknowledged” : true
}

2.4.2.3.索引静态配置

索引静态配置只能在创建索引时设置,一旦索引创建完成就不能再修改静态配置。

参数名默认值说明
number_of_shards5主分片数量,范围1~1024,es.index.max_number_of_shards可设置上限
shard.check_on_startupfalse是否打开分片前校验,校验错误会阻止分片打开。可选值:
false:默认值,在打开分片前不做数据校验;
checksum:使用checksum做数据完整性校验;
true:做checksum完整性校验和数据逻辑校验
codecdefault索引保存时采用的压缩编码算法,可选值含义:
default:默认编码为LZ4,速度快但压缩率不高;
best_compresses:DEFLATE编码,速度慢但压缩率高
routing_partition_size1自定义路由中分区数量,应该大于1且小于number_of_shards,默认值1代表不使用
load_fixed_bitset_filter_eagerlytrue是否加载bitset过滤器,可选值true/false
2.4.2.4.索引动态配置

ElasticSearch为查询和修改索引配置提供_settings接口。

参数名默认值说明
number_of_replicas1每个主分片的副本数量
auto_expand_replicasfalse根据集群节点数量自动扩展副本数量的范围
search.idle.after30s分片被视为空闲的时间
refresh_interval1s索引刷新的时间间隔为-1则代表永不刷新
max_result_window10000检索文档时返回的最大数量
max_inner_result_window100inner和聚焦最大数量
max_rescore_window10000rescore请求中window_size的上限,默认与index.max_result_window which defaults相同
max_docvalue_fields_search100docvalue_fields查询上限
max_script_fields32script_fields查询上限
max_ngram_diff1NGramTokenizer与NGramTokenFilter最大差值
max_shingle_diff3ShingleTokenFilter最大差值
blocks.read_only|索引和索引源数据是否只读,true/false
blocks.read_only_allow_delete|与上一个参数相同,但允许删除以释放资源
blocks.read|是否禁止读,true/false
blocks.write|是否禁止写,true/false
block.metadata|禁止对源数据读写,true/false
max_refresh_listeners|每个分片上刷新监听器的最大数量
analyze.max_token_count10000_analyze接口最大词项数量
highlight.max_analyzed_offset1000000高亮请求中被分析的最大字符数量
max_terms_count65536term查询中可以使用词项的最大值
max_regex_length1000regex查询中正则表达式的最大值
routing.allocation.enableall控制索引分片分配,可选值all(所有分片)、primaries(主分片)、new_primaries(新创建分片)、none(都不分配)
routing.rebanlance.enableall重平衡时规则,可选值同上
gc_deletes60s删除文档版本号的时长
default_pipeline|索引默认ingest节点管道

下面看几个例子:

1.查看所有的动态配置:

GET _settings

2.查看某个索引的动态配置:

flat_settings可以将返回的JSON对象平铺展示。

GET /test1/_settings?flat_settings

{
“test1” : {
“settings” : {
“index.creation_date” : “1608210151608”,
“index.number_of_replicas” : “2”,
“index.number_of_shards” : “3”,
“index.provided_name” : “test1”,
“index.uuid” : “f3EI4o98QaWrisRTGd3ewQ”,
“index.version.created” : “7020099”
}
}
}

3.修改所有的索引的副本数为1:

PUT _settings
{
  "number_of_replicas": 1
}

{
“acknowledged” : true
}

4.可以同时修改多个索引:

PUT /test1,test2/_settings
{
  "blocks.write": false
}

{
“acknowledged” : true
}

3.Logstash

Logstash能够动态的采集、转换和传输数据,不受格式或复杂度的影响。利用Grok从非结构化数据中派生出结构,从 IP 地址解码出地理坐标,匿名化或排除敏感字段,并简化整体处理过程。

Logstash是基于数据事件的,一个数据事件可能有多行数据,从数据源传输到存储库的过程中,Logstash过滤器能够解析各个数据事件,识别已命名的字段、构建对应的数据结构,并将它们转换成通用格式,以便更轻松、更快速地进行分析,实现商业价值。

图片

简单来说,Logstash可以对数据进行传输和处理,它给用户提供了很多的插件来协助完成这些操作。

官方提供的插件分为三类:

这些插件使用起来非常简单,只需要启用插件,就可以在配置文件中使用。

3.1.Logstash初体验

目标:使用Logstash从控制台采集到“hello logstash”字符串,并将采集信息输出到控制台。

说明:只用到了输入、输出,并未用到过滤器。

图片

在生产环境,我们可以直接使用docker容器技术部署Logstash,但在学习过程中,为了体验原汁原味,我们暂时使用原生方式安装。

因为Logstashjava编写的,所以在安装Logstash之前,我们需要先准备好java运行环境:参考附录1 安装jdk8

安装好jdk之后,就可以开始安装Logstash了。

步骤一:下载

https://artifacts.elastic.co/downloads/logstash/logstash-7.2.0.tar.gz

图片

步骤二:上传至Linux系统

将下载好的安装包上传至Linux系统的/home/目录下:

图片

步骤三:解压

tar -zxvf /home/logstash-7.2.0.tar.gz

图片

步骤四:运行

bin/logstash -e "input { stdin { } } output { stdout { } }"

耐心等待启动
图片

这样子就是启动成功了,接下来输入“hello logstash”:

图片

采集日志成功。

3.2.logstash命令

bin/logstash -h

列出参数:

  • -n, --node.name:logstash实例的名称,如果不设置默认为当前的主机名(比如我的主机名为logstash)。
  • -f, --path.config:配置文件,我们可以指定一个特定的文件,也可以指定一个特定的目录,如果指定的是特定的目录,则logstash会读取该目录下的所有文本文件,将其在内存中拼接成完整的大配置文件,再去执行。
  • -e, --config.string:给定的可直接执行的配置内容,也就是说我们可以不指定-f参数,直接把配置文件中的内容作为字符串放到-e参数后面。
  • -w, --pipeline.workers:指定工作线程的个数。
  • -p, --path.plugins:logstash用来加载插件的目录。
  • -l, --path.logs:日志输出文件,如果不设置,logstash将会把日志发送至标准的output。
  • -t, --config.test_and_exit:检查配置的语法是否正确并退出。
  • -r, --config.reload.automatic:监视配置文件的变化,并且自动重新加载修改后的配置文件。
  • --config.reload.interval:为了检查配置文件是否改变,而去拉取配置文件的频率。
  • --http.host:Web API绑定的主机,默认为“127.0.0.1”。
  • --http.port:Web API绑定的端口,默认为9600-9700之间。
  • --log.format:logstash写它自身日志的时候使用json还是文本格式,默认是“plain”。
  • --path.settings:设置包含logstash.yml配置文件的目录,比如log4j日志配置。也可以设置LS_SETTINGS_DIR环境变量。

3.3.Logstash配置文件格式

Logstash的配置有三部分,如下:

input { # 输入
  stdin{ ... }  # 标准输入
}
filter { # 过滤,对数据进行分割、截取等处理
  ...
}
output { # 输出
  stdout { ... } # 标准输出
}

在这里只说一下过滤器,输入和输出留到后面案例中体现。

3.4.Logstash过滤器

Filter是Logstash功能强大的主要原因,它可以对Logstash Event进行丰富的处理,比如说解析数据、删除字段、类型转换等等。

常见的过滤器插件有:

  • date:日志解析
  • grok:正则匹配解析
  • dissect:分隔符解析
  • mutate:对字段做处理,比如重命名、删除、替换等
  • json:按照 json 解析字段内容到指定字段中
  • geoip:增加地理位置数据
  • ruby:利用ruby代码来动态修改Logstash Event
  • csv:该插件用于将逗号分隔的值数据解析为单个字段
  • split:该插件用于将多行消息拆分为不同的事件

3.4.1.所有过滤器通用的配置选项

设置输入类型需要
add_fieldhashNo
add_tagarrayNo
enable_metricboolearnNo
idstringNo
periodic_flushbooleanNo
remove_fieldarrayNo
remove_tagarrayNo

3.4.2.date插件

3.4.2.1.日期过滤器配置选项
设置输入类型需要
localstringNo
matcharrayNo
tag_on_failurearrayNo
targetstringNo
timezonestringNo

按指定的时间格式读取事件的指定字段值后,赋值给指定的字段(默认为@timestamp)。

创建logstash的配置文件:

vim config/logstash.conf
# 在文件中输入以下内容
input {
stdin { }
}
filter {
date {
match => ["logdate", "yyyy-MM-dd HH:mm:ss"]
}
}
output {
stdout { }
}

运行logstash

# -f 指定加载的配置文件
bin/logstash -f config/logstash.conf

耐心等待完全启动成功后,在控制台输入如下内容:

2020-12-17 22:27:30

图片

发现"@timestamp"字段的值就是我们输入的时间

其他的过滤器的使用就不在这里赘述了,用到了直接去官网看,官网写的非常详细

比如date过滤器的官网地址:https://www.elastic.co/guide/en/logstash/current/plugins-filters-date.html

4.Beats

官方目前提供了7种Beats供用户使用,而对于大多数用户来说,常用的有FilebeatMetricbeatHeartbeat,当然其它的也可能会使用到,根据具体的业务需求而定。

图片

4.1.Filebeat

4.1.1.简介

Filebeat是用于转发和集中日志数据的轻量级传送程序。作为服务器上的代理安装,Filebeat监视您指定的日志文件或配置,收集日志时间,并将它们转发到ElasticSearchLogstash进行索引。

Filebeat的工作方式如下:启动Filebeat时,它将启动一个或多个输入,这些输入将在为日志数据指定的位置中查找。对于Filebeat所找到的每个日志,Filebeat都会启动收集器。收集器也可以叫做收割机(Harvester)。每个收割机都读取单个日志以获取新内容,并将新日志数据发送到libbeat,libbeat(libbeat是一个Go库,其中包含所有Beats的通用软件包)将聚焦数据发送到为Filebeat配置的输出。

图片

Filebeat原理请参考官网:https://www.elastic.co/guide/en/beats/filebeat/current/how-filebeat-works.html

4.1.2.Filebeat安装

步骤一:下载

https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-7.2.0-linux-x86_64.tar.gz

图片

步骤二:上传至Linux系统

将下载好的压缩包上传到Linux操作系统的/home/beats/目录下:

图片

步骤三:解压
tar -zxvf filebeat-7.2.0-linux-x86_64.tar.gz

图片

图片

4.1.3.Filebeat运行

4.1.3.1.控制台输入-控制台输出

编写配置文件:

mkdir config
vim config/filebeat-console.yml
# 在配置文件中加入以下配置
# 输入,是一个数组,可以指定多个
filebeat.inputs:
- type: stdin
  # 开启这个输入, 只有是true的时候,这个输入才生效
  enabled: true
# 输出,指定输出到控制台
output.console:
  pretty: true
  enable: true

启动filebeat:

./filebeat -e -c config/filebeat-console.yml

在控制台输入"hello filebeat":
图片

4.1.3.2.文件输入-控制台输出

编写配置文件

mkdir logs/
vim config/filebeat-file-console.yml
# 在文件中输入以下内容
filebeat.inputs:
- type: log
  enabled: true
  paths:
    - ./logs/*.log
output.console:
  pretty: true
  enable: true

启动filebeat

./filebeat -e -c config/filebeat-file-console.yml

新打开一个终端,在logs文件夹下随便创建文件,然后输入内容,观察变化:

图片

图片

其他的输入请参考官网:https://www.elastic.co/guide/en/beats/filebeat/current/configuration-filebeat-options.html#filebeat-input-types

后面会有例子从日志文件读取,输出到kafka。

5.Kibana

前面在安装ElasticSearch的时候已经将Kibana安装好了, 接下来主要看一下如何使用它

Kibana的功能列表:https://www.elastic.co/cn/kibana/features

官方有非常详细的使用方式, 接下来就以几个常用的操作作为示例:

5.1.创建索引模式

要想将ElasticSearch中的索引数据可视化, 就必须给它创建索引模式,索引创建为索引模式后,才可以进行Kibana的可视化工作。

图片

如下图所示,左侧红框中是已经创建好的索引模式,我们可以点击右上角的“创建索引模式”按钮跳转到创建索引模式页面。

图片

创建索引模式分为两步,第一步是选择要创建的索引模式包括的索引,换句话说就是你要对哪些索引创建索引模式,要将哪些索引可视化。

图片

第二部是配置,如下图,直接选择时间字段即可,点击创建索引模式。

图片

如下图,索引模式创建成功,如果想要删除这个索引模式,可以点击右上角的删除按钮。

图片

5.2.发现索引模式

选择菜单栏的“Discover”选择,进入到如下图界面,选择我们刚刚创建好的索引模式,可以看到关于这个索引模式的一些信息。

图片

左侧栏是我们在收集日志过程中定义的字段,在这里可以通过添加和删除的方式指定某个字段是否显示。

图片

索引模式创建成功后,索引中的数据还是会不断的更新,所以我们可以通过时间选项来控制要展示的数据。

图片

可以对字段进行筛选,如下图所示。

图片

修改筛选值为ERROR,展示出来的数据的日志级别就都是ERROR的了

图片

图片

5.3.创建可视化

接下来我们创建一个可视化。

图片

可视化有如下图所示的多种方式创建,我们先来创建一个条形图。

图片

学案则刚刚创建建的索引模式。

图片

配置可视化选项,然后点击保存。

图片

图片

图片

为这个可视化指定一个名字。

图片

5.4.创建仪表盘

接下来创建一个仪表盘。

图片

5.5.将可视化添加到仪表盘

点击添加按钮,添加可视化到仪表盘。

图片

选择刚刚创建的可视化,条形图。

图片

添加后就可以在仪表盘中看到。如果想要保存仪表盘,点击左上角的保存按钮即可。

图片

创建第二个可视化,值得说明的是,我们想要新建第二个可视化,就要先将可视化界面中的元素先清空,如下图所示。

图片

全部删除之后,再次点击可视化菜单。

图片

点击创建新的可视化。

图片

这次我们创建一个饼图。

图片

选择的还是之前创建的索引模式

图片

创建后,填写饼图的配置参数,如下图所示。

图片

图片

配置成功后点击运行,即可展示结果,可以点击左上角的保存按钮进行保存。

图片

给饼图可视化起一个名字。

图片

然后和上面一样的流程, 将饼图添加到仪表盘:

同样的方式, 我们创建一个标签云图

图片

然后将它添加到仪表盘.

图片

最后将仪表盘保存即可

6.日志收集案例

6.1.产生日志的app

首先我们要编写一个不停产生日志的应用程序,为了测试方便,就使用循环随机时间去产生日志。

图片

6.1.1.pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.unionman</groupId>
    <artifactId>filebeats-springboot</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>filebeats-springboot</name>
    <properties>
        <skipTests>true</skipTests>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <springfox-swagger.version>2.8.0</springfox-swagger.version>
        <hibernate-validator.version>6.0.10.Final</hibernate-validator.version>
        <mysql.version>5.1.47</mysql.version>
        <commons-collections4>4.1</commons-collections4>
        <ant.version>1.9.7</ant.version>
        <pinyin4j.version>2.5.0</pinyin4j.version>
        <ouath.version>2.3.3.RELEASE</ouath.version>
        <redis.version>1.4.7.RELEASE</redis.version>
        <httpclient.version>4.5.6</httpclient.version>
        <excel.version>2.6.10</excel.version>
        <maven-surefire.version>2.18.1</maven-surefire.version>
        <jsoup.version>1.9.2</jsoup.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <includeSystemScope>true</includeSystemScope>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${maven-surefire.version}</version>
                <configuration>
                    <skip>${skipTests}</skip>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

6.1.2.application.yml

server:
  port: 20201

6.1.3.logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
    <property name="LOG_HOME" value="logs"/>
    <!-- 彩色日志 -->
    <!-- 彩色日志依赖的渲染类 -->
    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
    <conversionRule conversionWord="wex"
                    converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
    <conversionRule conversionWord="wEx"
                    converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
    <!-- 彩色日志格式 -->
    <property name="CONSOLE_LOG_PATTERN"
              value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
    <!-- Console 输出设置 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    <!-- 按照每天生成日志文件 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!--日志文件输出的文件名-->
            <fileNamePattern>${LOG_HOME}/%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <!--日志文件保留天数-->
            <maxHistory>365</maxHistory>
            <!--日志文件最大的大小-->
            <!--用来指定日志文件的上限大小,那么到了这个值,就会删除旧的日志-->
            <totalSizeCap>1GB</totalSizeCap>
            <maxFileSize>10MB</maxFileSize>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <charset>UTF-8</charset>
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss} %level [%thread] %class => %method 行:%line ==> %msg%n</pattern>
        </encoder>
    </appender>
    <!-- 日志输出级别 -->
    <root level="INFO">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="FILE"/>
    </root>
</configuration>

6.1.4.主启动类

package com.unionman.filebeats;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class FileBeatsApplication {
    public static void main(String[] args) {
        SpringApplication.run(FileBeatsApplication.class, args);
    }
}

6.1.5.controller

package com.unionman.filebeats.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.Random;
import java.util.concurrent.TimeUnit;
@Slf4j
@RestController
public class TestController {
    public static String[] logArr = {
            "有一辆车过来了,它的车牌号码是:粤LAAAAA",
            "程序出现异常",
            "consumerTopicOne(ConsumerRecord<String, String> record) Consumer news topic,1."
    };
    @RequestMapping(value = "/sendlog", method = RequestMethod.GET)
    public String info() {
        Random random = new Random();
        boolean flag = true;
        while (flag) {
            try {
                TimeUnit.SECONDS.sleep(random.nextInt(5) + 1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            int index = random.nextInt(3);
            String logStr = logArr[index];
            switch (index){
                case 0:
                    log.info(logStr);
                    break;
                case 1:
                    log.error(logStr);
                    break;
                case 2:
                    log.warn(logStr);
                    break;
                default:break;
            }
        }
        return "success";
    }
}

6.1.6.启动

将编写好的app使用maven打成jar包,上传到Linux系统node1节点的/home/unionman/elk/jars/目录下:

图片

运行之前,请确保node1节点安装了jdk,如果没有安装,请安装jdk8,参考附录1。

将程序跑起来

nohup java -jar filebeats-springboot-0.0.1-SNAPSHOT.jar &

看一下启动日志,如果没有报错,说明一切正常:

tail -f nohup.out

图片

接下来我们要监控的就是logs文件下的内容。

图片

调用生成日志的接口:http://192.168.133.11:20201/sendlog

图片

6.2.在node1安装kafka

mkdir -p /home/unionman/kafka/
cd /home/unionman/kafka
touch docker-compose.yml
cat >>/home/unionman/kafka/docker-compose.yml<<EOF
version: '3'
services:
  zoo1:
    image: wurstmeister/zookeeper
    restart: always
    hostname: zoo1
    container_name: zookeeper
    networks:
      - base_net
  kafka:
    image: wurstmeister/kafka
    ports:
      - "9092:9092"
    environment:
      KAFKA_ADVERTISED_HOST_NAME: "192.168.133.11"
      KAFKA_ADVERTISED_PORT: "9092"
      KAFKA_ZOOKEEPER_CONNECT: "zoo1:2181"
      KAFKA_BROKER_ID: 1
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
      KAFKA_CREATE_TOPICS: "storage-producer-topic:3:1"
      KAFKA_LOG_CLEANUP_POLICT: delete
      KAFKA_LOG_RETENTION_HOURS: 4464
      KAFKA_LOG_RETENTION_BYTES: 53687091200
      KAFKA_DELETE_TOPIC_ENABLE: "true"
      KAFKA_PARTITION_ASSIGNMENT_STRATEGY: "roundrobin"
    depends_on:
      - zoo1
    container_name: kafka
    networks:
      - base_net
networks:
  base_net:
    external: true
EOF

启动kafka

docker-compose up -d

6.3.配置并启动filebeat

在节点:node1

安装filebeat在前面已经说过了,这里重点列出filebeat的配置:

mkdir -p /home/unionman/elk/beats/filebeat/filebeat-7.2.0-linux-x86_64/config/
touch /home/unionman/elk/beats/filebeat/filebeat-7.2.0-linux-x86_64/config/filebeat-file-kafka.yml
cat >>/home/unionman/elk/beats/filebeat/filebeat-7.2.0-linux-x86_64/config/filebeat-file-kafka.yml<<EOF
filebeat.inputs:
  - type: log
    enabled: true
    fields:
      server_id: spring-app
    paths:
      - /home/unionman/elk/jars/logs/*.log
    multiline:
      pattern: '^(\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2})'
      negate: true
      match: after
setup.template.settings:
  index.number_of_shards: 3
output.kafka:
  hosts: ["192.168.133.11:9092"]
  topic: spring-boot-app-logs-topic
EOF
cd /home/unionman/elk/beats/filebeat/filebeat-7.2.0-linux-x86_64/

启动filebeat

nohup ./filebeat -e -c config/filebeat-file-kafka.yml  -strict.perms=false &

6.4.配置并启动logstash

本次使用unionman用户(普通用户)安装Logstash , 安装过程没有什么区别,安装在/home/unionman/elk/logstash-7.2.0/目录即可。

安装Logstash在前面已经说过了,这里重点列出Logstash的配置:

touch /home/unionman/elk/logstash-7.2.0/config/logstash-kafka-elasticsearch.conf
cat >>/home/unionman/elk/logstash-7.2.0/config/logstash-kafka-elasticsearch.conf<<EOF
input {
kafka {
bootstrap_servers => "192.168.130.211:9092"
topics => ["spring-boot-app-logs-topic"]
codec => "json"
group_id => "logstash-consumer-group"
consumer_threads => 1
decorate_events => true
}
}
filter {
grok {
match => {
"message" => "(?<dateTime>20%{YEAR}-%{MONTHNUM}-%{MONTHDAY} %{HOUR}:?%{MINUTE}(?::?%{SECOND}))\s%{DATA:javaLogLevel}\s%{DATA:threadName}\s%{DATA:className}\s=>\s%{DATA:methodName}\s%{DATA:codeLineNum}\s==>\s%{GREEDYDATA:logMessage}"
}
}
mutate {
rename => {
"[fields][server_id]" => "serverId"
"[host][name]" => "hostname"
"[log][file][path]" => "logFilePath"
}
}
mutate {
remove_field => ["agent","ecs","log","input","fields","host"]
}
date {
match => ["dateTime","YYYY-MM-dd HH:mm:ss"]
target => "dateTime"
}
ruby{
code => "event.set('dateTime',event.get('dateTime').to_i*1000)"
}
}
output {
elasticsearch {
hosts => ["192.168.130.211:9200","192.168.130.212:9200","192.168.130.213:9200"]
index => "boluo-%{+YYYY.MM.dd}"
}
}
EOF
cd /home/unionman/elk/logstash-7.2.0/

启动logstash

nohup ./bin/logstash -f config/logstash-kafka-elasticsearch.conf &

6.5.查看结果

进入Kibana可以看到,生成了一个索引:

图片

我们给这个索引创建索引模式,创建索引模式的方式在5.1章节说过了,这里直接说结果:

图片

图片

附1安装jdk8

准备一台干净的虚拟机,配置好网络,安装基础的软件包如:vim, net-tools等。

图片

将jdk压缩包上传至Linux操作系统的/usr/local/lib64/目录下:

图片

解压缩:

tar -zxvf /usr/local/lib64/jdk-8u211-linux-x64.tar.gz -C /usr/local/src/

图片

配置环境变量:

cat >>/etc/profile<<EOF
# JAVA_HOME
export JAVA_HOME=/usr/local/src/jdk1.8.0_211
export CLASSPATH=.:\$JAVA_HOME/lib:\$JAVA_HOME/lib/tools.jar
export PATH=\$JAVA_HOME/bin:\$PATH
EOF

刷新配置:

source /etc/profile

检验是否配置成功:

java -version

图片

javac -version

图片

附2 安装docker环境

在线安装

Centos7安装docker官方地址 :https://docs.docker.com/install/linux/docker-ce/centos/

安装所需要的的软件包:

yum install -y yum-utils device-mapper-persistent-data lvm2

设置稳定的存储库:

yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

配置启用一些参数:

yum-config-manager --enable docker-ce-nightly
yum-config-manager --enable docker-ce-test
yum-config-manager --disable docker-ce-nightly

安装最新版docker-ce:

yum -y install docker-ce docker-ce-cli containerd.io

启动docker:

systemctl start docker

helloworld:

docker run hello-world

将普通用户加入到docker组:

usermod -a -G docker username

设置docker开机自启动

systemctl enable docker

卸载

yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine

安装docker-compose:

curl \
-L \
"https://github.com/docker/compose/releases/download/1.25.0/docker-compose-$(uname -s)-$(uname -m)" \
-o\
/usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose

附3 docker-compose 部署filebeat

图片

docker-compose.yml

version: '3'
services:
  filebeat:
    image: elastic/filebeat:7.2.0
    container_name: filebeat
    command: --strict.perms=false
    volumes:
      - ./filebeat.yml:/usr/share/filebeat/filebeat.yml
      - /var/log/laravel/:/var/log/laravel/
      - /var/lib/docker/containers:/var/lib/docker/containers:ro
      - /var/run/docker.sock:/var/run/docker.sock
    networks:
      - es_cluster_net
networks:
  es_cluster_net:
    external: true

filebeat.yml

filebeat.inputs:
  - type: log
    enabled: true
    tags: ["platformserver"]
    fields:
      server_id: platformserver
    paths:
      - /home/unionman/apps/boluomonitor-2.1.1/bin/common/logs/platformserver/*.log
    multiline:
      pattern: '^(\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2})'
      negate: true
      match: after
  - type: log
    enabled: true
    tags: ["statistical-analysis"]
    fields:
      server_id: statistical-analysis
    paths:
      - /home/unionman/apps/boluomonitor-2.1.1/bin/common/logs/statistical-analysis/*.log
    multiline:
      pattern: '^(\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2})'
      negate: true
      match: after
  - type: log
    enabled: true
    tags: ["storageserver"]
    fields:
      server_id: storageserver
    paths:
      - /home/unionman/apps/boluomonitor-2.1.1/bin/common/logs/storageserver/*.log
    multiline:
      pattern: '^(\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2})'
      negate: true
      match: after
  - type: log
    enabled: true
    tags: ["structuredserver"]
    fields:
      server_id: structuredserver
    paths:
      - /home/unionman/apps/boluomonitor-2.1.1/bin/common/logs/structuredserver/*.log
    multiline:
      pattern: '^(\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2})'
      negate: true
      match: after
setup.template.settings:
  index.number_of_shards: 3
output.kafka:
  hosts: ["192.168.130.211:9092"]
  topic: spring-boot-app-logs-topic

附4 docker-compose 部署logstash

图片

docker-compose.yml

version: '3'
services:
  logstash:
    image: logstash:7.2.0
    container_name: logstash
    ports:
      - 5044:5044
    volumes:
      - ./logstash.yml:/usr/share/logstash/config/logstash.yml
      - ./pipeline/:/usr/share/logstash/pipeline
    networks:
      - es_cluster_net
networks:
  es_cluster_net:
    external: true

logstash.yml

http.host: "0.0.0.0"

pipeline/logstash.conf

input {
kafka {
bootstrap_servers => "192.168.130.211:9092"
topics => ["spring-boot-app-logs-topic"]
codec => "json"
group_id => "logstash-consumer-group"
consumer_threads => 1
decorate_events => true
}
}
filter {
grok {
match => {
"message" => "(?<dateTime>20%{YEAR}-%{MONTHNUM}-%{MONTHDAY} %{HOUR}:?%{MINUTE}(?::?%{SECOND}))\s%{DATA:javaLogLevel}\s%{DATA:threadName}\s%{DATA:className}\s=>\s%{DATA:methodName}\s%{DATA:codeLineNum}\s==>\s%{GREEDYDATA:logMessage}"
}
}
mutate {
rename => {
"[fields][server_id]" => "serverId"
"[host][name]" => "hostname"
"[log][file][path]" => "logFilePath"
}
}
mutate {
remove_field => ["agent","ecs","log","input","fields","host"]
}
date {
match => ["dateTime","YYYY-MM-dd HH:mm:ss"]
target => "dateTime"
}
ruby{
code => "event.set('dateTime',event.get('dateTime').to_i*1000)"
}
}
output {
elasticsearch {
hosts => ["192.168.130.211:9200","192.168.130.212:9200","192.168.130.213:9200"]
index => "boluo-%{+YYYY.MM.dd}"
}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值