ELK Stack企业级日志平台搭建从0到1

ELK需求背景

(1)开发人员经常需要去服务器后端查看日志,为了排除问题。而对于开发人员后来说,不可能拥有服务器的绝对权限。而且服务器数量很可能激增。

(2)服务器数量激增,项目数量激增,日志类型众多。

对于Linux运维人员来说,一般采用awk, sed,grep 三种命令来对日志进行stdout(csv).但是这种纯文本输出不易于阅读,需要采用开源画图JS进行日志展示(比如:百度开源的Echarts: https://echarts.baidu.com/

Logstash: 开源的服务器端数据处理管道,能够同时从多个来源采集数据、转换数据,然后将数据存储到数据库中。(抽取日志,制定规则)
Elasticsearch:搜索、分析和存储数据。(分布式数据库,相当于一个管道)
Kibana:数据可视化。
Beats :轻量型采集器的平台,从边缘机器向Logstash 和Elasticsearch 发送数据。
Filebeat:轻量型日志采集器。
https://www.elastic.co/cn/
https://www.elastic.co/subscriptions

ELK Stack架构


Input:输入,输出数据可以是Stdin、File、TCP、Redis、Syslog等。(数据源,redis, mysql )
https://www.elastic.co/guide/en/logstash/current/input-plugins.html
Filter:过滤,将日志格式化。有丰富的过滤插件:Grok正则捕获、Date时间处理、Json编解码、Mutate数据修改等。(将数据结构化).
https://www.elastic.co/guide/en/logstash/current/filter-plugins.html
Output:输出,输出目标可以是Stdout、File、TCP、Redis、ES等。
https://www.elastic.co/guide/en/logstash/current/output-plugins.html

ElasticSearch

基本概念

Node:运行单个ES实例的服务器.
Cluster:一个或多个节点构成集群
Index:索引是多个文档的集合
Document:Index里每条记录称为Document,若干文档构建一个Index
Type:一个Index可以定义一种或多种类型,将Document逻辑分组
Field:ES存储的最小单元
Shards:ES将Index分为若干份,每一份就是一个分片
Replicas:Index的一份或多份副本。

ElasticSearch 关系型数据库(比如MySQL)
Index Database
Type Table
Document Row
Field Column

环境配置

组件名称 IP设定
elk-web 172.16.4.22
elk-lk 172.16.4.21
elk-es3 172.16.4.20
elk-es2 172.16.4.19
elk-es1 172.16.4.18

安装要求

以下操作需要在所有组件上执行

# 安装网络插件
yum install -y net-tools
# 安装vim文本编辑器
yum install -y vim
# 确认各组件之间网络是互通的,以确保之间能相互通信。
iptables -F
iptables -I INPUT -s 172.16.4.0/24 -j ACCEPT
# 当然也可以设置开启启动生效,在/etc/rc.loal下加入iptables -I INPUT -s 172.16.4.0/24 -j ACCEPT
vim /etc/rc.local
chmod +x /etc/rc.local
# 关闭selinux,修改SELINUX=disabled 并重启生效。
vim /etc/selinux/config
执行getenforce,确保得到的值为Disabled。
# 修改时区信息,使得各节点之间时间同步
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
yum install -y ntpdate -y; ntpdate time.windows.com
# 安装上传下载拖拽工具
yum -y install lrzsz

ElasticSearch集群部署

在elk-es1,elk-es2,elk-es3三台机器上安装环境

yum install java-1.8.0-openjdk -y

安装Logstash的方式:https://www.elastic.co/guide/en/logstash/current/installing-logstash.html#package-repositories
此处采用yum安***r> (1)首先导入公钥

rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch

(2)添加其yum源

# elastic.repo不存在则创建
[root@elk-es1 ~]# cat /etc/yum.repos.d/elastic.repo 
[elastic-7.x]
name=Elastic repository for 7.x packages
baseurl=https://artifacts.elastic.co/packages/7.x/yum
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=1
autorefresh=1
type=rpm-md

(3)安装elasticsearch

yum install -y elasticsearch
  • 注意,安装时间挺长的,取决于网速。
    (4)修改 /etc/elasticsearch/elasticsearch.yml,配置如下参数
# 此处以第一个节点为例,其余节点请修改相应的node.name和network.host值
cluster.name: elk-cluster
node.name: node-1  #此处以第一个es节点为例,后续请修改此值。
path.data: /var/lib/elasticsearch
path.logs: /var/log/elasticsearch
# 注意:172.16.4.18是第一个es节点的IP,其余两个请自行更换掉。
network.host: 172.16.4.18
http.port: 9200
discovery.seed_hosts: ["172.16.4.18", "172.16.4.19", "172.16.4.20"]
cluster.initial_master_nodes: ["172.16.4.18", "172.16.4.19"]
或cluster.initial_master_nodes: ["node-1", "node-2"]
# 你可以通过为 cluster.initial_master_nodes 参数设置一系列符合主节点条件的节点的主机名或 IP 地址来引导启动集群。

(5)启动elasticsearch

systemctl restart elasticsearch
# 以第一个节点为例:查看启动日志,发现node-1已加入集群中。
[root@elk-es1 ~]# tail -f /var/log/elasticsearch/elk-cluster.log
[2019-06-20T10:20:03,555][INFO ][o.e.n.Node               ] [node-1] initialized
[2019-06-20T10:20:03,555][INFO ][o.e.n.Node               ] [node-1] starting ...
[2019-06-20T10:20:03,778][INFO ][o.e.t.TransportService   ] [node-1] publish_address {172.16.4.18:9300}, bound_addresses {172.16.4.18:9300}
[2019-06-20T10:20:03,786][INFO ][o.e.b.BootstrapChecks    ] [node-1] bound or publishing to a non-loopback address, enforcing bootstrap checks
[2019-06-20T10:20:03,813][INFO ][o.e.c.c.Coordinator      ] [node-1] cluster UUID [NH-xRtJlSxedjkkTTU6W6w]
[2019-06-20T10:20:04,322][INFO ][o.e.c.s.ClusterApplierService] [node-1] master node changed {previous [], current [{node-2}{FMT_mamATbaSJN4jTO0HgA}{TxDTa1-pQ_mAV5eAeYqaoA}{172.16.4.19}{172.16.4.19:9300}{ml.machine_memory=16629563392, ml.max_open_jobs=20, xpack.installed=true}]}, added {{node-2}{FMT_mamATbaSJN4jTO0HgA}{TxDTa1-pQ_mAV5eAeYqaoA}{172.16.4.19}{172.16.4.19:9300}{ml.machine_memory=16629563392, ml.max_open_jobs=20, xpack.installed=true},{node-3}{Q1gJOBGLQLa_tT47v_wAAA}{gH9EbgR6QU2dozCVIqFCbw}{172.16.4.20}{172.16.4.20:9300}{ml.machine_memory=16629559296, ml.max_open_jobs=20, xpack.installed=true},}, term: 40, version: 21, reason: ApplyCommitRequest{term=40, version=21, sourceNode={node-2}{FMT_mamATbaSJN4jTO0HgA}{TxDTa1-pQ_mAV5eAeYqaoA}{172.16.4.19}{172.16.4.19:9300}{ml.machine_memory=16629563392, ml.max_open_jobs=20, xpack.installed=true}}
[2019-06-20T10:20:04,355][INFO ][o.e.h.AbstractHttpServerTransport] [node-1] publish_address {172.16.4.18:9200}, bound_addresses {172.16.4.18:9200}
[2019-06-20T10:20:04,356][INFO ][o.e.n.Node               ] [node-1] started
[2019-06-20T10:20:04,567][WARN ][o.e.x.s.a.s.m.NativeRoleMappingStore] [node-1] Failed to clear cache for realms [[]]
[2019-06-20T10:20:04,594][INFO ][o.e.l.LicenseService     ] [node-1] license [9452dc51-8874-4ac1-82f3-bb9f6002dc7e] mode [basic] - valid
[2019-06-20T10:20:04,854][INFO ][o.e.x.s.a.TokenService   ] [node-1] refresh keys
[2019-06-20T10:20:05,039][INFO ][o.e.x.s.a.TokenService   ] [node-1] refreshed keys

(6)校验集群健康性:https://www.elastic.co/guide/en/elasticsearch/reference/current/getting-started-cluster-health.html

  • 点击上述链接中的COPY AS CURL,复制粘贴到某个节点,注意修改IP值。
[root@elk-es3 ~]# curl -X GET "172.16.4.20:9200/_cat/health?v"
epoch      timestamp cluster     status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent
1560997781 02:29:41  elk-cluster green           3         3      0   0    0    0        0             0                  -                100.0%
# 集群状态显示“Green”,表示集群健康。还有其他“Yellow和Red”两种状态。

- 查看集群节点
<https://www.elastic.co/guide/en/elasticsearch/reference/current/getting-started-cluster-health.html>点击链接中的下图红框,并粘贴到终端:
![1560998430683](C:\Users\49137\AppData\Roaming\Typora\typora-user-images\1560998430683.png)

[root@elk-es1 ~]# curl -X GET "172.16.4.20:9200/_cat/nodes?v"
ip          heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
172.16.4.18           25          16   0    0.03    0.02     0.05 mdi       -      node-1
172.16.4.20           26          16   0    0.00    0.01     0.05 mdi       -      node-3
172.16.4.19           15          16   0    0.00    0.01     0.05 mdi       *      node-2
# heap.percent是堆内存的百分比

## ElasticSearch-数据操作
RestFul API格式
curl -X<verb> ‘<protocol>://<host>:<port>/<path>?<query_string>’-d ‘<body>’
-X 指定动作:(get post, put等)
| 参数         | 描述                                                    |
| ------------ | ------------------------------------------------------- |
| verb         | HTTP方法,比如GET、POST、PUT、HEAD、DELETE              |
| host         | ES集群中的任意节点主机名                                |
| port         | ES HTTP服务端口,默认9200                               |
| path         | 索引路径                                                |
| query_string | 可选的查询请求参数。例如?pretty参数将格式化输出JSON数据 |
| -d           | 里面放一个GET的JSON格式请求主体                         |
| body         | 自己写的 JSON格式的请求主体                             |
### 增删改
(1)列出索引:
[root@elk-es1 ~]# curl -X GET "172.16.4.18:9200/_cat/indices?v"
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
# 当下没有创建索引,所以默认显示上述结果。

(2)创建索引:

[root@elk-es1 ~]# curl -X PUT "172.16.4.18:9200/logs-test-2019.06.20?pretty"
{
  "error" : {
    "root_cause" : [
      {
        "type" : "resource_already_exists_exception",
        "reason" : "index [logs-test-2019.06.20/TbyeRd2pQZCciyJ4O5mRog] already exists",
        "index_uuid" : "TbyeRd2pQZCciyJ4O5mRog",
        "index" : "logs-test-2019.06.20"
      }
    ],
    "type" : "resource_already_exists_exception",
    "reason" : "index [logs-test-2019.06.20/TbyeRd2pQZCciyJ4O5mRog] already exists",
    "index_uuid" : "TbyeRd2pQZCciyJ4O5mRog",
    "index" : "logs-test-2019.06.20"
  },
  "status" : 400
}
# 其中?pretty表示是采用json格式化显示结果,再次查看索引,发现已经发生变化。
[root@elk-es1 ~]# curl -X GET "172.16.4.18:9200/_cat/indices?v"
health status index                uuid                   pri rep docs.count docs.deleted store.size pri.store.size
green  open   logs-test-2019.06.20 TbyeRd2pQZCciyJ4O5mRog   1   1          0            0       460b           230b

(3)查询文档

# 向创建的索引中添加内容。
[root@elk-es1 ~]# curl -X PUT "172.16.4.18:9200/logs-test-2019.06.20/_doc/1?pretty" -H 'Content-Type: application/json' -d'
{
  "name": "Ge Zirong"
}
'
# 返回如下结果
{
  "_index" : "logs-test-2019.06.20",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 2,
    "failed" : 0
  },
  "_seq_no" : 0,
  "_primary_term" : 1
}

