学习目的

  • 业务发展越来越庞大,服务器越来越多
  • 各种访问日志、应用日志、错误日志量越来越多,导致运维人员无法很好的去管理日志
  • 开发人员排查问题,需要到服务器上查日志,不方便
  • 运营人员需要一些数据,需要我们运维到服务器上分析日志

为啥选用ELK

通常情况下我们通过linux的一些命令(grep,awk等)就可以查看一些命令,当随着业务量大,集群增多,日志管理比较麻烦,通过登陆服务器查看日志的做法已经跟不上日常工作,所以需要将日志统一管理,排查问题系统化,提高运维效率,更加方便的定位问题。

工具介绍

本次选用的工具主要为以下:

1、elasticsearch-6.8.10

2、filebeat-6.8.10-linux-x86_64

3、kibana-6.8.10-linux-x86_64

4、nginx-1.21.2

ELK选用的是6.8.10的版本,之所以选这个版本主要也是为了跟公司使用同步,具体下载地址可以通过官网:https://www.elastic.co/cn/downloads/,官网学习地址:https://www.elastic.co/guide/en/elasticsearch/reference/6.6/indices.html

安装步骤及配置文件部分讲解

ELK的安装使用是比较人性化的,开箱即用,这里对各个工具的配置文件进行简单演示,我用的用户统一为es用户,这里大家根据自己需要进行创建

1、elasticsearch安装,配置文件在/elasticsearch-6.8.10/config/elasticsearch.yml,因为我是单机配置,所以其他的不用配置,完成之前直接今日bin目录下通过命令:./elasticsearch启动即可

# ----------------------------------- Paths ------------------------------------
# Path to directory where to store the data (separate multiple locations by comma):
path.data: /home/es/elasticsearch/data
# Path to log files:
path.logs: /home/es/elasticsearch/logs
network.host: 0.0.0.0  # 这里配置0.0.0.0表示所有机器均可以访问,在集群中通常配置为内网ip
http.port: 9200   # 端口号不打开即默认9200

2、kibana安装,配置文件在/kibana-6.8.10/config/kibana.yml,主要更改以下地址,配置完成以后通过命令:./kibana即可

server.port: 5601

# Specifies the address to which the Kibana server will bind. IP addresses and host names are both valid values.
# The default is 'localhost', which usually means remote machines will not be able to connect.
# To allow connections from remote users, set this parameter to a non-loopback address.
server.host: 172.29.16.150

# Enables you to specify a path to mount Kibana at if you are running behind a proxy.
# Use the `server.rewriteBasePath` setting to tell Kibana if it should remove the basePath
# from requests it receives, and to prevent a deprecation warning at startup.
# This setting cannot end in a slash.
#server.basePath: ""

# Specifies whether Kibana should rewrite requests that are prefixed with
# `server.basePath` or require that they are rewritten by your reverse proxy.
# This setting was effectively always `false` before Kibana 6.3 and will
# default to `true` starting in Kibana 7.0.
#server.rewriteBasePath: false

# The maximum payload size in bytes for incoming server requests.
#server.maxPayloadBytes: 1048576

# The Kibana server's name.  This is used for display purposes.
#server.name: "your-hostname"

# The URLs of the Elasticsearch instances to use for all your queries.
elasticsearch.hosts: ["http://172.29.16.150:9200"]

3、自定义nginx日志就需要将其定义为json格式,方便filebeat识别,此处根据自己需要调节,仅仅做一个演示

1、先清空原来日志

2、# 日志格式自定义
log_format json '{ "@timestamp": "$time_iso8601", '
                          '"remote_addr": "$remote_addr", '
                          '"request": "$uri", '
                          '"request_uri": "$request_uri", '
                          '"request_method":"$request_method",'
                          '"server_protocol":"$server_protocol",'
                          '"status": $status, '
                          '"bytes": $body_bytes_sent, '
                          '"up_addr": "$upstream_addr",'
                          '"request_time": $request_time'
    ' }';

    access_log  logs/access.log  json;

#为了后面根据索引取数据也需要将日志根据日期进行切割,具体如下

