1. 前言
針對nginx的訪問日誌需要進行一個近實時的監控,以便統計用戶的訪問情況,包括用戶的請求IP,請求數據等。因爲服務器是和負載均衡用的是阿里雲的,阿里雲本身也有提供針對負載均衡的日誌統計功能,但是作爲一個通用日誌其是有嚴重缺陷的:
- 無法收集用戶自定義的請求頭(我們的實際情況是通過一個自定義的請求頭來做用戶的身份識別,請求頭有用戶的唯一識別信息)
- 無法獲取到用戶的請求數據($request_body) (很多時候,當出現異常訪問的時候,是需要通過查詢用戶的訪問數據的,特別像搜索這種業務模塊時)
由於存在上面比較嚴重的缺點,基本上阿里雲的日誌對我來說就有點雞肋(食之無味,棄之可惜,你不得不同意他除了上面兩點缺點外,日誌的其他方面都是做得不錯的)。因此我決定自己動手搭建符合自己業務的ELK日誌服務。
之前我是有搭建過ELK的經驗的,但是沒在生產環境試過,起初在搭建的時候我考慮到可能會有以下幾個問題:
- Elasticsearch對內存的佔用情況
- 收集nginx訪問日誌,相當於需要打開nginx的access日誌開關,硬盤的讀寫情況,硬盤的佔用,會不會影響到用戶。
- 日誌收集對應用服務器的影響
我現在還是在一兩臺服務器開啓了日誌收集,做一個灰度測試。
2. Elasticsearch搭建
2.1 Elasticsearch下載安裝
Elasticsearch的安裝比較簡單,直接下載解壓縮即可
- 官網地址:https://www.elastic.co/cn/downloads/elasticsearch
### 下載
wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.4.2-linux-x86_64.tar.gz
### 解壓縮:
tar -zxvf elasticsearch-7.4.2-linux-x86_64.tar.gz
2.2 Elasticsearch配置
1. 修改cluster-name和node-name
cluster.name: my-application
node.name: node-1
node.master: true
node.data: true
2. 修改網絡相關
network.host: 0.0.0.0 ## 0.0.0.0允許所有的ip,這裏我是爲了方便,後面可以根據實際情況填寫
http.port: 9200 ## default
transport.tcp.port: 9300 ## defalut
3. 修改head插件的跨域問題
# 以下的兩個配置是爲了解決跨域問題,在head插件訪問es的時候報跨域了
http.cors.enabled: true
http.cors.allow-origin: "*"
4. 發現配置
# Pass an initial list of hosts to perform discovery when this node is started:
# The default list of hosts is ["127.0.0.1", "[::1]"]
discovery.seed_hosts: ["192.168.1.57"] ## 可被發現的節點IP集合
cluster.initial_master_nodes: ["node-1", "node-2"]
5. 解決es啓動時的報錯問題,.ElasticSearch集羣啓動錯誤,錯誤的原因是:因爲Centos6不支持SecComp,而ES默認bootstrap.system_call_filter爲true進行檢測,所以導致檢測失敗
bootstrap.memory_lock: false
bootstrap.system_call_filter: false
2.3 問題
1. 外網訪問不了9200的Elasticsearch?
- 配置防火牆端口
- 修改elasticsearch.yml,主要是要修改host(我改成0.0.0.0,方便調試,後期穩定之後會再修改)
2. 啓動報錯
- vm.max_map_count過小,java虛擬機的virtual memory過小
在/etc/sysctl.conf最後加入一行vm.max_map_count=262144數量根據實際情況定
- max user threads過小(root和dves兩個用戶的max user threads不一樣)
針對這個問題,可以通過
ulimit -a
查看目前可支持的max user threads
修改 max user process
Linux系統爲每一個用戶都設置了一個最大進程數,這個特性可以讓我們控制服務器上現有用戶可以創建的進程數量
- 修改方式1:
修改
/etc/security/limits.conf
文件,添加以下配置
## 針對所有用戶的可開啓最大線程數爲4096
* soft nproc 4096
* hard nproc 4096
- 修改方式2:
修改
/etc/security/limits.d/90-nproc.conf
文件,添加以下配置(先把其他默認的給註釋掉):
* soft nproc 4096 ## 非root用戶配置爲4096,這個對es來說夠用了
root soft nproc unlimited ## root用戶配置最大 128354
針對這兩種修改方式解釋下,我是在
/etc/security/limits.d/90-nproc.conf
這個文件下修改的。對於max user process
的配置,Linux系統默認先讀取/etc/security/limits.conf
中的信息,如果/etc/security/limits.d/
目錄下還有配置文件的話,也會依次遍歷讀取,最終/etc/security/limits.d/
的配置會覆蓋/etc/security/limits.conf
的相同配置信息
3. FileBeat搭建
3.1 Filebeat下載安裝
curl -L -O https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-7.4.2-x86_64.rpm
rpm -vi filebeat-7.4.2-x86_64.rpm
安裝完成,
filebeat
命令已配置到service服務中,配置文件在/etc/filebeat
,日誌文件在/var/log/filebeat/filebeat
,注意,每次重啓filebeat的時候,該日誌文件都會重新生成,這個時候若如果你是通過tail
命令來觀察的,需要結束當前的tail
命令再重複執行一次tail
命令tail -900f filebeat
3.2 Filebeat收集nginx訪問日誌
3.2.1 nginx的訪問日誌格式及配置
nginx訪問日誌是如何配置的,我這裏不細說,我只是說下我是如何配置的,如果對nginx的訪問日誌配置不熟悉的,可以查看這個:ngx_http_log_module
1. 日誌格式
因爲我們最終要通過Kibana進行展示的,我們需要對訪問日誌的每個字段都進行解析,所以我們就需要確定nginx的日誌是json格式的
## 這個是寫在http模塊的
log_format json_main '{"@times" : "$time_iso8601",' ## 該time作爲Kibana的date類型,在創建Kibana的index_pattern時作爲時間篩選的key
'"userAgent" : "$http_user_agent",'
'"remoteAddr" : "$remote_addr", '
'"realIp" : "$http_x_forwarded_for", '
'"requestUri" : "$request", '
'"status" : "$status", '
'"referer" : "$http_referer", '
'"requestTime" : "$request_time", '
'"requestBody" : "$request_body", ' ## 請求數據
'"domoCustom" : "$http_domo_custom"}'; ## 自定義請求頭
2. 打開請求日誌
對map模塊有興趣的可以查看這裏:ngx_http_map_module
## 這個在server模塊
access_log /opt/nginx/logs/requestLog.log json_main if=$myUri;
## /opt/nginx/logs/requestLog.log:這個是日誌收集的地方,若該日誌不存在,在reload或重啓nginx的是,會自動創建
## json_main:`log_format`定義的日誌格式名
## if=$myUri:一個判斷,如果`$myUri`爲0或者空字符串,則該日誌不收集。這個的作用可以過濾掉一些我們不想收集的日誌信息,我是根據請求頭來設置的。
## 這個寫在http模塊,使用map這個內置模塊,可以通過判斷`$request`這個
## 內置變量是否符合下面的判斷,然後把0或1賦值給`$myUri`.
## 這裏我的用法就是,如果是OPTIONS請求爲0不記錄日誌信息,
## 如果是`/api/qiqyu`這個第三方的請求也爲0不記錄日誌信息
## 其他的`/api/`正則的則爲1記錄日誌信息
## 非上面集中情況的一律爲0不記錄相應的日誌信息
map $request $myUri {
~OPTIONS 0;
~/api/qiyu/ 0;
~/api/ 1;
default 0;
}
reload
nginx之後就能在/opt/nginx/logs/requestLog.log
這個文件夾下看到相應的日誌了
3.3 Filebeat配置
3.3.1 基本配置文件配置
vim /etc/filebeat/filebeat.yml
1. Filebeat inputs
- type: log
# Change to true to enable this input configuration.
enabled: true ## 打開開關
exclude_lines: ['^DBG'] ## 忽略的行
# Paths that should be crawled and fetched. Glob based paths.
paths:
#- /var/log/*.log
- /opt/nginx/logs/requestLog.log ## 配置從哪裏按行讀取日誌
#- c:\programdata\elasticsearch\logs\*
## 如果要讓filebeat輸入json格式的數據,除了nginx的日誌本身是json格式
## 的外,還必須在filebeat中配置一下設置
json.keys_under_root: true
json.overwrite_keys: true
json.add_error_key: true
json.message_key: message ## 如果message在啓動的時候報錯,可以改成"json"(帶雙引號)
2. Elasticsearch template setting
setup.ilm.enabled: false ## 這個必須要配置成false,否則自定義index就不會生效
setup.template.enabled: false ## 關掉filebeat默認的es模板
setup.template.overwrite: true ## 表示我們自定義的模板和index會覆蓋默認的
setup.template.fields: "/etc/filebeat/self/fields.yml" ## 自定義的fields.yml,這個文件不熟悉的話就別瞎折騰了,我直接從原來的copy了一份而已
setup.template.name: "nginx-access-log" ## 模板名,這個和後面要創建的es模板名保持一致
setup.template.pattern: "nginx-access-log-*" ## 模板對應的pattern,也是對應的index,這些都是有規律的。因爲你模板名是`nginx-access-log`,則你接下來的自定義索引名就最好是`nginx-access-log-%{[agent.version]}-%{+yyyy.MM.dd}`,pattern的正則就是`nginx-access-log-*`
3. Dashboards(這個配置可不配,主要是看你的filebeat是否安裝了默認看板,這個安裝後會生效的前提是:你必須使用filebeat的默認模板,默認索引,一旦你執行了上面第二步,這裏的配置就不會生效)
setup.dashboards.enabled: true
setup.dashboards.index: "nginx-access-log-*"
4. Kibana
setup.kibana:
host: "192.168.1.57:5601"
# Kibana Host
# Scheme and port can be left out and will be set to the default (http and 5601)
# In case you specify and additional path, the scheme is required: http://localhost:5601/path
# IPv6 addresses should always be defined as: https://[2001:db8::1]:5601
#host: "localhost:5601"
5. Outputs(Elasticsearch)
#-------------------------- Elasticsearch output ------------------------------
output.elasticsearch:
# Array of hosts to connect to.
hosts: ["192.168.1.57:9200"]
## 這裏的index的前綴要和上面第二步配置的模板名保持一致,而且,index的名字中不能包含大寫字母,這是我的教訓來着
index: "nginx-access-log-%{[agent.version]}-%{+yyyy.MM.dd}"
# Optional protocol and basic auth credentials.
#protocol: "https"
#username: "elastic"
#password: "changeme"
3.3.2 啓用nginx模塊
其實對於filebeat中,它提供了多個模塊,比如nginx,mysql等,這些能夠讓我們快速地上手filebeat。但是我一直搞不懂這些模塊的用途是什麼,明明我可以在
filebeat.yml
中配置的。nginx的模塊格式如下:
我雖然安裝了該模塊,但是該模塊我是關掉的,一方面是我對該模塊的理解不夠,我不啓用該模塊也能正常收集日誌,另一方面,開啓該模塊的時候,在收集日誌的時候會不定時地報錯
Provided Grok expressions do not match field value:
# Module: nginx
# Docs: https://www.elastic.co/guide/en/beats/filebeat/7.4/filebeat-module-nginx.html
- module: nginx
# Access logs
access:
enabled: false
var.paths: ["/opt/nginx/logs/requestLog.log"]
# Set custom paths for the log files. If left empty,
# Filebeat will choose the paths depending on your OS.
#var.paths:
# Error logs
error:
enabled: false
# Set custom paths for the log files. If left empty,
# Filebeat will choose the paths depending on your OS.
#var.paths:
安裝過程
在配置好
filebeat.ymt
後執行如下命令:
filebeat modules enable nginx
3.3.3 啓動filebeat模塊
filebeat setup
service filebeat start
4. Kibana搭建
4.1 Kibana下載安裝
注意:如果es是7版本,那麼kibana的版本也要是7,兩者之間的大版本要互相對應
wget https://artifacts.elastic.co/downloads/kibana/kibana-7.4.2-linux-x86_64.tar.gz
## 解壓縮後即可使用
tar -zxvf kibana-7.4.2-linux-x86_64.tar.gz
4.2 Kibana配置
vim kibana.yml
## 端口號,默認5601
#server.port: 5601
## 指定kibana指定的IP
server.host: "192.168.1.57"
## 因爲我的kibana和Elasticsearch是在同一臺機器上的,所以我不需要去配置es的IP和端口等信息,kibana會自動在本機尋找
#elasticsearch.hosts: ["http://localhost:9200"]
4.2.1 自定義Elasticsearch的template
這個
template_name
和index_patterns
要和前面我們在Elasticsearch中配置的一致。除了template_name
和index_patterns
要修改外,還有一個要修改的地方就是mapping
,這個因人而異,我覺得默認的mapping輸出太多我不需要用到的東西,所以纔去簡化的,其餘的都保持默認。新建好template
後重啓filebeat即可。重啓filebeat之前建議把nginx的訪問日誌也清空掉,免得有髒數據影響
mapping信息:
{
"_meta": {
"beat": "filebeat",
"version": "7.4.2"
},
"dynamic_templates": [
{
"labels": {
"path_match": "labels.*",
"mapping": {
"type": "keyword"
},
"match_mapping_type": "string"
}
},
{
"container.labels": {
"path_match": "container.labels.*",
"mapping": {
"type": "keyword"
},
"match_mapping_type": "string"
}
},
{
"dns.answers": {
"path_match": "dns.answers.*",
"mapping": {
"type": "keyword"
},
"match_mapping_type": "string"
}
},
{
"fields": {
"path_match": "fields.*",
"mapping": {
"type": "keyword"
},
"match_mapping_type": "string"
}
},
{
"docker.container.labels": {
"path_match": "docker.container.labels.*",
"mapping": {
"type": "keyword"
},
"match_mapping_type": "string"
}
},
{
"kubernetes.labels.*": {
"path_match": "kubernetes.labels.*",
"mapping": {
"type": "keyword"
},
"match_mapping_type": "*"
}
},
{
"kubernetes.annotations.*": {
"path_match": "kubernetes.annotations.*",
"mapping": {
"type": "keyword"
},
"match_mapping_type": "*"
}
},
{
"docker.attrs": {
"path_match": "docker.attrs.*",
"mapping": {
"type": "keyword"
},
"match_mapping_type": "string"
}
},
{
"cef.extensions": {
"path_match": "cef.extensions.*",
"mapping": {
"type": "keyword"
},
"match_mapping_type": "string"
}
},
{
"kibana.log.meta": {
"path_match": "kibana.log.meta.*",
"mapping": {
"type": "keyword"
},
"match_mapping_type": "string"
}
},
{
"strings_as_keyword": {
"mapping": {
"ignore_above": 1024,
"type": "keyword"
},
"match_mapping_type": "string"
}
}
],
## 這裏是指是否要指定一個key作爲date類型來進行時間篩選,如果不爲true,
## 後面你就無法根據@times進行時間篩選
"date_detection": true
}
4.2.2 devtool工具查詢
在未建立kibana的
index_pattern
之前,我們是無法在discover
發現面板上去查看自己的數據的,這個時候我們並不着急去創建index_pattern
,我們要先驗證一下日式數據是否已成功收集。打開kibana的dev tool
1. 查看目前es中的索引,確認我們的自定義索引是否已生成
get _cat/indices?v
這個索引出現了表明索引已自動生成了
2. 查看該索引目前的數據,是否符合我們預期
GET /nginx-access-log-7.4.2-2019.11.18/_search
數據結果是json格式的,而且我們需要的數據都有,符合我們的預期。
4.2.3 自定義index_pattern
在上面的步驟確認好之後,就可以創建kibana的
index_pattern
4.2.3 Discover功能
選擇剛我們創建的
index_pattern
就可以看到和我們index_pattern格式相對應的數據了
4.2.4 Visualize功能
新建一個可視化的圖表,它裏面幫我們內嵌了多個圖表,但數據源都不是我們自定義的數據,可以參考,但是沒多大意義,我們可以自己創建。
4.2.5 Dashboard功能
多個 Visualize放在一起就是一個面板,這裏不多贅述,沒什麼技術含量
5. 參考鏈接
開始使用Filebeat
快速開始Elasticsearch
Es的官網教學
自定義index template
Filebeat自定義index的一個坑
Filebeat模塊與配置
Linux修改max user threads
ElkStack運維手冊
Es中文手冊
nginx中文地址
6. 總結
搭建這個簡單的ELK還是花費了我一些時間的,一方面主要是我太久沒接觸ES,另一方面我對filebeat和kibana一直都是一知半解的狀態。在搭建的過程中,沒有像我上面寫得那麼順利,一路磕磕碰碰,遇到很多問題,最後通過查詢大量資料加上自己的理解才終於搞定。