# 获取索引内容
[root@elk-es1 ~]# curl -X GET "172.16.4.18:9200/logs-test-2019.06.20/_doc/1?pretty"
{
  "_index" : "logs-test-2019.06.20",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 1,
  "_seq_no" : 0,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "name" : "Ge Zirong"
  }
}

(4)删除索引

[root@elk-es1 ~]# curl -X DELETE "172.16.4.18:9200/logs-test-2019.06.20?pretty"
{
  "acknowledged" : true
}
# 如果再次查看索引内容,会返回404错误。
[root@elk-es1 ~]# curl -X GET "172.16.4.18:9200/logs-test-2019.06.20/_doc/1?pretty"
{
  "error" : {
    "root_cause" : [
      {
        "type" : "index_not_found_exception",
        "reason" : "no such index [logs-test-2019.06.20]",
        "resource.type" : "index_expression",
        "resource.id" : "logs-test-2019.06.20",
        "index_uuid" : "_na_",
        "index" : "logs-test-2019.06.20"
      }
    ],
    "type" : "index_not_found_exception",
    "reason" : "no such index [logs-test-2019.06.20]",
    "resource.type" : "index_expression",
    "resource.id" : "logs-test-2019.06.20",
    "index_uuid" : "_na_",
    "index" : "logs-test-2019.06.20"
  },
  "status" : 404
}

(5)修改数据

# 重新创建
[root@elk-es1 ~]# curl -X PUT "172.16.4.18:9200/logs-test-2019.06.20/_doc/1?pretty" -H 'Content-Type: application/json' -d'{ 
  "name": "Zhangsan"
}                   
'
# 再次查看,确保索引创建成功
[root@elk-es1 ~]# curl -X GET "172.16.4.18:9200/logs-test-2019.06.20/_doc/1?pretty"

# 直接修改
[root@elk-es1 ~]# curl -X PUT "172.16.4.18:9200/logs-test-2019.06.20/_doc/1?pretty" -H 'Content-Type: application/json' -d'
{
  "name": "Lisi"
}
'
{
  "_index" : "logs-test-2019.06.20",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 2,
  "result" : "updated",
  "_shards" : {
    "total" : 2,
    "successful" : 2,
    "failed" : 0
  },
  "_seq_no" : 1,
  "_primary_term" : 1
}
# 发现上述结果是更新操作,Zhangsan 变成了Lisi
# 通过POST进行修改
[root@elk-es1 ~]# curl -X POST "172.16.4.18:9200/logs-test-2019.06.20/_doc/1?pretty" -H 'Content-Type: application/json' -d'
{
  "name": "Wangwu"
}
'
{
  "_index" : "logs-test-2019.06.20",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 3,
  "result" : "updated",
  "_shards" : {
    "total" : 2,
    "successful" : 2,
    "failed" : 0
  },
  "_seq_no" : 2,
  "_primary_term" : 1
}
# 修改Document的字段,增加age字段
[root@elk-es1 ~]# curl -X POST "172.16.4.18:9200/logs-test-2019.06.20/_doc/1?pretty" -H 'Content-Type: application/json' -d'
{
  "name": "Wangwu","age": 20
}
'
[root@elk-es1 ~]# curl -X GET "172.16.4.18:9200/logs-test-2019.06.20/_doc/1?pretty"
{
  "_index" : "logs-test-2019.06.20",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 4,
  "_seq_no" : 3,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "name" : "Wangwu",
    "age" : 20
  }
}

多种查询方式

# 下载官方提供的数据进行
[root@elk-es1 ~]# wget https://raw.githubusercontent.com/elastic/elasticsearch/master/docs/src/test/resources/accounts.json
# 导入数据
[root@elk-es1 ~]# curl -H "Content-Type: application/json" -XPOST "172.16.4.18:9200/bank/_bulk?pretty&refresh" --data-binary "@accounts.json"
# 查看数据
[root@elk-es1 ~]# curl -X GET "172.16.4.18:9200/_cat/indices?v"
health status index                uuid                   pri rep docs.count docs.deleted store.size pri.store.size
green  open   bank                 XHBHpyl-SxiLlaWaRzta1A   1   1       1000            0    841.2kb        414.3kb
green  open   logs-test-2019.06.20 1sV4KMz6SdKapiUoOkmgrA   1   1          1            2     13.1kb          9.3kb

(1)多类型查询
https://www.elastic.co/guide/en/elasticsearch/reference/current/getting-started-search-API.html

  • match_all查询
[root@elk-es1 ~]# curl -X GET "172.16.4.18:9200/bank/_search?q=*&sort=account_number:asc&pretty"
# 将查询结果返回,通过search接口查询,q=*表示所有文档 asc升序排序。
[root@elk-es1 ~]# curl -X GET "172.16.4.18:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
  "query": { "match_all": {} },
  "sort": [
    { "account_number": "asc" }
  ]
}
'
  • from-size查询方式
# 1. 规定记录条数,size=1
[root@elk-es1 ~]# curl -X GET "172.16.4.18:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": { "match_all": {} },
  "size": 1
}
'
# 返回结果如下
[root@elk-es1 ~]# curl -X GET "172.16.4.18:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
> {
>   "query": { "match_all": {} },
>   "size": 1
> }
> '
{
  "took" : 6,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1000,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "bank",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "account_number" : 1,
          "balance" : 39225,
          "firstname" : "Amber",
          "lastname" : "Duke",
          "age" : 32,
          "gender" : "M",
          "address" : "880 Holmes Lane",
          "employer" : "Pyrami",
          "email" : "amberduke@pyrami.com",
          "city" : "Brogan",
          "state" : "IL"
        }
      }
    ]
  }
}
# 2. 规定记录的范围
[root@elk-es1 ~]# curl -X GET "172.16.4.18:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
  "query": { "match_all": {} },
  "from": 10,
  "size": 10
}
'
# 易于用于分页显示

# 3. 指定字段排序
[root@elk-es1 ~]# curl -X GET "172.16.4.18:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": { "match_all": {} },
  "_source": ["account_number", "balance"]
}
'
{
  "took" : 10,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1000,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "bank",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "account_number" : 1,
          "balance" : 39225
        }
      },
      ......
    ]
  }
}
  • match(根据字段输出结果)
[root@elk-es1 ~]# curl -X GET "172.16.4.18:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
# 显示结果如下
{
  "query": { "match_all": {} },
  "_source": ["account_number", "balance"]
}
'
{
  "took" : 10,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1000,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "bank",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "account_number" : 1,
          "balance" : 39225
        }
      },
      ......
    ]
  }
}

# match模糊查询,不区分大小写
[root@elk-es1 ~]# curl -X GET "172.16.4.18:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": { "match": { "address": "mill" } }
}
'
# 匹配或关系查询
[root@elk-es1 ~]# curl -X GET "172.16.4.18:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": { "match": { "address": "mill lane" } }
}
'
  • bool值查询
# 1. 与查询,查询包含mill和lane的记录
[root@elk-es1 ~]# curl -X GET "172.16.4.18:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "bool": {
      "must": [
        { "match": { "address": "mill" } },
        { "match": { "address": "lane" } }
      ]
    }
  }
}
'
# 2. 或查询,查询包含
[root@elk-es1 ~]# curl -X GET "172.16.4.18:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "bool": {
      "should": [
        { "match": { "address": "mill" } },
        { "match": { "address": "lane" } }
      ]
    }
  }
}
'
# 3. “非”查询
[root@elk-es1 ~]# curl -X GET "172.16.4.18:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "bool": {
      "must_not": [
        { "match": { "address": "mill" } },
        { "match": { "address": "lane" } }
      ]
    }
  }
}
'
# 4. 混合查询,精确查询
[root@elk-es1 ~]# curl -X GET "172.16.4.18:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "bool": {
      "must": [
        { "match": { "age": "40" } }
      ],
      "must_not": [
        { "match": { "state": "ID" } }
      ]
    }
  }
}
'
  • filter(范围查询)