if ($time_iso8601 ~ "^(\d{4})-(\d{2})-(\d{2})") {
            set $year $1;
            set $month $2;
            set $day $3;
                }
        access_log logs/access-${year}-${month}-${day}.log json;

4、filebeat-6.8.10安装,创建配置文件/filebeat-6.8.10/config/filebeat-es.yml文件,主要配置如下:配置完成后在bin目录下通过./filebeat -e -c filebeat-es.yml命令启动,其中-e表示标准启动,-c表示指定配置文件

filebeat.inputs:
- type: log

  enabled: true
  harvester_buffer_size: 10240000
  filebeat.spool_size: 200000
  filebeat.idle_timeout: 5s
  # Paths that should be crawled and fetched. Glob based paths.
  # 配置日志路径
  paths:
    - /home/es/nginx/logs/access*.log
  
# 这两行参数配置表示按json格式去解析
  json.keys_under_root: true
  overwrite_keys: true

# 将结果输出到elasticsearch中
output.elasticsearch:
  # Array of hosts to connect to.es的安装服务器
  hosts: ["172.29.16.150:9200"]
  #  根据日期在es中创建索引,这个很重要,如果配置此配置,就需要配置setup.template.name, 
  #  setup.template.pattern
  index: "zpl-aliyun-access-%{+yyyy.MM.dd}"
  bulk_max_size: 15000
  #flush_interval: 3s
# //设置一个新的模板,模板的名称
setup.template.name: "template-zpl-aliyun-access"
# 模板是否被覆盖,表示不用默认模板,而引用自定义
setup.template.overwrite: false
# //模板匹配那些索引,这里表示以zpl开头的所有的索引
setup.template.pattern: "zpl-aliyun-access*"

如果是将不同路径下日志写入es,参照下图所示,通过字段路径去识别

kibana ES清空所有数据_elasticsearch

还可以通过打标签方式实现,如图所示

kibana ES清空所有数据_elk_02

5、在elasticsearch中创建模板,这一步很重要

PUT _template/template-zpl-aliyun-access
{
  "template-zpl-aliyun-access" : {
    "order" : 0,
    "index_patterns" : [
      "zpl-aliyun-access*"
    ],
    "settings" : {
      "index" : {
        "max_result_window" : "1000000",
        "number_of_shards" : "3",
        "number_of_replicas" : "0"
      }
    },
    "mappings" : {
      "doc" : {
        "properties" : {
          "remote_addr" : {
            "ignore_above" : 1024,
            "type" : "keyword"
          },
          "request" : {
            "ignore_above" : 1024,
            "type" : "keyword"
          },
          "offset" : {
            "type" : "long"
          },
          "prospector" : {
            "properties" : {
              "type" : {
                "ignore_above" : 1024,
                "type" : "keyword"
              }
            }
          },
          "read_timestamp" : {
            "ignore_above" : 1024,
            "type" : "keyword"
          },
          "request_method" : {
            "ignore_above" : 1024,
            "type" : "keyword"
          },
          "source" : {
            "ignore_above" : 1024,
            "type" : "keyword"
          },
          "message" : {
            "norms" : false,
            "type" : "text"
          },
          "request_uri" : {
            "ignore_above" : 1024,
            "type" : "keyword"
          },
          "@timestamp" : {
            "type" : "date"
          },
          "request_time" : {
            "type" : "float"
          },
          "up_addr" : {
            "ignore_above" : 1024,
            "type" : "keyword"
          },
          "bytes" : {
            "type" : "long"
          },
          "service" : {
            "properties" : {
              "name" : {
                "ignore_above" : 1024,
                "type" : "keyword"
              }
            }
          },
          "server_protocol" : {
            "ignore_above" : 1024,
            "type" : "keyword"
          }
        }
      }
    },
    "aliases" : {
      "zpl-aliyun-access" : { }
    }
  }
}

针对配置文件做几点说明:

1、index_patterns:此字段中配置的索引表示以zpl-aliyun-access*的索引都适合该mapping规则