# 1. 数值范围查询(20000<=balance<=30000),也可以采用时间之类的范围查询
[root@elk-es1 ~]# curl -X GET "172.16.4.18:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "bool": {
      "must": { "match_all": {} },
      "filter": {
        "range": {
          "balance": {
            "gte": 20000,
            "lte": 30000
          }
        }
      }
    }
  }
}
'

Head插件图形管理ElasticSearch

以下操作只在es1:172.16.4.18上操作:

[root@elk-es1 ~]# wget https://npm.taobao.org/mirrors/node/latest/node-v12.4.0-linux-x64.tar.gz
[root@elk-es1 ~]# tar zxvf node-v12.4.0-linux-x64.tar.gz
# 将解压的文件移动
[root@elk-es1 ~]# mv node-v12.4.0-linux-x64 /usr/local/node-v12.4
# 配置环境变量
vim /etc/profile
# 并加入以下内容
NODE_NAME=/usr/local/node-v12.4
PATH=$NODE_NAME/bin:$PATH
export NODE_HOME PATH
source /etc/profile  # 使其生效

# 验证安装成功与否
[root@elk-es1 ~]# npm -v
6.9.0
[root@elk-es1 ~]# node -v
v12.4.0

# 安装git客户端并拉取
yum install -y git
git clone https://github.com/mobz/elasticsearch-head.git

# 进入elasticsearch-head并安装npm
cd elasticsearch-head/ && npm install

# 编辑Gruntfile.js,在95行后加入hostname: '*',以监听所有地址。
vim Gruntfile.js
......
 port: 9100,
 base: '.',
 keepalive: true,
 hostname: '*'
 
# 启动npm
[root@elk-es1 elasticsearch-head]# npm run start

> elasticsearch-head@0.0.0 start /root/elasticsearch-head
> grunt server

>> Local Npm module "grunt-contrib-jasmine" not found. Is it installed?

Running "connect:server" (connect) task
Waiting forever...
Started connect web server on http://localhost:9100
# 此时需要修改/etc/elasticsearch/elasticsearch.yml,在文档最后加入下面两行内容
vim /etc/elasticsearch/elasticsearch.yml
http.cors.enabled: true
http.cors.allow-origin: "*"
# 重启elasticsearch使其生效
[root@elk-es1 ~]# systemctl restart elasticsearch
# 查看日志,看到“started”字样表明启动成功
[root@elk-es1 ~]# tail /var/log/elasticsearch/elk-cluster.log -f
......
[2019-06-22T11:30:25,163][INFO ][o.e.n.Node               ] [node-1] started
....
  • 在浏览器中输入172.16.4.18:9100即可访问
  • 可以根据字段来查询
  • 复合查询
  • 查看集群信息(点击右上角的“信息”按钮)

Logstash

安装

以下操作在elk-lk:172.16.4.21上执行。

# 先从es1上传入yum源过来。
[root@elk-es1 ~]# scp /etc/yum.repos.d/elastic.repo root@172.16.4.21:/etc/yum.repos.d/

# 安装jdk
[root@elk-lk ~]# yum install java-1.8.0-openjdk -y

# 在elk节点上安装
[root@elk-lk ~]# yum install -y logstash

# 查看命令执行路径
[root@elk-lk ~]# rpm -ql logstash |more
....
/usr/share/logstash/bin/logstash
......

# 将执行路径写入环境变量,写入/etc/profile
PATH=$PATH:/usr/share/logstash/bin
export PATH
source /etc/profile

# 查看安装版本信息
[root@elk-lk ~]# logstash -V
logstash 7.1.1

Logstash-条件判断

比较操作符:
相等: ==, !=, <, >, <=, >=
正则: =~(匹配正则), !~(不匹配正则)
包含: in(包含), not in(不包含)
布尔操作符:
and(与), or(或), nand(非与), xor(非或)
一元运算符:
!(取反)
()(复合表达式), !()(对复合表达式结果取反)

Input插件

(1)Stdin:https://www.elastic.co/guide/en/logstash/current/plugins-inputs-stdin.html

[root@elk-lk ~]# vim  /etc/logstash/conf.d/test.conf
# 输入以下内容。
input {
        stdin {
                
        }
}
filter {
        
}

output {
        stdout {
                codec => rubydebug
        }
}

# 测试配置文件正确性,如下:-t表示测试,-f 指定文件位置
[root@elk-lk ~]# logstash -t -f /etc/logstash/conf.d/test.conf 
......
Configuration OK
[INFO ] 2019-06-22 16:46:25.010 [LogStash::Runner] runner - Using config.test_and_exit mode. Config Validation Result: OK. Exiting Logstash
.......
# 直接运行,加-r参数
    
[root@elk-lk ~]# logstash -r -f /etc/logstash/conf.d/test.conf
......
[INFO ] 2019-06-22 16:50:27.933 [Api Webserver] agent - Successfully started Logstash API endpoint {:port=>9600}
345
/usr/share/logstash/vendor/bundle/jruby/2.5.0/gems/awesome_print-1.7.0/lib/awesome_print/formatters/base_formatter.rb:31: warning: constant ::Fixnum is deprecated
{
      "@version" => "1",
    "@timestamp" => 2019-06-22T08:50:53.993Z,
       "message" => "345",
          "host" => "elk-lk"
}
124
{
      "@version" => "1",
    "@timestamp" => 2019-06-22T08:50:56.786Z,
       "message" => "124",
          "host" => "elk-lk"
}
# 如上键入123 345,都能形成timestamp日志,系统帮你加入时间戳

(2)Flie示例:https://www.elastic.co/guide/en/logstash/current/plugins-inputs-file.html

# 重新开一个终端:elk-lk-1:172.16.4.21 更改/etc/logstash/conf.d/test.conf内容如下:
input {
file {
path =>"/var/log/messages"
tags =>"123"
type =>"syslog"
}
}
filter {
}
output {
stdout {
codec => rubydebug
}
}

# 重新执行
[root@elk-lk ~]# logstash -r -f /etc/logstash/conf.d/test.conf
......
[INFO ] 2019-06-22 16:57:47.013 [[main]-pipeline-manager] javapipeline - Pipeline started {"pipeline.id"=>"main"}
[INFO ] 2019-06-22 16:57:47.094 [Ruby-0-Thread-1: /usr/share/logstash/lib/bootstrap/environment.rb:6] agent - Pipelines running {:count=>1, :running_pipelines=>[:main], :non_running_pipelines=>[]}
[INFO ] 2019-06-22 16:57:47.103 [[main]<file] observingtail - START, creating Discoverer, Watch with file and sincedb collections
[INFO ] 2019-06-22 16:57:47.376 [Api Webserver] agent - Successfully started Logstash API endpoint {:port=>9600}
/usr/share/logstash/vendor/bundle/jruby/2.5.0/gems/awesome_print-1.7.0/lib/awesome_print/formatters/base_formatter.rb:31: warning: constant ::Fixnum is deprecated
{
          "path" => "/var/log/messages",
          "type" => "syslog",
    "@timestamp" => 2019-06-22T09:01:02.150Z,
      "@version" => "1",
       "message" => "Jun 22 17:01:01 elk-lk systemd: Started Session 85 of user root.",
          "tags" => [
        [0] "123"
    ],
          "host" => "elk-lk"
}
# 输出内容如上

# 可以重开一个终端,安装httpd,使其产生日志
[root@elk-lk ~]# yum install -y httpd
......
---> Package httpd-tools.x86_64 0:2.4.6-89.el7.centos will be installed
......
# 返回另外一个终端,查看到/var/log/messages下出现很多日志,其中有一条与上述对应:
[root@elk-lk ~]# logstash -r -f /etc/logstash/conf.d/test.conf
......
[INFO ] 2019-06-22 17:08:50.190 [Api Webserver] agent - Successfully started Logstash API endpoint {:port=>9600}
/usr/share/logstash/vendor/bundle/jruby/2.5.0/gems/awesome_print-1.7.0/lib/awesome_print/formatters/base_formatter.rb:31: warning: constant ::Fixnum is deprecated
{
......
{
          "path" => "/var/log/messages",
      "@version" => "1",
          "host" => "elk-lk",
          "tags" => [
        [0] "123"
    ],
          "type" => "syslog",
    "@timestamp" => 2019-06-22T09:13:50.914Z,
       "message" => "Jun 22 17:13:50 elk-lk yum[10167]: Installed: httpd-tools-2.4.6-89.el7.centos.x86_64"
}
......
}

(3)TCP示例:https://www.elastic.co/guide/en/logstash/current/plugins-inputs-tcp.html

# 更改/etc/logstash/conf.d/test.conf内容如下:
input {
tcp {
port =>12345
type =>"nc"
}
}
filter {
}
output {
stdout {
codec => rubydebug
}
}

[root@elk-lk ~]# logstash -r -f /etc/logstash/conf.d/test.conf
# 查看是否监听到12345
[root@elk-lk ~]# netstat -antp | grep 12345
tcp6       0      0 :::12345                :::*                    LISTEN      10436/java   
# 采用nc发送数据,为此在elk-web:172.16.4.22上安装nc
[root@elk-web ~]# yum install -y nc
# 同时关闭两端(elk-web和elk-lk)的iptabels
iptables -F
# 在elk-web采用nc发送TCP数据;
[root@elk-web ~]# nc 172.16.4.21 12345
1
2
3
gezirong

# 在elk-lk对端能够监听并接受到
[root@elk-lk ~]# logstash -r -f /etc/logstash/conf.d/test.conf
......
[INFO ] 2019-06-22 20:51:32.979 [[main]<tcp] tcp - Starting tcp input listener {:address=>"0.0.0.0:12345", :ssl_enable=>"false"}
[INFO ] 2019-06-22 20:51:33.054 [Ruby-0-Thread-1: /usr/share/logstash/lib/bootstrap/environment.rb:6] agent - Pipelines running {:count=>1, :running_pipelines=>[:main], :non_running_pipelines=>[]}
[INFO ] 2019-06-22 20:51:33.286 [Api Webserver] agent - Successfully started Logstash API endpoint {:port=>9600}
/usr/share/logstash/vendor/bundle/jruby/2.5.0/gems/awesome_print-1.7.0/lib/awesome_print/formatters/base_formatter.rb:31: warning: constant ::Fixnum is deprecated
{
          "type" => "nc",
    "@timestamp" => 2019-06-22T12:53:17.524Z,
          "host" => "elk-web",
      "@version" => "1",
       "message" => "1",
          "port" => 55440
}
{
          "type" => "nc",
    "@timestamp" => 2019-06-22T12:53:23.484Z,
          "host" => "elk-web",
      "@version" => "1",
       "message" => "2",
          "port" => 55440
}
{
          "type" => "nc",
    "@timestamp" => 2019-06-22T12:53:24.977Z,
          "host" => "elk-web",
      "@version" => "1",
       "message" => "3",
          "port" => 55440
}
{
          "type" => "nc",
    "@timestamp" => 2019-06-22T12:53:28.152Z,
          "host" => "elk-web",
      "@version" => "1",
       "message" => "gezirong",
          "port" => 55440
}
# 上述键入1 2 3 gezirong都被作为一个nc数据监听到记录下来了。

Logstash Codec常用插件

官方参考地址:https://www.elastic.co/guide/en/logstash/current/codec-plugins.html

(1)Json/Json_lines示例:

# 更改/etc/logstash/conf.d/test.conf为以下内容:
input {
stdin {
codec =>json {
charset => ["UTF-8"]
}
}
}
filter {
}
output {
stdout {
codec => rubydebug
}
}

[root@elk-lk ~]# logstash -r -f /etc/logstash/conf.d/test.conf
.........
[INFO ] 2019-06-22 21:06:04.902 [Api Webserver] agent - Successfully started Logstash API endpoint {:port=>9600}
# 粘贴如下内容,json必须为一行。
{"ip":"172.16.4.18","hostname":"elk-es1","method":"GET"}
# 按回车就能识别出来json数据
{
      "@version" => "1",
    "@timestamp" => 2019-06-22T13:21:33.539Z,
        "method" => "GET",
            "ip" => "172.16.4.18",
          "host" => "elk-lk",
      "hostname" => "elk-es1"
}

(2)Multiline示例:https://www.elastic.co/guide/en/logstash/current/plugins-codecs-multiline.html

日志可以按照特征进行聚合,避免报重复错误。

# 更改/etc/logstash/conf.d/test.conf为以下内容,并保存退出:
# pattern => "^\s"表示匹配以字符开头的行,what => "previous" 表示将不是以字符串开头的行聚合到上一行。
input {
  stdin {
    codec => multiline {
      pattern => "^\s"
      what => "previous"
    }
  }
}
filter {
}
output {
   stdout {
     codec => rubydebug
   }
}

# 执行文件
[root@elk-lk ~]# logstash -r -f /etc/logstash/conf.d/test.conf
.....
[INFO ] 2019-06-22 21:35:50.908 [Api Webserver] agent - Successfully started Logstash API endpoint {:port=>9600}
# 键入以下内容
Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
# 显示内容如下,把日志聚合了:
{
          "host" => "elk-lk",
    "@timestamp" => 2019-06-22T13:42:52.708Z,
          "tags" => [
        [0] "multiline"
    ],
      "@version" => "1",
       "message" => "Caused by: java.lang.reflect.InvocationTargetException\n    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)"
}

Logstash Filter常用插件

主要对日志进行结构化处理。

官方文档:https://www.elastic.co/guide/en/logstash/current/filter-plugins.html

json插件

https://www.elastic.co/guide/en/logstash/current/plugins-filters-json.html

# 更改/etc/logstash/conf.d/test.conf为以下内容,并保存退出:
# 其中source是指定待转化的字段,
input {
  stdin {
  }
}
filter {
  json {
    source => "message"
    target => "content"
  }
}
output {
  stdout {
    codec => rubydebug
  }
}

[root@elk-lk ~]# logstash -r -f /etc/logstash/conf.d/test.conf
......
[INFO ] 2019-06-22 21:52:14.165 [Api Webserver] agent - Successfully started Logstash API endpoint {:port=>9600}
# 键入以下内容,并回车
{"ip":"172.16.4.18","hostname":"elk-es1","method":"GET"}
# 显示结果如下,把message中的数据结构化到content中了,因为上述test.conf明确指定了message结构化指定的字段名称。
{
       "content" => {
              "ip" => "172.16.4.18",
        "hostname" => "elk-es1",
          "method" => "GET"
    },
       "message" => "{\"ip\":\"172.16.4.18\",\"hostname\":\"elk-es1\",\"method\":\"GET\"}",
    "@timestamp" => 2019-06-22T13:54:32.910Z,
          "host" => "elk-lk",
      "@version" => "1"
}

kv插件

https://www.elastic.co/guide/en/logstash/current/plugins-filters-kv.html

主要用于拆分一个字符串。

# 更改/etc/logstash/conf.d/test.conf为以下内容,并保存退出
input {
  stdin {
  }
}
filter {
  kv {
    field_split => "&?"
  }
}
output {
  stdout {
    codec => rubydebug
  }
}
[root@elk-lk ~]# logstash -r -f /etc/logstash/conf.d/test.conf
......
[INFO ] 2019-06-22 22:05:44.802 [Api Webserver] agent - Successfully started Logstash API endpoint {:port=>9600}
# 输入官方提供的以下示例内容按回车
pin=12345~0&d=123&e=foo@bar.com&oq=bobo&ss=12345?a=123  
# 显示内容如下:其中用“&”和“?”将字符分割了,采用的是key=value进行解析的。
{
    "@timestamp" => 2019-06-22T14:14:58.026Z,
          "host" => "elk-lk",
           "pin" => "12345~0",
             "e" => "foo@bar.com",
            "ss" => "12345",
             "a" => "123",
       "message" => "pin=12345~0&d=123&e=foo@bar.com&oq=bobo&ss=12345?a=123",
      "@version" => "1",
             "d" => "123",
            "oq" => "bobo"
}
# 当然也可以使用正则匹配,更改/etc/logstash/conf.d/test.conf中kv里的值。
input {
  stdin {
  }
}
filter {
  kv {
    field_split_pattern => ":+"
  }
}
output {
  stdout {
    codec => rubydebug
  }
}
# 重新启动
[root@elk-lk ~]# logstash -r -f /etc/logstash/conf.d/test.conf
......
[INFO ] 2019-06-22 22:18:57.477 [Api Webserver] agent - Successfully started Logstash API endpoint {:port=>9600}
# 键入以下内容
k1=v1:k2=v2::k3=v3:::k4=v4
# 显示如下:可以自动忽略多个:
{
            "k4" => "v4",
            "k3" => "v3",
       "message" => "k1=v1:k2=v2::k3=v3:::k4=v4",
            "k2" => "v2",
          "host" => "elk-lk",
            "k1" => "v1",
    "@timestamp" => 2019-06-22T14:22:58.473Z,
      "@version" => "1"
}

GeoIP示例

是获取IP地理位置信息的插件

官方文档:https://www.elastic.co/guide/en/logstash/current/plugins-filters-geoip.html

下载GeoIP的官方城市数据库:https://geolite.maxmind.com/download/geoip/database/GeoLite2-City.tar.gz放在elk-lk服务器上。

[root@elk-lk ~]# wget https://geolite.maxmind.com/download/geoip/database/GeoLite2-City.tar.gz
[root@elk-lk ~]# tar zxvf GeoLite2-City.tar.gz 
GeoLite2-City_20190618/
GeoLite2-City_20190618/LICENSE.txt
GeoLite2-City_20190618/GeoLite2-City.mmdb
GeoLite2-City_20190618/COPYRIGHT.txt
GeoLite2-City_20190618/README.txt
# 将最新更新的城市数据拷贝到/opt下。
[root@elk-lk ~]# cp GeoLite2-City_20190618/GeoLite2-City.mmdb /opt/

# 更改/etc/logstash/conf.d/test.conf为以下内容,并保存退出
input {
  stdin {
  }
}
filter {
  grok {
    match => {
      "message" => "%{IP:client} %{WORD:method} %{URIPATHPARAM:request} %{NUMBER:bytes} %{NUMBER:duration}"
    }
  }
  geoip {
    source => "client"
    database => "/opt/GeoLite2-City.mmdb"
  }
}
output {
  stdout {
    codec => rubydebug
  }
}

[root@elk-lk ~]# logstash -r -f /etc/logstash/conf.d/test.conf
......
[INFO ] 2019-06-22 22:40:44.731 [Api Webserver] agent - Successfully started Logstash API endpoint {:port=>9600}
# 输入以下内容
202.201.1.20 GET /lab_team.html 500 0.1
# 返回如下内容,如显示地理位置为兰州。
{
       "request" => "/lab_team.html",
       "message" => "202.201.1.20 GET /lab_team.html 500 0.1",
         "geoip" => {
           "region_name" => "Gansu",
          "country_name" => "China",
              "location" => {
            "lat" => 36.057,
            "lon" => 103.8399
        },
              "timezone" => "Asia/Shanghai",
         "country_code3" => "CN",
         "country_code2" => "CN",
        "continent_code" => "AS",
                    "ip" => "202.201.1.20",
           "region_code" => "GS",
             "city_name" => "Lanzhou",
             "longitude" => 103.8399,
              "latitude" => 36.057
    },
      "duration" => "0.1",
        "client" => "202.201.1.20",
    "@timestamp" => 2019-06-23T01:56:12.786Z,
      "@version" => "1",
          "host" => "elk-lk",
         "bytes" => "500",
        "method" => "GET"
}

date示例

主要用于数据的时间进行修改。即匹配日志中时间戳,而不是处理日志的时间戳。

官方地址:https://www.elastic.co/guide/en/logstash/current/plugins-filters-date.html

Grok插件

正则匹配非结构化的数据进行结构化的输出,方便前台展示。

官方地址:https://www.elastic.co/guide/en/logstash/current/plugins-filters-grok.html

grok有很多的正则匹配模式,详见:https://github.com/logstash-plugins/logstash-patterns-core/blob/master/patterns/grok-patterns