2、order:多个索引模板可能与索引匹配,在这种情况下,设置和映射都合并到索引的最终配置中。 可以使用order参数控制合并的顺序,首先应用较低的顺序,并且覆盖它们的较高顺序

3、aliases:别名,举例说明,zpl-aliyun-access-2021-12-12的index, 那么它同时生成了一个叫做logs-2019-04-01-alias的别名

其他字段属性可以参考es的学习,这里不说明,此外本文章适合具有一定基础的ELK学习者。


做聚合查询时候text跟keyword的取别

1、创建索引及其mapping,

1、创建索引及其mapping
PUT index-00000001
{"mappings" : {
      "doc" : {
        "properties" : {
          "age" : {
            "type" : "long"
          },
          "desc" : {
            "type" : "text",
            "fields" : {
              "keyword" : {
                "type" : "keyword",
                "ignore_above" : 256
              }
            }
          },
          "from" : {
            "type" : "text",
            "fields" : {
              "keyword" : {
                "type" : "keyword",
                "ignore_above" : 256
              }
            }
          },
          "name" : {
            "type" : "text",
            "fields" : {
              "keyword" : {
                "type" : "keyword",
                "ignore_above" : 256
              }
            }
          },
          "tags" : {
            "type" : "text",
            "fields" : {
              "keyword" : {
                "type" : "keyword",
                "ignore_above" : 256
              }
            }
          }
        }
      }
    }
  
}

2、查看已经创建好的索引
GET index-00000001/_mapping,结果如上述创建所示

3、插入数据
PUT index-00000001/doc/1
{
  "name":"顾老二",
  "age":30,
  "from": "gu",
  "desc": "皮肤黑、武器长、性格直",
  "tags": ["黑", "长", "直"]
}

PUT index-00000001/doc/2
{
  "name":"大娘子",
  "age":18,
  "from":"sheng",
  "desc":"肤白貌美,娇憨可爱",
  "tags":["白", "富","美"]
}

PUT index-00000001/doc/3
{
  "name":"龙套偏房",
  "age":22,
  "from":"gu",
  "desc":"mmp,没怎么看,不知道怎么形容",
  "tags":["造数据", "真","难"]
}


PUT index-00000001/doc/4
{
  "name":"石头",
  "age":29,
  "from":"gu",
  "desc":"粗中有细,狐假虎威",
  "tags":["粗", "大","猛"]
}

PUT index-00000001/doc/5
{
  "name":"魏行首",
  "age":25,
  "from":"广云台",
  "desc":"仿佛兮若轻云之蔽月,飘飘兮若流风之回雪,mmp,最后竟然没有嫁给顾老二!",
  "tags":["闭月","羞花"]
}

4、此时我要对from字段进行聚合查询,并对每个from的人进行age平均值计算
错误示例:
GET zhifou/doc/_search
{
  
  "aggs": {
    "1": {
      "terms": {
        "field": "from"  //这里from采用的第一属性text,无法查询到结果
      },
      "aggs": {
        "2": {
          "avg": {
            "field": "age"
            
          }
        }
      }
    }
  },
   "size":0,
  "_source": ["name", "age"]
}
当采用如何方式查询时候有报错,所以将其调整为以下方式
正确示例:
GET zhifou/doc/_search
{
  
  "aggs": {
    "1": {
      "terms": {
        "field": "from.keyword"
      },
      "aggs": {
        "2": {
          "avg": {
            "field": "age"
            
          }
        }
      }
    }
  },
   "size":0,
  "_source": ["name", "age"]
}

结果为:
{
  "took" : 3,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 5,
    "max_score" : 0.0,
    "hits" : [ ]
  },
  "aggregations" : {
    "1" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : "gu",
          "doc_count" : 3,
          "2" : {
            "value" : 27.0
          }
        },
        {
          "key" : "sheng",
          "doc_count" : 1,
          "2" : {
            "value" : 18.0
          }
        },
        {
          "key" : "广云台",
          "doc_count" : 1,
          "2" : {
            "value" : 25.0
          }
        }
      ]
    }
  }
}