# 更改/etc/logstash/conf.d/test.conf为以下内容,并保存退出
input {
  stdin {
  }
}
filter {
  grok {
    match => {
      "message" => "%{IP:client} %{WORD:method} %{URIPATHPARAM:request} %{NUMBER:bytes} %{NUMBER:duration}"
    }
  }
}
output {
  stdout {
    codec => rubydebug
  }
}
# 启动logstash
[root@elk-lk ~]# logstash -r -f /etc/logstash/conf.d/test.conf
......
[INFO ] 2019-06-23 10:29:59.401 [Api Webserver] agent - Successfully started Logstash API endpoint {:port=>9600}
# 输入以下内容,回车
202.201.1.20 GET /lab_team.html 500 0.1
# 显示内容如下:
{
      "@version" => "1",
         "bytes" => "500",
    "@timestamp" => 2019-06-23T02:59:10.150Z,
       "request" => "/lab_team.html",
       "message" => "202.201.1.20 GET /lab_team.html 500 0.1",
      "duration" => "0.1",
        "method" => "GET",
        "client" => "202.201.1.20",
          "host" => "elk-lk"
}

[root@elk-lk ~]# rpm -ql logstash | grep grok-patterns
/usr/share/logstash/vendor/bundle/jruby/2.5.0/gems/logstash-patterns-core-4.1.2/patterns/grok-patterns

[root@elk-lk ~]# cat /usr/share/logstash/vendor/bundle/jruby/2.5.0/gems/logstash-patterns-core-4.1.2/patterns/grok-patterns
# 上述结果的输出与官网中的grok-patterns内容一致

# 更改/etc/logstash/conf.d/test.conf为以下内容,并保存退出
input {
  stdin {
  }
}
filter {
  grok {
    patterns_dir =>"/opt/patterns"
    match => {
      "message" => "%{IP:client} %{WORD:method} %{URIPATHPARAM:request} %{NUMBER:bytes} %{NUMBER:duration} %{ID:id}"
    }
  }
}
output {
  stdout {
    codec => rubydebug
  }
}
# 自定义模式
vim /opt/patterns
# 写入以下内容,匹配10-11位上的数字和大写字母
ID [0-9A-Z]{10,11}
# 启动
[root@elk-lk ~]# logstash -r -f /etc/logstash/conf.d/test.conf
......
[INFO ] 2019-06-23 19:04:57.425 [Api Webserver] agent - Successfully started Logstash API endpoint {:port=>9600}
# 输入以下内容
202.201.1.20 GET /lab_team.html 500 0.1 123456
# 显示内容如下,由于123456不足10位,所以显示匹配不成功:_grokparsefailure
{
          "tags" => [
        [0] "_grokparsefailure"
    ],
          "host" => "elk-lk",
       "message" => "202.201.1.20 GET /lab_team.html 500 0.1 123456",
      "@version" => "1",
    "@timestamp" => 2019-06-23T11:07:53.062Z
}
# 虽然位数够了,但是第10位上不是数字或者大写字母,所以照样失败
202.201.1.20 GET /lab_team.html 500 0.1 123456789a
{
          "tags" => [
        [0] "_grokparsefailure"
    ],
          "host" => "elk-lk",
       "message" => "202.201.1.20 GET /lab_team.html 500 0.1 123456789a",
      "@version" => "1",
    "@timestamp" => 2019-06-23T11:11:15.303Z
}
# 匹配成功
202.201.1.20 GET /lab_team.html 500 0.1 123456789A
{
       "message" => "202.201.1.20 GET /lab_team.html 500 0.1 123456789A",
        "client" => "202.201.1.20",
      "@version" => "1",
    "@timestamp" => 2019-06-23T11:11:25.405Z,
            "id" => "123456789A",
          "host" => "elk-lk",
       "request" => "/lab_team.html",
        "method" => "GET",
         "bytes" => "500",
      "duration" => "0.1"
}

(5)多模式匹配:

# 更改/etc/logstash/conf.d/test.conf为以下内容,并保存退出。
input {
  stdin {
  }  
}
filter {
  grok {
    patterns_dir =>"/opt/patterns"
    match => [
      "message","%{IP:client} %{WORD:method} %{URIPATHPARAM:request} %{NUMBER:bytes} %{NUMBER:duration} %{ID:id}",
      "message","%{IP:client} %{WORD:method} %{URIPATHPARAM:request} %{NUMBER:bytes} %{NUMBER:duration} %{TAG:tag}"
    ]
  } 
}
output {
  stdout {
    codec => rubydebug
  }
}
# 修改/opt/patterns内容为如下:
ID [0-9A-Z]{10-11}
TAG SYSLOG1
# 此时grok应该能匹配两种日志格式:SYSLOG1 和 ID的
[root@elk-lk ~]# logstash -r -f /etc/logstash/conf.d/test.conf
......
[INFO ] 2019-06-23 19:51:30.492 [Api Webserver] agent - Successfully started Logstash API endpoint {:port=>9600}
# 输入以下内容回车:
202.201.1.20 GET /lab_team.html 500 0.1 123456789A
# 显示内容如下:
{
          "host" => "elk-lk",
        "client" => "202.201.1.20",
    "@timestamp" => 2019-06-23T11:52:43.098Z,
            "id" => "123456789A",
      "@version" => "1",
       "message" => "202.201.1.20 GET /lab_team.html 500 0.1 123456789A",
         "bytes" => "500",
      "duration" => "0.1",
        "method" => "GET",
       "request" => "/lab_team.html"
}
# 输入以下内容回车
202.201.1.20 GET /lab_team.html 500 0.1 SYSLOG1 
# 显示内容如下:
{
          "host" => "elk-lk",
        "client" => "202.201.1.20",
           "tag" => "SYSLOG1",
    "@timestamp" => 2019-06-23T11:53:23.848Z,
      "@version" => "1",
       "message" => "202.201.1.20 GET /lab_team.html 500 0.1 SYSLOG1",
         "bytes" => "500",
      "duration" => "0.1",
        "method" => "GET",
       "request" => "/lab_team.html"
}

Logstash-Output插件

官方地址:https://www.elastic.co/guide/en/logstash/current/output-plugins.html

在elk-es1上启动下head,必须在elasticsearch-head目录下执行
[root@elk-es1 ~]# cd elasticsearch-head/

# 将head放入后台执行
[root@elk-es1 elasticsearch-head]# nohup npm run start &
[1] 11957
[root@elk-es1 elasticsearch-head]# nohup: ignoring input and appending output to ‘nohup.out’
# 按下回车即可放入后台,并查看是否在后台启动成功。
[root@elk-es1 elasticsearch-head]# tail nohup.out 
Started connect web server on http://localhost:9100

> elasticsearch-head@0.0.0 start /root/elasticsearch-head
> grunt server

>> Local Npm module "grunt-contrib-jasmine" not found. Is it installed?

Running "connect:server" (connect) task
Waiting forever...
Started connect web server on http://localhost:9100

# 在elk-lk下安装上传下载工具:
[root@elk-lk ~]# yum install -y lrzsz
# 编写一个测试文档logstash-to-es.conf,内容如下,注意修改hosts的IP:
input {
    file {
        path => ["/var/log/messages"]
        type => "system"
        tags => ["syslog","test"]
        start_position => "beginning"
    }
    file {
        path => ["/var/log/audit/audit.log"]
        type => "system"
        tags => ["auth","test"]
        start_position => "beginning"
    }
}
filter {

}
output {
    if [type] == "system" {
        if [tags][0] == "syslog" {
            elasticsearch {
                hosts  => ["http://172.16.4.18:9200","http://172.16.4.19:9200","http://172.16.4.20:9200"]
                index  => "logstash-system-syslog-%{+YYYY.MM.dd}"
            }
            stdout { codec=> rubydebug }
        }
        else if [tags][0] == "auth" {
            elasticsearch {
                hosts  => ["http://172.16.4.18:9200","http://172.16.4.19:9200","http://172.16.4.20:9200"]
                index  => "logstash-system-auth-%{+YYYY.MM.dd}"
            }
            stdout { codec=> rubydebug }
        }
    }
}
# 上述文件表明收集了系统日志/var/log/messages(安装软件,debug日志)和 /var/log/audit/audit.log(ssh登录时,或者一些其他的审计日志)
# 由于上次已经配置过了yum源,所以可以直接安装kibana
[root@elk-lk ~]# yum install -y kibana

output中elasticsearch插件:https://www.elastic.co/guide/en/logstash/current/plugins-outputs-elasticsearch.html

Kibana

ELK Stack 常用案例

# 在elk-lk上更改/etc/kibana/kibana.yml 配置主节点
server.port: 5601
server.host: "0.0.0.0"
elasticsearch.hosts: ["http://172.16.4.19:9200"]
  • 打开浏览器输入172.16.4.21:5601即可访问kibana, 如果出现:“Kibana server is not ready yet”, 请多刷几次页面,或者查看kibana和elasticsearch的版本是否一致。

    [root@elk-es2 ~]# rpm -qa elasticsearch
    elasticsearch-7.1.1-1.x86_64
    [root@elk-lk ~]# rpm -qa kibana
    kibana-7.1.1-1.x86_64
    
    
  • 可以查看到kibana中管理的我们的索引

[root@elk-lk ~]# mv logstash-to-es.conf /etc/logstash/conf.d/
[root@elk-lk ~]# logstash -r -f /etc/logstash/conf.d/logstash-to-es.conf
# 然后会看到生成很多个记录文件。
# 再到es1的elasticsearch-head目录下,查看生成的索引,发现logstash-system-auth和logstash-system-syslog中有了。
[root@elk-es1 elasticsearch-head]# curl -X GET "172.16.4.18:9200/_cat/indices?v"
health status index                             uuid                   pri rep docs.count docs.deleted store.size pri.store.size
green  open   bank                              XHBHpyl-SxiLlaWaRzta1A   1   1       1000            0    841.2kb        414.3kb
green  open   logs-test-2019.06.20              1sV4KMz6SdKapiUoOkmgrA   1   1          1            2     13.1kb          9.3kb
green  open   .kibana_task_manager              KPSZy8i0RPmLcb9wctH0aA   1   1          2            0       75kb         45.5kb
green  open   logstash-system-auth-2019.06.25   AUWPnGytSKyOTaZvZlK67A   1   1       2141            0    795.5kb        388.9kb
green  open   .kibana_1                         9EaFiVF_TyOxnX2-_nk9Bg   1   1          4            0     35.3kb         17.6kb
green  open   logstash-system-syslog-2019.06.25 YbV3UavDQXuBSFsJcfXDng   1   1        913            0    418.4kb        205.9kb
  • 再次刷新页面,发现出现了新的索引
  • 创建logstash-system-auth
  • 同样的操作创建logstash-system-syslog
  • 点击发现日志,选择刚才的“logstash-system-syslog”或者“logstash-system-auth”即可查看到日志
  • 可以在“Available fields”中选择需要显示的字段,如hosts,message等

Kibana常用查询表达式

  • 注意如果要查询的字段是包含空格需要加双引号,使其成为一个整体字符串。
Find requests that contain the number 200, in any field      200
Find 200 in the status field                                 status:200
Find all status codes between 400-499                        status:[400 TO 499]
Find status codes 400-499 with the extension php    status:[400 TO 499] AND extension:PHP

Find status codes 400-499 with the extension php or html
status:[400 TO 499] AND (extension:php OR extension:html)

基于Nginx实现Kibana访问认证

增加kibana的安全性,elastic提供了安全组件X-Pack(付费服务):

网上提供了破解的方法(试试看):

# 在elk-web上安装NGINX,地址:http://nginx.org/download/
wget http://nginx.org/download/nginx-1.17.0.tar.gz
# 进行源码编译安装
[root@elk-web ~]# tar zxvf nginx-1.17.0.tar.gz
[root@elk-web ~]# cd nginx-1.17.0
# 安装依赖库文件 	
[root@elk-web nginx-1.17.0]# yum install -y openssl-devel zlib-devel pcre-devel
[root@elk-web nginx-1.17.0]# yum install -y gcc
[root@elk-web nginx-1.17.0]# ./configure --prefix=/usr/local/nginx
[root@elk-web nginx-1.17.0]# make && make install
[root@elk-web nginx-1.17.0]# pkill nginx
# 进入安装目录
[root@elk-web nginx-1.17.0]# cd /usr/local/nginx/sbin
[root@elk-web sbin]# ./nginx
  • 在浏览器测试能不能访问。输入elk-web的IP
# 可以查看到访问日志
[root@elk-web sbin]# tail ../logs/access.log -f
210.26.55.133 - - [25/Jun/2019:11:33:17 +0800] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36"
210.26.55.133 - - [25/Jun/2019:11:33:17 +0800] "GET /favicon.ico HTTP/1.1" 404 555 "http://172.16.4.22/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36"
# 接下来配置虚拟主机,来代理Kibana
[root@elk-web sbin]# cd ../conf/
# 在nginx.conf中配置代理的地址,在43行location下增加以下字段
[root@elk-web conf]# vim nginx.conf
......
43         location / {
 44             proxy_pass http://172.16.4.21:5601;
.......
# 重载下nginx
[root@elk-web conf]# ../sbin/nginx -t
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
[root@elk-web conf]# ../sbin/nginx -s reload
  • 此时elk-lk(172.16.4.21)已经被代理到了elk-web(172.16.4.22)上,浏览器输入172.16.4.22就可以访问kibana了。
  • 现在我们可以在nginx内加入请求认证
# 再次修改/usr/local/nginx/conf文件,加入auth_basic字段,要求用户输入用户名和密码,加入auth_basic_user_file字段,用户名和密码的存放位置.
vim /usr/local/nginx/conf/nginx.conf
......
proxy_pass http://172.16.4.21:5601;
auth_basic "Please input user and password";
auth_basic_user_file /usr/local/nginx/conf/passwd.db;
......
# 注意在原先加入proxy_pass下加入上述两行文件
[root@elk-web conf]# ../sbin/nginx -s reload
  • 刷新浏览器,此时就要求输入用户名和密码了
# 创建用户名和密码,上述已经指定了文件路径为/usr/local/nginx/conf/passwd.db
# 采用openssl生成密文密码
[root@elk-web conf]# openssl passwd -crypt 123456
Fvh88eAdf3TWI
# vim /usr/local/nginx/conf/passwd.db
username:Fvh88eAdf3TWI
  • 然后在浏览器中输入“username”和“123456”就可以访问了。

引入Redis


Logstash:过滤和收集,此处分开功能。

  • 在服务器比较多的情况下,日志数据较多的时候,可以采用redis(缓存),防止elasticsearch采集日志较慢,前台kibana展示数据有时间差。
  • 统一日志的管理

(1)编写logstash-from-redis.conf文件至elk-lk的/etc/logstash/conf.d/目录中,该文件是从redis中去读数据。

[root@elk-lk conf.d]# cat logstash-from-redis.conf 
input {
    redis {
        host => "172.16.4.21"
        port => 6379
        password => "123456"
        db => "0"
        data_type => "list"
        key => "logstash"
    }
}

filter {

}

output {
    if [type] == "system" {
        if [tags][0] == "syslog" {
            elasticsearch {
                hosts  => ["http://172.16.4.18:9200","http://172.16.4.19:9200","http://172.16.4.20:9200"]
                index  => "logstash-system-syslog-%{+YYYY.MM.dd}"
            }
            stdout { codec=> rubydebug }
        }
        else if [tags][0] == "auth" {
            elasticsearch {
                hosts  => ["http://172.16.4.18:9200","http://172.16.4.19:9200","http://172.16.4.20:9200"]
                index  => "logstash-system-auth-%{+YYYY.MM.dd}"
            }
            stdout { codec=> rubydebug }
        }
    }
}
# Input redis插件中的host只能为一个特定的IP,不是数组类型
# 安装redis的源,默认是没有的
[root@elk-lk ~]# yum install -y epel-release
[root@elk-lk conf.d]# yum install -y redis

# 配置redis
vim /etc/redis.conf
# 在第61行加入监听地址,此处为了方便,默认监听所有地址
......
61 bind 0.0.0.0
.....
# 在第480行,输入redis密码,此处为方便,设置为123456,并保存退出
480 requirepass 123456
# 启动redis
[root@elk-lk conf.d]# systemctl start redis
[root@elk-lk conf.d]# ps -ef | grep redis
redis     21456      1  0 15:07 ?        00:00:00 /usr/bin/redis-server 0.0.0.0:6379

(2)在elk-web(172.16.4.22)上安装logstash,因为需要

# 复制elk-es1的yum源至elk-web
[root@elk-es1 ~]# scp /etc/yum.repos.d/elastic.repo root@172.16.4.22:/etc/yum.repos.d/
# 在elk-web上安装logstash
[root@elk-web conf.d]# yum install -y java-1.8.0-openjdk
[root@elk-web ~]# yum install -y logstash
# 在elk-web中将logstash加入环境变量
PATH=$PATH:/usr/share/logstash/bin
export PATH
# 强制使其生效
source /etc/profile

[root@elk-web ~]# cd /etc/logstash/conf.d/
[root@elk-web conf.d]# vim logstash-to-redis.conf
# 编写logstash写入redis的文件,内容如下,注意修改为自己的集群IP

input {
    file {
        path => ["/var/log/messages"]
        type => "system"
        tags => ["syslog","test"]
        start_position => "beginning"
    }
    file {
        path => ["/var/log/audit/audit.log"]
        type => "system"
        tags => ["auth","test"]
        start_position => "beginning"
    }
}

filter {

}

output {
    redis {
        host => ["172.16.4.21:6379"]
        password => "123456"
        db => "0"
        data_type => "list"
        key => "logstash"
    }
}
# 注意:output的redis的host一定是指向安装了redis的IP。可以是多个IP,这样可以在多台里写入数据,提高冗余。elastic output plugin的redis插件用法详见官网:https://www.elastic.co/guide/en/logstash/current/plugins-outputs-redis.html

# 查看redis中是否有数据
[root@elk-lk ~]# redis-cli -a 123456
127.0.0.1:6379> keys *
(empty list or set)
# 启动logstash,看到Successfully started表示启动成功。
[root@elk-web conf.d]# logstash -f /etc/logstash/conf.d/logstash-to-redis.conf 
......
[INFO ] 2019-06-25 15:34:31.289 [Api Webserver] agent - Successfully started Logstash API endpoint {:port=>9600}

# 再次登录redis,查看key,发现多了一个key
[root@elk-lk ~]# redis-cli -a 123456
127.0.0.1:6379> keys *
1) "logstash"
# 查看key的记录数
127.0.0.1:6379> llen logstash
(integer) 2244

# 在elk-es1的elasticsearch-head下启动npm
[root@elk-es1 ~]# cd elasticsearch-head/
# 之前的操作,将npm放入后台启动过,如果没关,无需下述操作。
[root@elk-es1 ~]# npm run start 
  • 在浏览器输入elk-es的IP,端口号9100,访问es集群
  • 在elk-lk开始启动logstash-from-redis.conf,开始从redis读入日志数据,为了方便演示,可以将上述无关的索引进行删除
[root@elk-lk conf.d]# logstash -f /etc/logstash/conf.d/logstash-from-redis.conf
# 会发现有大量日志记录刷新在屏幕上,重开一个elk-lk终端,再次查看redis中的记录数,发现被消费完了。
127.0.0.1:6379> llen logstash
(integer) 0
  • 再次在浏览器中访问Kibana,也能查看到日志情况

引入Filebeat



官方文档:https://www.elastic.co/products/beats/filebeat

Filebeat实际上比较省资源,为了解决logstash比较占资源的弊端。

[root@elk-web conf.d]# yum install -y filebeat
[root@elk-web conf.d]# vim /etc/filebeat/filebeat.yml
# 将以下内容生效,其余暂时注释掉。
filebeat.inputs:
- type: log
  paths:
    - /var/log/messages
  tags: ["syslog","test"]
  fileds:
    type: system
  fileds_under_root: true
 
- type: log
  paths:
    - /var/log/audit/audit.log
  tags: ["auth","test"]
  fields:
    type: system
  fields_under_root: true
# 输出到redis中
output.redis:
  hosts: ["172.16.4.21"]
  password: "123456"
  key: "filebeat"
  db: 0
  datatype: list
# 启动filebeat
[root@elk-web conf.d]# systemctl start filebeat

# 查看redis中是否收集到信息
[root@elk-lk ~]# redis-cli -a 123456
127.0.0.1:6379> keys *
1) "filebeat"
127.0.0.1:6379> llen filebeat
(integer) 3277
# 更改输入源为filebeat,其余数据保持不变
[root@elk-lk conf.d]# cat /etc/logstash/conf.d/logstash-from-redis.conf 
input {
    redis {
        host => "172.16.4.21"
        port => 6379
        password => "123456"
        db => "0"
        data_type => "list"
        key => "filebeat"   # 此处修改
    }
}

filter {

}

output {
    if [type] == "system" {
        if [tags][0] == "syslog" {
            elasticsearch {
                hosts  => ["http://172.16.4.18:9200","http://172.16.4.19:9200","http://172.16.4.20:9200"]
                index  => "logstash-system-syslog-%{+YYYY.MM.dd}"
            }
            stdout { codec=> rubydebug }
        }
        else if [tags][0] == "auth" {
            elasticsearch {
                hosts  => ["http://172.16.4.18:9200","http://172.16.4.19:9200","http://172.16.4.20:9200"]
                index  => "logstash-system-auth-%{+YYYY.MM.dd}"
            }
            stdout { codec=> rubydebug }
        }
    }
}

# 启动logstash,从redis读入数据
[root@elk-lk conf.d]# logstash -f /etc/logstash/conf.d/logstash-from-redis.conf
# 重开一个elk-lk, 进入redis,发现日志记录为0,已被logstash消费完
127.0.0.1:6379> llen filebeat
(integer) 0
127.0.0.1:6379> get filebeat
(nil)

# 在elk-es1中ssh到elk-web,然后会发现elk-lk会收集到登录日志信息,如下:
......
{
           "log" => {
        "offset" => 466114,
          "file" => {
            "path" => "/var/log/audit/audit.log"
        }
    },
          "tags" => [
        [0] "auth",
        [1] "test"
    ],
           "ecs" => {
        "version" => "1.0.0"
    },
      "@version" => "1",
          "type" => "system",
       "message" => "type=CRYPTO_SESSION msg=audit(1561470434.558:1651): pid=34870 uid=0 auid=4294967295 ses=4294967295 msg='op=start direction=from-client cipher=chacha20-poly1305@openssh.com ksize=512 mac=<implicit> pfs=curve25519-sha256 spid=34871 suid=74 rport=33384 laddr=172.16.4.22 lport=22  exe=\"/usr/sbin/sshd\" hostname=? addr=172.16.4.18 terminal=? res=success'",
         "agent" => {
             "version" => "7.1.1",
            "hostname" => "elk-web",
                "type" => "filebeat",
        "ephemeral_id" => "ed5247b8-5bc1-48ea-a57c-4d18d474b023",
                  "id" => "bb3b79b8-a0f1-48e6-ba52-1d6c70a77833"
    },
         "input" => {
        "type" => "log"
    },
          "host" => {
        "name" => "elk-web"
    },
    "@timestamp" => 2019-06-25T13:47:17.441Z
}
......
# 说明收集日志成功
  • 此时刷新浏览器,访问es集群,发现出现了logstash-system-auth的信息,同样地,打开kibana也能查看到刷新的日志信息。

生产应用案例

收集Nginx访问日志

采用filebeat写入日志数据到Redis

# 确保以下服务正常运行
[root@elk-lk ~]# ps -ef | grep redis
redis     21456      1  0 15:07 ?        00:01:03 /usr/bin/redis-server 0.0.0.0:6379

# 修改/usr/local/nginx/conf的nginx.conf,取消如下三行
......
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                 '$status $body_bytes_sent "$http_referer" '
                 '"$http_user_agent" "$http_x_forwarded_for"';
......

# 将日志格式修改为json格式,内容如下并在上述的nginx.conf文件中的log_format main内容后:
log_format json '{ "@timestamp": "$time_iso8601", '
         '"remote_addr": "$remote_addr", '
         '"remote_user": "$remote_user", '
         '"body_bytes_sent": "$body_bytes_sent", '
         '"request_time": "$request_time", '
         '"status": "$status", '
         '"request_uri": "$request_uri", '
         '"request_method": "$request_method", '
         '"http_referrer": "$http_referer", '
         '"http_x_forwarded_for": "$http_x_forwarded_for", '
         '"http_user_agent": "$http_user_agent"}';
# 将以下行生效,注意,之前是调用main.
access_log  logs/access.log  json;
# 将nginx命令加入环境变量/etc/profile中
PATH=$PATH:/usr/share/logstash/bin:/usr/share/filebeat/bin:/usr/local/nginx/sbin
export PATH
# 使其生效
[root@elk-web conf]# source /etc/profile
# 重载nginx配置
[root@elk-web conf]# nginx -t
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
[root@elk-web conf]# nginx -s reload
  • 更改elk-web上的filebeat配置文件内容为如下:
[root@elk-web ~]# cat /etc/filebeat/filebeat.yml 
filebeat.inputs:
- type: log
  paths:
    - /usr/local/nginx/logs/access.log
  fields:
    app: www
    type: nginx-access
  fields_under_root: true
  
- type: log
  paths:
    - /usr/local/nginx/logs/error.log
  fields:
    app: www
    type: nginx-error
  fields_under_root: true

output.redis:
  hosts: ["172.16.4.21"]
  password: "123456"
  key: "filebeat"
  db: 0
  datatype: list
  • 重启filebeat,并查看是否是收集上述两个目录的日志。
[root@elk-web ~]# systemctl restart filebeat
[root@elk-web ~]# tail /var/log/messages
Jun 26 15:19:17 elk-web filebeat: 2019-06-26T15:19:17.406+0800#011INFO#011log/input.go:148#011Configured paths: [/usr/local/nginx/logs/access.log]
Jun 26 15:19:17 elk-web filebeat: 2019-06-26T15:19:17.406+0800#011INFO#011input/input.go:114#011Starting input of type: log; ID: 13046343894051577886
Jun 26 15:19:17 elk-web filebeat: 2019-06-26T15:19:17.406+0800#011INFO#011log/input.go:148#011Configured paths: [/usr/local/nginx/logs/error.log]
Jun 26 15:19:17 elk-web filebeat: 2019-06-26T15:19:17.406+0800#011INFO#011input/input.go:114#011Starting input of type: log; ID: 3665717057268333493
Jun 26 15:19:17 elk-web filebeat: 2019-06-26T15:19:17.406+0800#011INFO#011crawler/crawler.go:106#011Loading and starting Inputs completed. Enabled inputs: 2
Jun 26 15:19:17 elk-web filebeat: 2019-06-26T15:19:17.407+0800#011INFO#011log/harvester.go:254#011Harvester started for file: /usr/local/nginx/logs/access.log
Jun 26 15:19:17 elk-web filebeat: 2019-06-26T15:19:17.407+0800#011INFO#011log/harvester.go:254#011Harvester started for file: /usr/local/nginx/logs/error.log
Jun 26 15:19:18 elk-web filebeat: 2019-06-26T15:19:18.407+0800#011INFO#011pipeline/output.go:95#011Connecting to redis(tcp://172.16.4.21:6379)
Jun 26 15:19:18 elk-web filebeat: 2019-06-26T15:19:18.411+0800#011INFO#011pipeline/output.go:105#011Connection to redis(tcp://172.16.4.21:6379) established

# 查看redis是否写入数据
[root@elk-lk ~]# redis-cli -a 123456
127.0.0.1:6379> keys *
1) "filebeat"
127.0.0.1:6379> llen filebeat
(integer) 2978
# 刷新下浏览器,发现日志数确实有所增加
127.0.0.1:6379> llen filebeat
(integer) 3073

采用Logstash读取日志到elasticsearch中

(1)更改编写日志读取配置文件logstash-to-es-nginxjson.conf

[root@elk-lk ~]# cat /etc/logstash/conf.d/logstash-to-es-nginxjson.conf 
input {
    redis {
        host => "172.16.4.21"
        port => 6379
        password => "123456"
        db => "0"
        data_type => "list"
        key => "filebeat"
    }
}

filter {
  if [app] == "www" {
    if [type] == "nginx-access" {
      json {
          source => "message"
          remove_field => ["message"]
      }
      geoip {
          source => "remote_addr"
          target => "geoip"
          database => "/opt/GeoLite2-City.mmdb"
          add_field => ["[geoip][coordinates]", "%{[geoip][longitude]}"] 
          add_field => ["[geoip][coordinates]", "%{[geoip][latitude]}"]
      }
     
      mutate {
          convert => ["[geoip][coordinates]", "float"]  
      }
    }
  }
}

output {
  elasticsearch {
      hosts  => ["http://172.16.4.18:9200","http://172.16.4.19:9200","http://172.16.4.20:9200"]
      index  => "logstash-%{type}-%{+YYYY.MM.dd}"
  }
  stdout{codec => rubydebug }
}
  • mutate是filter里的插件

启动logstash,消费redis中的日志记录,在屏幕中会看到,很多条记录刷新

[root@elk-lk ~]# logstash -f /etc/logstash/conf.d/logstash-to-es-nginxjson.conf
{
                     "log" => {
        "offset" => 49770,
          "file" => {
            "path" => "/usr/local/nginx/logs/access.log"
        }
    },
                  "status" => "200",
                   "agent" => {
             "version" => "7.1.1",
                "type" => "filebeat",
        "ephemeral_id" => "d0ac533d-e71a-4a39-b3f6-b3a6c906d9ec",
            "hostname" => "elk-web",
                  "id" => "bb3b79b8-a0f1-48e6-ba52-1d6c70a77833"
    },
                    "type" => "nginx-access",
                     "app" => "www",
         "body_bytes_sent" => "898",
    "http_x_forwarded_for" => "-",
            "request_time" => "0.033",
                     "ecs" => {
        "version" => "1.0.0"
    },
          "request_method" => "POST",
                    "host" => {
        "name" => "elk-web"
    },
                "@version" => "1",
                   "input" => {
        "type" => "log"
    },
             "remote_user" => "username",
                   "geoip" => {
           "coordinates" => [
            [0] 103.8399,
            [1] 36.057
        ],
          "country_name" => "China",
        "continent_code" => "AS",
         "country_code3" => "CN",
             "longitude" => 103.8399,
           "region_name" => "Gansu",
              "location" => {
            "lon" => 103.8399,
            "lat" => 36.057
        },
             "city_name" => "Lanzhou",
                    "ip" => "210.26.55.133",
           "region_code" => "GS",
              "latitude" => 36.057,
         "country_code2" => "CN",
              "timezone" => "Asia/Shanghai"
    },
         "http_user_agent" => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36",
             "request_uri" => "/api/index_management/indices/reload",
           "http_referrer" => "http://172.16.4.22/app/kibana",
             "remote_addr" => "210.26.55.133",
              "@timestamp" => 2019-06-26T08:33:56.000Z
}
.....
# 此时在另一个elk-lk终端再次连接到redis查看记录,发现已被消耗完
127.0.0.1:6379> llen filebeat
(integer) 0
  • 打开kibana的“Index management”,会发现多出了两个索引logstash-nginx-access-XXX和logstash-nginx-error-XXX.
  • 我们可以点击下方的Index Patterns创建后,在Discovery中观察到日志。


[root@elk-web ~]# tail /usr/local/nginx/logs/access.log 
......
{ "@timestamp": "2019-06-26T16:47:32+08:00", "remote_addr": "210.26.55.133", "remote_user": "username", "body_bytes_sent": "208", "request_time": "0.017", "status": "200", "request_uri": "/elasticsearch/_msearch?rest_total_hits_as_int=true&ignore_throttled=true", "request_method": "POST", "http_referrer": "http://172.16.4.22/app/kibana", "http_x_forwarded_for": "-", "http_user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36"}
# 仔细对比kibana中和文件日志中的最后 一条记录,发现两者的时间一致。

(2)同样地,打开elasticsearch,也能发现日志已经收集到es中了

  • 可以按IP查询访问日志

收集Java堆栈日志

安装tomcat

此处采用tomcat收集日志,官方软件下载地址:http://tomcat.apache.org/

[root@elk-web ~]# wget https://www-us.apache.org/dist/tomcat/tomcat-8/v8.5.42/bin/apache-tomcat-8.5.42.tar.gz
[root@elk-web ~]# tar zxvf apache-tomcat-8.5.42.tar.gz
[root@elk-web ~]# mv apache-tomcat-8.5.42/ /usr/local/tomcat && cd /usr/local/tomcat/; ls
bin  BUILDING.txt  conf  CONTRIBUTING.md  lib  LICENSE  logs  NOTICE  README.md  RELEASE-NOTES  RUNNING.txt  temp  webapps  work
[root@elk-web tomcat]# cd bin/
[root@elk-web bin]# ./startup.sh 
Using CATALINA_BASE:   /usr/local/tomcat
Using CATALINA_HOME:   /usr/local/tomcat
Using CATALINA_TMPDIR: /usr/local/tomcat/temp
Using JRE_HOME:        /usr
Using CLASSPATH:       /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar
Tomcat started.

[root@elk-web bin]# tail ../logs/catalina.out -f
......
26-Jun-2019 21:11:13.397 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 584 ms
  • 浏览器输入服务器端IP和端口号(8080)即可访问

使用Filebeat收集日志

(1)修改filebeat.yml,监听tomcat

# 在/etc/filebeat/filebeat.yml的filebeat.inputs添加日志类型如下,其余保持不变
- type: log
  paths:
    - /usr/local/tomcat/logs/catalina.out
  # tags: ["tomcat"]
  fields:
    app: www
    type: tomcat-catalina
  fields_under_root: true
  multiline:
    pattern: '^\['
    negate: true
    match: after

(2)重启filebeat,使配置生效

[root@elk-web ROOT]# systemctl restart filebeat
[root@elk-web ROOT]# tail /var/log/messages
......
Jun 26 21:28:19 elk-web filebeat: 2019-06-26T21:28:19.400+0800#011INFO#011log/harvester.go:254#011Harvester started for file: /usr/local/tomcat/logs/catalina.out
Jun 26 21:28:25 elk-web filebeat: 2019-06-26T21:28:25.402+0800#011INFO#011pipeline/output.go:95#011Connecting to redis(tcp://172.16.4.21:6379)
Jun 26 21:28:25 elk-web filebeat: 2019-06-26T21:28:25.406+0800#011INFO#011pipeline/output.go:105#011Connection to redis(tcp://172.16.4.21:6379) established

(3)打开kibana发现多出了logstash-tomcat-catalina-XXX的索引日志。

定制日志格式收集

(1)以nginx为例,之前采用json格式化日志输出形式,此处先改回来

# 修改/usr/local/nginx/conf/nginx.conf的内容,将
access_log  logs/access.log  json;
# 改为如下:
access_log  logs/access.log  main;
# 重载配置
[root@elk-web bin]# nginx -t
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
[root@elk-web bin]# nginx -s reload

(2)采用grok插件进行模式匹配

input {
    redis {
        host => "172.16.4.21"
        port => 6379
        password => "123456"
        db => "0"
        data_type => "list"
        key => "filebeat"
    }
}

filter {
  if [app] == "www" {
    if [type] == "nginx-access" {
      grok {
        match => {
          "message" => "%{IPV4:remote_addr} - (%{USERNAME:remote_user}|-) \[%{HTTPDATE:time_local}\] \"%{WORD:request_method} %{URIPATHPARAM:request_uri} HTTP/%{NUMBER:http_protocol}\" %{NUMBER:http_status} %{NUMBER:body_bytes_sent} \"%{GREEDYDATA:http_referer}\" \"%{GREEDYDATA:http_user_agent}\" \"(%{IPV4:http_x_forwarded_for}|-)\""
        }
        overwrite => ["message"]
      }
      geoip {
          source => "remote_addr"
          target => "geoip"
          database => "/opt/GeoLite2-City.mmdb"
          add_field => ["[geoip][coordinates]", "%{[geoip][longitude]}"] 
          add_field => ["[geoip][coordinates]", "%{[geoip][latitude]}"]
      }
      date {
          locale => "en"
          match => ["time_local", "dd/MMM/yyyy:HH:mm:ss Z"]
      }
      mutate {
          convert => ["[geoip][coordinates]", "float"]  
      }
    }
  }
}

output {
  elasticsearch {
      hosts  => ["http://172.16.4.18:9200","http://172.16.4.19:9200","http://172.16.4.20:9200"]
      index  => "logstash-%{type}-%{+YYYY.MM.dd}"
  }
  stdout{codec => rubydebug }
}

# 先取消代理,注释一下3行内容
[root@elk-web ~]# vim /usr/local/nginx/conf/nginx.conf
#proxy_pass http://172.16.4.21:5601;
#auth_basic "Please input user and password";
#auth_basic_user_file /usr/local/nginx/conf/passwd.db;
# 重载nginx
[root@elk-web ~]# nginx -t
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
[root@elk-web ~]# nginx -s reload
  • 默认访问nginx首页

    (3)启动logstash
[root@elk-lk ~]# logstash -f /etc/logstash/conf.d/logstash-to-es-custom.conf

Kibana仪表盘与可视化

手动添加日志记录,方便演示

[root@elk-web logs]# echo '210.26.55.132 - username [26/Jun/2019:22:34:34 +0800] "GET /app/kibana HTTP/1.1" 404 555 "http://172.16.4.22/app/kibana" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36" "-"' >> access.log
[root@elk-web logs]# echo '210.26.55.131 - username [26/Jun/2019:22:34:34 +0800] "GET /app/kibana HTTP/1.1" 404 555 "http://172.16.4.22/app/kibana" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36" "-"' >> access.log
[root@elk-web logs]# echo '210.26.55.130 - username [26/Jun/2019:22:34:34 +0800] "GET /app/kibana HTTP/1.1" 404 555 "http://172.16.4.22/app/kibana" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36" "-"' >> access.log
[root@elk-web logs]# echo '210.16.55.110 - username [26/Jun/2019:22:34:34 +0800] "GET /app/kibana HTTP/1.1" 404 555 "http://172.16.4.22/app/kibana" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36" "-"' >> access.log
[root@elk-web logs]# echo '210.16.54.119 - username [26/Jun/2019:22:34:34 +0800] "GET /app/kibana HTTP/1.1" 404 555 "http://172.16.4.22/app/kibana" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36" "-"' >> access.log
  • 刷新浏览器,发现kibana中确实多了上述几个IP的日志记录

PV/UV

(1)可以通过Kibana左侧的“Visualize”来可视化日志记录数,首先选择需要统计的日志,此处以“logstash-nginx-access-*”为例,选择X轴以时间轴为单位,纵坐标以日志数(Count)为值.

(2)对UV进行统计用户数

用户地理位置分布

  • 按照之前的操作,创建Coordinate Map.
  • 按照如下字段选择横纵左边所要显示的的内容。
  • 点击上述的加号,缩小地理范围,可以查看到IP来自的城市

URL/HTTP Status/IP TOP10

(1)统计URI数量,建立新的可视化视图,选择“Data Table”

(2)统计HTTP状态码Top10

(3)统计源IP访问TOP10

  • 至此,我们完成了6张图的绘制,现在我们采用dashboard仪表盘来添加刚才的图表,从而可视化数据

    备注:本内容来源于阿良老师的博客和课程,如有侵权,请联系删除,无意冒犯。只是记录学习。