0%

ELK日志分析系统

ELK日志分析系统

概述

日志简化分析的管理工具,由Elasticsearch(ES)、Logstash、Kibana三个开源工具组成。

Elasticsearch(nosql非关数据库):存储功能和索引

Logstash(收集日志):到应用服务器上拿取log,并进行格式转换后输出到es中。

Kibana(展示工具):将es内的数据在浏览器展示出来,通过UI界面展示(可以根据自己的需求对日志进行处理,方便查阅读取)。

elk常用几种架构

1
2
3
4
5
6
7
8
Elasticsearch + Logstash + Kibana
这是一种最简单的架构。这种架构,通过logstash收集日志,Elasticsearch分析日志,然后在Kibana(web界面)中展示。这种架构虽然是官网介绍里的方式,但是往往在生产中很少使用。

Elasticsearch + Logstash + filebeat + Kibana
与上一种架构相比,这种架构增加了一个filebeat模块。filebeat是一个轻量的日志收集代理,用来部署在客户端,优势是消耗非常少的资源(较logstash), 所以生产中,往往会采取这种架构方式,但是这种架构有一个缺点,当logstash出现故障, 会造成日志的丢失。

Elasticsearch + Logstash + filebeat + redis(也可以是其他中间件,比如kafka(集群化)) + Kibana
这种架构是上面那个架构的完善版,通过增加中间件,来避免数据的丢失。当Logstash出现故障,日志还是存在中间件中,当Logstash再次启动,则会读取中间件中积压的日志。目前我司使用的就是这种架构,我个人也比较推荐这种方式。

日志处理步骤(工作流程)

Logstash收集服务器的日志,并将日志进行集中化管理
将日志格式化(Logstash) 并存放到ElasticSearch集群中
对格式化后的数据进行索引和存储( Elasticsearch)
Kibana则从Es集群中查询数据生成图表,再返回给browsers

在这里插入图片描述

Elasticsearch概述

Elasticsearch 是一个非常强大的搜索引擎,同时也是非关系型数据库。

关系型数据库与Elasticsearch对应的关系

1
2
3
4
5
mysql	                  Elasticsearch
database数据库 index索引
table表 type类型
row行 document文档
column列 属性

1.接近实时(NRT)
elasticsearch是一个接近实时的搜索平台,这意味着,从索引一个文档直到这个文档能够被搜索到有一个轻微的延迟(通常是1秒)

2.集群(cluster)
集群有一个唯一性标示的名字,默认是elasticsearch;
集群就是由一个或多个节点组织在一起,它们共同持有整个的数据,并一起提供索引和搜索功能;
其中一个节点为主节点,这个主节点是可以通过选举产生的,并提供跨节点的联合索引和搜索的功能;
集群名字很重要,每个节点是基于集群名字加入到其集群中的

3.节点(node)
节点就是一台单一的服务器,是集群的一部分,存储数据并参与集群的索引和搜索功能;
像集群一样,节点也是通过名字来标识,默认是在节点启动时随机分配的字符名,可以自己定义;
名字在集群中用于识别服务器对应的节点。

4.索引(index)
一个索引就是一个拥有几分相似特征的文档的集合;
一个索引由一个名字来标识(必须全部是小写字母的),并且当我们要对对应于这个索引中的文档进行索引、搜索、更新和删除的时候,都要使用到这个名字。

5.类型(type)
在一个索引中,你可以定义一种或多种类型。一个类型是你的索引的一个逻辑上的分类/分区;通常,会为具有一组共同字段的文档定义一个类型

6.文档(document)
文档以JSON ( Javascript object Notation)格式来表示,而JSON是一个到处存在的互联网数据交互格式。
虽然一个文档在物理上位于一个索引中,实际上一个文档必须在一个索引内被索引和分配一个类型。

7.分片(shards)
即es作为搜索引擎快的原因:
在实际情况下,索引存储的数据可能超过单个节点的硬件限制。如一个10亿文档需1TB空间可能不适合存储在单个节点的磁盘上,或者从单个节点搜索请求太慢了。为了解决这个问题,elasticsearch提供将索引分成多个分片的功能。当在创建索引时,可以定义想要分片的数量。每一个分片就是一个全功能的独立的索引,可以位于集群中任何节点上。
分片的好处:
①:水平分割扩展,增大存储量
②:分布式并行跨分片操作,提高性能和吞吐量

8.副本(replicas)
为了防止网络问题等其它问题造成数据丢失,需要有一个故障切换机制,为此,elasticsearch让我们将索引分片复制一份或多份,称之为分片副本或副本。
副本也有两个最主要原因:
①:高可用性,以应对分片或者节点故障,需要在不同的节点上
②:提高性能, 增大吞吐量,搜索可以并行在所有副本上执行

总之,每个索引可以被分成多个分片。一个索引也可以被复制0次( 意思是没有复制)或多次。一旦复制了,每个索引就有了主分片(作为复制源的原来的分片)和复制分片(主分片的拷贝)之别。分片和副本的数量可以在索引创建的时候指定。在索引创建之后,你可以在任何时候动态地改变副本的数量,但是你事后不能改变分片的数量。

logstash概述

Logstash由JRuby语言编写,基于消息(message-based) 的简单架构,并运行在Java虚拟机(JVM)上。LogStash可配置单一的代理端(agent) 与其它开源软件结合,以实现不同的功能。
Logstash的理念很简单,它只做3件事情:Collect:数据输入、Enrich:数据加工,1如过滤,改等、Transport:数据输出( 被其他模块进行调用)
1.logStash的主要组件
①:Shipper(日志收集者):负责监控本地日志文件的变化,及时把日志文件的最新内容收集起来。通常,远程代理端(agent)只需要运行这个组件即可;
②:Indexer(日志存储者):负责接收日志并写入到本地文件。
③:Broker(日志Hub):负责连接多个Shipper和多个Indexer
④:Search and Storage(搜索和存储器):允许对事件进行搜索和存储;
⑤:Web Interface(web界面端):基于Web的展示界面

LogStash主机分类
①:代理主机(agent host) :作为事件的传递者(shipper),将各种日志数据发送至中心主机;只需运行Logstash代理( agent)
②:程序中心主机(central host) :可运行包括中间转发器(Broker) 、索引器(Indexer) 、搜索和存储器( Search and
Storage )、web界面端(web Interface)在内的各个组件,以实现对日志数据的接收、处理和存储

logstash基本架流程架构

logstash有三种方法:input,filter ,output。如需对数据进行额外处理,filter可省略。

在这里插入图片描述

Input(输入):采集各种样式,大小和相关来源数据,从各个服务器中收集数据。

1
2
数据往往以各种各样的形式,或分散或集中地存在于很多系统中。Logstash 支持各种输入选择 ,可以在同一时间从众多常用来源捕捉事件。能够以连续的流式传输方式,轻松地从您的日志、指标、Web 应用、数据存储以及各种 AWS 服务采集数据。
必选项,负责产生事件(Inputs generate events),常用input数据源:File、syslog、redis、beats(如:Filebeats)

在这里插入图片描述

Filter(过滤器)

1
2
3
4
5
6
7
用于在将event通过output发出之前对其实现某些处理功能。grok。
grok:用于分析结构化文本数据。目前 是logstash中将非结构化数据日志数据转化为结构化的可查询数据的不二之选
[root@node1 ~]# rpm -ql logstash | grep "patterns$" grok定义模式结构化的位置。
/usr/share/logstash/vendor/bundle/jruby/2.5.0/gems/logstash-patterns-core-4.1.2/patterns/grok-patterns
/usr/share/logstash/vendor/bundle/jruby/2.5.0/gems/logstash-patterns-core-4.1.2/patterns/mcollective-patterns

可选,负责数据处理与转换(filters modify them),常用:grok、mutate、drop、clone、geoip

Output(输出):

1
2
将我们过滤出的数据保存到那些数据库和相关存储中。
必选项,负责数据输出(outputs ship them elsewhere),常用output输出源:elasticsearch、file、graphite、statsd

在这里插入图片描述

流程图:

image-20220728160639003

kibana概述

1.简介
Kibana是一个针对Elasticsearch的开源分析及可视化平台,用来搜索、查看交互存储在Elasticsearch索引中的数据。使用Kibana,可以通过各种图表进行高级数据分析及展示。它操作简单,基于浏览器的用户界面可以快速创建仪表板( dashboard)实时显示Elasticsearch查询动态。设置Kibana非常简单。无需编写代码,几分钟内就可以完成Kibana安装并启动Elasticsearch索引监测。

2.主要功能
①:Elasticsearch无缝之集成:Kibana架构为Elasticsearch定制, 可以将任何结构化和非结构化数据加入Elasticsearch索引;Kibana还充分利用了Elasticsearch强大的搜索和分析功能。
②:整合你的数据:Kibana能够更好地处理海量数据,并据此创建柱形图、折线图、散点图、直方图、饼图和地图。
③:复杂数据分析:Kibana提升了Elasticsearch分析能力,能够更加智能地分析数据,执行数学转换并且根据要求对数据切割分块。
④:让更多团队成员受益:强大的数据库可视化接口让各业务岗位都能够从数据集合受益。
⑤:接口灵活,分享更容易:使用Kibana可以更加方便地创建、保存、分享数据,并将可视化数据快速交流。
⑥:配置简单:Kibana的配置和启用非常简单,用户体验非常友好。Kibana自带Web服务器,可以快速启动运行。
⑦: 可视化多数据源:Kibana可以非常方便地把来自Logstash、 ES-Hadoop、 Beats或第三方技术的数据整合到Elasticsearch,支持的第三方技术包括Apache Flume、 Fluentd等。
⑧:简单数据导出:Kibana可以方便地导出感兴趣的数据,与其它数据集合并融合后快速建模分析,发现新结果。

部署

这里实验环境在一台服务器上,部署elasticsearch,logstash,kibana。logstash监控该服务器的/var/log/message和/var/log/secure日志。

安装jdk

1
2
3
4
5
6
7
yum search jdk #先看一下yum源没有jdk,如果没有还需要解决yum源
yum install java-1.8.0-openjdk
安装完看一下jdk环境,java环境变量可以了就行了。如果非centos(redhat)或者安装不了可以去官方下载二进制包进行部署。
[root@localhost ~]# java -version
openjdk version "1.8.0_161"
OpenJDK Runtime Environment (build 1.8.0_161-b14)
OpenJDK 64-Bit Server VM (build 25.161-b14, mixed mode)

这里采用yum方式部署,如需二进制包部署,可以在官网下载:https://www.elastic.co/cn/downloads/

Elasticsearch和kibana和logstash都可以直接在上面下载。

yum源配置

1
2
3
4
5
6
7
cat > /etc/yum.repos.d/elk.repo << EOF  
[ELK]
name=ELK-Elasticstack
baseurl=https://mirrors.tuna.tsinghua.edu.cn/elasticstack/yum/elastic-7.x/
gpgcheck=0
enabled=1
EOF
1
2
yum makecache
yum -y install elasticsearch-7.4.0 kibana-7.4.0 logstash-7.4.0 #这个源下面三个服务包都有,直接通过yum安装三个服务。

Elasticsearch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
mv /etc/elasticsearch/elasticsearch.yml /etc/elasticsearch/elasticsearch.yml.backup
cat > /etc/elasticsearch/elasticsearch.yml << EOF
cluster.name: my-application
node.name: node-1
path.data: /elk/data
path.logs: /elk/logs
cluster.initial_master_nodes: ["node-1"]
network.host: 0.0.0.0
http.port: 9200
http.cors.enabled: true ##开启跨域访问支持,默认为false
http.cors.allow-origin: "*" ##跨域访问允许的域名地址 http.cors.enabled和http.cors.allow-origin如果没配置下面的es-head管理界面连不上ES。
EOF

mkdir -p /elk/data /elk/logs #elk的数据文件目录和日志文件目录
chown -R elasticsearch.elasticsearch /elk/ && chmod -R 755 /elk #默认yum安装后是以elasticesearch作为启动用户的,如果数据目录和日志目录elastic的启动用户没有写权限是无法成功启动的。
systemctl enable elasticsearch.service && systemctl start elasticsearch

访问测试

image-20220727164858011

安装elasticsearch-head插件

该插件用来查看和管理elasticsearch

github:https://github.com/mobz/elasticsearch-head

1
2
3
4
5
6
7
8
9
yum -y install git #先解决git环境
wget https://nodejs.org/dist/v14.15.4/node-v14.15.4-linux-x64.tar.xz #解决nodejs环境,head是nodejs的代码
ln -s /root/node-v14.15.4-linux-x64/bin/node /usr/local/bin/node
ln -s /root/node-v14.15.4-linux-x64/bin/npm /usr/local/bin/npm

git clone https://github.com/mobz/elasticsearch-head.git
cd elasticsearch-head/
npm install --registry https://registry.npm.taobao.org -g
npm run start & #后台启动

访问image-20220727170117369

logstash

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
cat > /etc/logstash/conf.d/system.conf << EOF
input {
file {
path => "/var/log/messages" #日志路径
type => "systemlog" #事件的唯一类型
start_position => "beginning" #第一次收集日志的位置
stat_interval => "3" #日志收集的间隔时间
}
file {
path => "/var/log/secure"
type => "securelog"
start_position => "beginning"
stat_interval => "3"
}
}

output {
if [type] == "systemlog" {
elasticsearch {
hosts => ["127.0.0.1:9200"]
index => "system-log-%{+YYYY.MM.dd}"
}}
if [type] == "securelog" {
elasticsearch {
hosts => ["127.0.0.1:9200"]
index => "secury-log-%{+YYYY.MM.dd}"
}}
}
EOF

systemctl enable logstash && systemctl start logstash
chmod 644 /var/log/messages /var/log/secure #logstash要监控的文件至少logstash的启动用户需要有读权限

进入ES-head的索引页面,如果索引创建成功说明这里配置已经没问题了。(后面kibana在读取日志的时候实质上就是读取elasticsearch的索引)

image-20220727173001821

kibana

1
2
3
4
5
6
7
8
9
10
mv /etc/kibana/kibana.yml /etc/kibana/kibana.yml.backup

cat > /etc/kibana/kibana.yml << EOF
server.port: 5601
server.host: "0.0.0.0"
kibana.index: ".kibana"
elasticsearch.hosts: ["http://127.0.0.1:9200"]
i18n.locale: "zh-CN"
server.name: "my-application"
EOF

访问后进入management-》stack management-》kibana-》索引模式—》创建索引

image-20220727173844859

image-20220727173918164

image-20220727173927993

这里创建两个索引分别看secury和system的日志,由于上面logstash在output时使用了变量%{+YYYY.MM.dd},所以这里后面的名称是日期,且每天都会生成一个日志,索引我们这里创建secury*和system* 两个索引,*号表示模糊匹配(匹配其日期,这里如果不进行匹配直接写索引例如这里直接写索引名写全secury-log-2022.07.27那么明天logstash创建一个secury-log-2022.07.28的日志索引后就没办法看到日志了)。

image-20220727174018947

image-20220727174216709

查看日志

进入Analytics下面的discover就能看到日志了

image-20220727174452882

image-20220727174528421

filebeat

filebeat简介

1
2
3
4
5
6
7
8
Filebeat 是使用 Golang 实现的轻量型日志采集器,也是 Elasticsearch stack 里面的一员。本质上是一个 agent ,可以安装在各个节点上,根据配置读取对应位置的日志,并上报到相应的地方去。

Filebeat 的可靠性很强,可以保证日志 At least once 的上报,同时也考虑了日志搜集中的各类问题,例如日志断点续读、文件名更改、日志 Truncated 等。

Filebeat 并不依赖于 ElasticSearch,可以单独存在。我们可以单独使用Filebeat进行日志的上报和搜集。filebeat 内置了常用的 Output 组件, 例如 kafka、ElasticSearch、redis 等,出于调试考虑,也可以输出到 console 和 file 。我们可以利用现有的 Output 组件,将日志进行上报。

当然,我们也可以自定义 Output 组件,让 Filebeat 将日志转发到我们想要的地方。

filebeat和logstash

1
2
3
4
5
6
7
因为logstash是jvm跑的,资源消耗比较大,所以后来作者又用golang写了一个功能较少但是资源消耗也小的轻量级的logstash-forwarder。不过作者只是一个人,加入elastic公司以后,因为es公司本身还收购了另一个开源项目packetbeat,而这个项目专门就是用golang的,有整个团队,所以es公司干脆把logstash-forwarder的开发工作也合并到同一个golang团队来搞,于是新的项目就叫filebeat了。

Filebeat是当今最好的日志文件发送器之一 - 它轻量级,支持SSL和TLS加密,支持背压,具有良好的内置恢复机制,并且非常可靠。但是,在大多数情况下,它不能使用用于日志增强的过滤器将日志转换为易于分析的结构化日志消息。这就是Logstash所扮演的角色。

Logstash充当聚合器 - 在将数据推送到管道之前从各种源中提取数据,通常是Elasticsearch,但也可以是大型生产环境中的缓冲组件。值得一提的是,最新版本的Logstash还包括在磁盘上存储消息队列时对持久队列的支持。

logstash 和filebeat都具有日志收集功能,但filebeat更为轻量级,这里之所以采用filebeat采用日志主要还是一个原因:轻量化。因其logstash太重量级了,不适合在每台服务器都装一个并用其来采集日志(jvm动辄占用1-2G)。而filebeat可能就占用10M左右的内存,这才是根本原因。而logstash 具有filter功能,能过滤分析日志。一般生产结构都是filebeat采集日志,然后发送到消息队列,redis,kafaka。然后logstash去获取,利用filter功能过滤分析,然后存储到elasticsearch中。一般生产的架构为:filebeat(采集日志)->消息队列->logstash(处理日志)->elasticsearch(数据库)->kibana(展示数据)

filebeat与beats

说到filebeat,必须要引出beats。首先filebeat是Beats中的一员。

eats在是一个轻量级日志采集器,其实Beats家族有6个成员,早期的ELK架构中使用Logstash收集、解析日志,但是Logstash对内存、cpu、io等资源消耗比较高。相比Logstash,Beats所占系统的CPU和内存几乎可以忽略不计。

目前Beats包含六种工具:

  • Packetbeat:网络数据(收集网络流量数据)
  • Metricbeat:指标(收集系统、进程和文件系统级别的CPU和内存使用情况等数据)
  • Filebeat:日志文件(收集文件数据)
  • Winlogbeat:windows事件日志(收集Windows事件日志数据)
  • Auditbeat:审计数据(收集审计日志)
  • Heartbeat:运行时间监控(收集系统运行时的数据)

工作原理

1
2
3
4
5
6
7
8
9
Filebeat 由两个主要组件组成:harvester 和 prospector。

采集器 harvester 的主要职责是读取单个文件的内容。读取每个文件,并将内容发送到 the output。 每个文件启动一个 harvester,harvester 负责打开和关闭文件,这意味着在运行时文件描述符保持打开状态。如果文件在读取时被删除或重命名,Filebeat 将继续读取文件。

查找器 prospector 的主要职责是管理 harvester 并找到所有要读取的文件来源。如果输入类型为日志,则查找器将查找路径匹配的所有文件,并为每个文件启动一个 harvester。每个 prospector 都在自己的 Go 协程中运行。

注:Filebeat prospector只能读取本地文件, 没有功能可以连接到远程主机来读取存储的文件或日志。这点和logstash不同,logstash的输入源较多,如filebeat,redis。

由以上两个组件一起工作来读取文件(tail file)并将事件数据发送到您指定的输出。

img

部署(elk+filebeat)

这里部署前要先装好elk环境。这里的环境elk和filebeat都是在一台服务器上,真正生产使用时可以分开。

1
2
3
4
配置了上面elastic的yum源可以直接yum部署
yum -y install filebeat-7.4.0
如果没有配置yum源或其他发行版:
https://www.elastic.co/cn/downloads/past-releases#filebeat #官网下载二进制包

这里以输出为logstash和输出为elasticsearch为例子(filebeat可以输出到logstash让logstash作为日志过滤然后再到es上面,也可以直接输出到es。)

logstash作为输出

filebeat.yml配置

1
2
3
4
5
6
7
8
9
filebeat.inputs:
- type: log
id: my-filestream-id
enabled: true # 启用
paths:
- /var/log/*.log #读取文件的路径
output.logstash:
# The Logstash hosts
hosts: ["127.0.0.1:5044"] #这个5044就是logstash要接受数据的端口

logstash配置文件

/etc/logstash/conf.d/system.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
input {
beats {
#数据来自 filebeat
port => "5044"
codec => "json"
id => "filebeat"

}
}
filter {
#grok过滤器插件,可以将非结构化日志数据解析为结构化和可查询的内容
grok {
match => { "message" => "%{COMBINEDAPACHELOG}"}
}
#geoip插件查找IP地址,从地址中获取地理位置信息,然后将该位置信息添加到日志中
geoip {
source => "clientip"
}
}
output {
stdout { codec => rubydebug }
#数据输出到es保存
elasticsearch {
hosts => [ "IP:9200" ]
index => "%{[@metadata][beat]}-%{[@metadata][version]}-%{+YYYY.MM.dd}"
}
}

配置好了以后启动这两个服务,当其在elsaticsearch生成索引和数据后表示logstash和filebeat配置正确并能正常输入输出数据了。

image-20220728174913972

kibana设置索引模式:索引模式里面添加一个:filebeat* 即可。

测试

由于这里filebeat输入的是/var/log下面所有日志文件,这里echo字符到一个log文件然后再kibana上面查看

1
[root@localhost elasticsearch-head]# echo test > /var/log/test.log

image-20220728175349798

elasticsearch作为输出

  • 这里输出到es有两种情况:默认索引名和自定义索引名。如果是默认索引名的话版本至少为7.4.0以上,这里我测试7.17.0无法生成默认的索引名。
  • 这里只有输出到es可以定义索引名,因为输出到logstash的话一般是由logstash定义es索引名并由logstash来输出到es。
  • 自定义索引setup.ilm.enabled,setup.template.name,setup.template.pattern,index这几个配置必须要定义。
filebeat.yml (自定义索引名)
1
2
3
4
5
6
7
8
9
10
11
filebeat.inputs:
- type: log
enabled: true
paths:
- /var/log/*.log
setup.ilm.enabled: false #修改索引名字,要关闭索引生命周期管理
setup.template.name: "filebeat-127" #模板的名字。默认是filebeat。Filebeat的版本总是跟在名字后面,所以最终的名字是 filebeat-%{[beat.version]}
setup.template.pattern: "filebeat-127*" #模板的模式。默认模式是filebeat-*
output.elasticsearch:
hosts: ["127.0.0.1:9200"]
index: "filebeat-127-%{+yyyy.MM.dd}"

image-20220803151610463

创建索引模式,然后就能看日志了

image-20220729120800297

filebeat.yml(默认索引名)
1
2
3
4
5
6
7
filebeat.inputs:
- type: log
enabled: true
paths:
- /var/log/*.log
output.elasticsearch:
hosts: ["127.0.0.1:9200"]

生成的索引:

image-20220802181014440

filebeat指向logstash集群

加一个loadblancer选项为true,在hosts中加入logstash的所有ip即可。

image-20220729175521756

elk for k8s

  • Elaticsearch:es需要创建svc监听端口,以供logstash-output输出。另外还需要创建pv/pvc用来做持久化存储。
  • logstash:logstash也需要创建svc,以供filebeat-out输出。另外还需要通过configmap将配置文件传入容器(其他方式传入配置文件也可以,保证程序启动可以正常读到配置文件即可)。
  • filebeat:这里filebeat要麻烦一点,还需要rbac授权。整体就是上面说的daemonset部署每个节点,然后hostpath将所有容器日志挂载到容器,容器再监控。这里的rbac权限主要是在kibana展示数据的时候,需要对容器或pod的名称或名称空间进行筛选,就需要在采集数据的时候拿到这些数据,就要用到k8s的授权才行。
  • es:这个创建svc用于web浏览,指定es端口,就行了。最后网页上配置索引模板。

这里用k8s部署elk+filebeat,是部署在k8s上,并使其获取k8s所有容器的日志。思路是es默认配置就行,但需要持久化es数据。然后logstash这里配置文件input监听端口给filebeat的output作为接收端,并filter 做处理后发送到es。filebeat用daemonset让其部署在每个节点下面,将每个节点下面的日志目录(/var/log)用hostpath挂载到filebeat,filebeat通过配置文件input监听/var/log/container/*.log下面所有日志(容器的日志都放在这里)。这里引出讲一下pod的日志放在哪里

pod的日志放在哪里

首先在/var/log/container/下面就能看到该节点上面所有pod的容器日志。

image-20220806110809873

这里以kube-proxy为例子。从上面的软链接来看我们找到 /var/log/pods下面的kube-proxy日志的真实目录:

从下图能看到/var/log/pods是以pod为单位存放日志都是从/var/lib/docker/containers下面的容器日志映射过来的。而/var/lib/docker/containers下面其实又时/var/lib/docker/containers/下面映射过来的,所以真正的容器日志存放位置是/var/lib/docker/containers/容器UID/(容器uid)-json.log。

image-20220806111855760

image-20220806112021108

这里软链接套了两层,关系如下:

1
/var/lib/docker/containers/(docker容器id)-json.log -> /var/log/pods/(pod名称)-(pod UID)/(数字).log -> /var/log/containers/(容器UID)/(容器UID)-json.log

elasticsearh部署

elasticsearch这里需要用到持久化存储,这里我用的是storageclass,如果是storageclass环境直接改storageClassName就可以,如果没有storageclass请手动创建好pv/pvc环境再部署es。

elasticsearch.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: elasticsearch
namespace: es
labels:
k8s-app: elasticsearch
spec:
serviceName: elasticsearch
selector:
matchLabels:
k8s-app: elasticsearch
template:
metadata:
labels:
k8s-app: elasticsearch
spec:
containers:
- image: elasticsearch:7.12.0
name: elasticsearch
resources:
limits:
cpu: 1
memory: 2Gi
requests:
cpu: 0.5
memory: 500Mi
env:
- name: "discovery.type"
value: "single-node"
- name: ES_JAVA_OPTS
value: "-Xms512m -Xmx2g"
ports:
- containerPort: 9200
name: db
protocol: TCP
volumeMounts:
- name: elasticsearch-data
mountPath: /usr/share/elasticsearch/data
volumeClaimTemplates:
- metadata:
name: elasticsearch-data
spec:
storageClassName: "nfs-delete"
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 20Gi

---

apiVersion: v1
kind: Service
metadata:
name: elasticsearch
namespace: halashow
spec:
clusterIP: None
ports:
- port: 9200
protocol: TCP
targetPort: db
selector:
k8s-app: elasticsearch

logstash部署

logstash这里有两个功能:

  • 监听5044端口用于接受filebeat的传输
  • filter过滤数据并发送到es

logstash.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
---
apiVersion: v1
kind: ConfigMap
metadata:
name: logstash-config
namespace: halashow
data:
logstash.conf: |-
input {
beats {
port => 5044
}
}
filter {
if [type] == "k8s-log" {
grok {
match => { "message" => "(%{TIMESTAMP_ISO8601:logdatetime} %{LOGLEVEL:level} %{GREEDYDATA:logmessage})|%{GREEDYDATA:logmessage}" }
remove_field => [ "message" ]
remove_field => [ "agent" ]
remove_field => [ "ecs" ]
remove_field => [ "tags" ]
}
}
}
output {
if [fields][service] == "k8s-log" {
elasticsearch {
hosts => ["elasticsearch:9200"]
index => "k8s-%{[kubernetes][namespace]}-%{+YYYY.MM.dd}" } } } # 以k8s-名称空间加上日期来定义索引名
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: logstash
namespace: halashow
labels:
name: logstash
spec:
replicas: 1
selector:
matchLabels:
name: logstash
template:
metadata:
labels:
app: logstash
name: logstash
spec:
containers:
- name: logstash
image: docker.elastic.co/logstash/logstash:7.12.0
ports:
- containerPort: 5044
protocol: TCP
- containerPort: 9600
protocol: TCP

volumeMounts:
- name: logstash-config
mountPath: /usr/share/logstash/pipeline/logstash.conf
subPath: logstash.conf

volumes:
- name: logstash-config
configMap:
#defaultMode: 0644
name: logstash-config

---
apiVersion: v1
kind: Service
metadata:
namespace: halashow
name: logstash
labels:
app: logstash
spec:
type: ClusterIP
ports:
- port: 5044
name: logstash
selector:
app: logstash

filebeat部署

  • 获得k8s的rbac权限
  • 监控每个节点下面/var/log/containers/*.log的所有日志(由于容器真正的日志路径是/var/lib/docker/containers/,这里input我们写上/var/log/containers/*.log,但需要把/var/lib/docker/containers/也给挂载上去,不然一个软连接文件无法使用。)
  • #备注:这里新版本k8s放弃了docker,直接使用containerd,所以日志没有再存放在docker中,这里如果是新版本,日志为/var/log/pods/中,然后软链接到/var/log/containers
  • indice 进行轮转
  • ouput到logstash

filebeat.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
---
#rbac权限
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: filebeat
subjects:
- kind: ServiceAccount
name: filebeat
namespace: halashow
roleRef:
kind: ClusterRole
name: filebeat
apiGroup: rbac.authorization.k8s.io
---
#具体授权项
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: filebeat
labels:
app: filebeat
rules:
- apiGroups: [""]
resources:
- namespaces
- pods
- nodes
verbs:
- get
- watch
- list
---
#授权给具体名称空间
apiVersion: v1
kind: ServiceAccount
metadata:
namespace: halashow
name: filebeat
labels:
app: filebeat

---
#配置文件
apiVersion: v1
kind: ConfigMap
metadata:
namespace: halashow
name: filebeat-config
labels:
app: filebeat
data:
filebeat.yml: |-

filebeat.inputs:
- type: container
enabled: true
paths:
- /var/log/containers/*.log
#多行合并
#下面有关multiline的都是多行合并的配置,多行合并配置本意是因为有的日志是一条日志占了多行,如果不进行该配置就会一条日志显示多行。但配置了多行合并后就会根据pattern匹配的内容将多行合并在一起。这个是作用以及其优点,下面说说缺点
#缺点是例如这下面multiline.pattern可以匹配到2023-02-23开头的日志条目,但有些服务并非以日期开头或者不以这样的规范日期开头就会匹配不到,导致那些服务完全看不到日志的情况。所以这里如果要用多行合并只能是仅监控部分服务的需求,例如仅监控java程序的日志就可以使用多行合并功能,避免一个报错很多行日志也会生成很多行的情况,但如果要收集所有服务日志,就没办法使用这个功能。(因为肯定有很多程序的日志输出格式不一样,没办法统一匹配)
#多行合并详情:https://blog.csdn.net/u012069313/article/details/123477160
#multiline.pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}'
# multiline.negate: true
# multiline.match: after
# multiline.timeout: 30
fields:
#自定义字段用于logstash识别k8s输入的日志
service: k8s-log
# multiline: # 多行处理,正则表示如果前面几个数字不是4个数字开头,那么就会合并到一行,解决Java堆栈错误日志收集问题
# pattern: ^\d{4}-\d{1,2}-\d{1,2}\s\d{1,2}:\d{1,2}:\d{1,2} #匹配Java日志开头时间
# negate: true # 正则是否开启,默认false不开启
# match: after # 不匹配的正则的行是放在上面一行的前面还是后面
processors:
- add_kubernetes_metadata:
in_cluster: true
host: ${NODE_NAME}
matchers:
- logs_path:
logs_path: "/var/log/containers/"

- add_cloud_metadata:
- add_kubernetes_metadata:
matchers:
- logs_path:
logs_path: "/var/log/containers/"
- add_docker_metadata:
- drop_fields:
#删除的多余字段
fields: ["host", "tags", "ecs", "log", "prospector", "agent", "input", "beat", "offset"]
ignore_missing: true

output.logstash:
hosts: ["logstash:5044"]


setup.ilm:
policy_file: /etc/indice-lifecycle.json


---
#生命周期配置文件。ElasticSearch 的 indice 生命周期表示一组规则,可以根据 indice 的大小或者时长应用到你的 indice 上。比如可以每天或者每次超过 1GB 大小的时候对 indice 进行轮转,我们也可以根据规则配置不同的阶段。由于监控会产生大量的数据,很有可能一天就超过几十G的数据,所以为了防止大量的数据存储,我们可以利用 indice 的生命周期来配置数据保留,这个在 Prometheus 中也有类似的操作。 如下所示的文件中,我们配置成每天或每次超过5GB的时候就对 indice 进行轮转,并删除所有超过30天的 indice 文件,我们这里只保留30天监控数据完全足够了。
apiVersion: v1
kind: ConfigMap
metadata:
namespace: halashow
name: filebeat-indice-lifecycle
labels:
app: filebeat
data:
indice-lifecycle.json: |-

{
"policy": {
"phases": {
"hot": {
"actions": {
"rollover": {
"max_size": "5GB" ,
"max_age": "1d"
}
}
},
"delete": {
"min_age": "30d",
"actions": {
"delete": {}
}
}
}
}
}

---
#主程序
apiVersion: apps/v1
kind: DaemonSet
metadata:
namespace: halashow
name: filebeat
labels:
app: filebeat
spec:
selector:
matchLabels:
app: filebeat
template:
metadata:
labels:
app: filebeat
spec:
serviceAccountName: filebeat
terminationGracePeriodSeconds: 30
containers:
- name: filebeat
image: docker.elastic.co/beats/filebeat:7.12.0
args: [
"-c", "/etc/filebeat.yml",
"-e",
]
env:
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
securityContext:
runAsUser: 0
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 100Mi
volumeMounts:
- name: config
mountPath: /etc/filebeat.yml
readOnly: true
subPath: filebeat.yml
- name: filebeat-indice-lifecycle
mountPath: /etc/indice-lifecycle.json
readOnly: true
subPath: indice-lifecycle.json
- name: data
mountPath: /usr/share/filebeat/data
- name: varlog
mountPath: /var/log
readOnly: true
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
- name: dockersock
mountPath: /var/run/docker.sock
volumes:
- name: config #配置文件
configMap:
defaultMode: 0600
name: filebeat-config
- name: filebeat-indice-lifecycle #indice配置文件
configMap:
defaultMode: 0600
name: filebeat-indice-lifecycle
- name: varlog #日志目录挂载
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers #真正的日志目录挂载
- name: dockersock
hostPath:
path: /var/run/docker.sock #docker socket文件
- name: data
hostPath:
path: /var/lib/filebeat-data #filebeat数据目录
type: DirectoryOrCreate

kibana部署

  • 连接到es
  • 提供web接口(service)

kibana.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: kibana
namespace: es
labels:
name: kibana
spec:
replicas: 1
selector:
matchLabels:
name: kibana
template:
metadata:
labels:
app: kibana
name: kibana
spec:
containers:
- name: kibana
image: docker.elastic.co/kibana/kibana:7.12.0
ports:
- containerPort: 5601
protocol: TCP
env:
- name: ELASTICSEARCH_URL
value: http://elasticsearch:9200
---
apiVersion: v1
kind: Service
metadata:
name: kibana
namespace: es
spec:
type: NodePort
ports:
- protocol: TCP
port: 80
targetPort: 5601
selector:
app: kibana

部署完测试

添加索引模式k8s*,然后查看日志。

image-20220804110536197

通过容器来筛选日志:kubernetes.container.name

image-20220804110828287

elk+redis(kafka)

Elasticsearch + Logstash + filebeat + redis这种方案主要就是elk的完善组合,filebeat用于收集日志(轻量化),而这里的redis则是作为中间件防止日志数据丢失,其功能最开始elk常用几种架构有些到了,就是:这种架构是上面那个架构的完善版,通过增加中间件,来避免数据的丢失。当Logstash出现故障,日志还是存在中间件中,当Logstash再次启动,则会读取中间件中积压的日志。

具体实现(elk+filebeat+redis)

这里只列出配置文件关键段

filebeat配置

filebeat.yml

1
2
3
4
5
6
7
8
9
output:
redis:
hosts: ["172.17.199.231:6379"] #发送给redis
save_topology: true
index: "filebeat"
db: 0
db_topology: 1
timeout: 5
reconnect_interval: 1

logstash配置

logstash.conf

1
2
3
4
5
6
7
8
9
10
input {
redis {
host => "172.17.199.231"
port => 6379
key => "filebeat"
db => "0"
data_type => "list"
}
}

解读

之前elk+filebeat是logstash的input监听端口,output输出到es,然后filebeat这里input收集数据,output到logstash的input所监听的端口上面。

然后加入了中间件数据库以后logstash的input不再监听端口,而是收集来自于redis的数据,filebeat output也变成了给redis发送数据。

一般只有elk+filebeat能用到中间件数据库,这里redis换成(kafka)mq也是一样的,无非是filebeat配置文件output到kafka,logstash的input也从redis换成kafka。es+logstash+kibana这种组合中间不需要再加中间件。

EFK

EFK是三个开源软件的缩写,分别表示:Elasticsearch , FileBeat, Kibana。这里相当于把logstash这一环节彻底干掉,filebeat既收集日志,还处理日志并发送到es。优点是结构更简单了,filebeat也确实很轻量化。缺点是 FileBeat 也可以格式化日志,但是相对于Logstash 来说,效果差很多。

elk日志生命周期

elk的日志索引我们一般情况下是按天来存储的,例如‘k8s-2022-10-22’。但因为日志本身不需要存储太久以前的,而且对存储占用也很大,不能一直保存,所以会定时清理。

1
索引生命周期分为四个阶段:HOT(热)=>WARM(温)=》COLD(冷)=>DELETE(删除)

配置生命周期策略

在kibana的dev tools中执行以下命令,表示创建一个超过7天自动删除的生命周期策略。

1
2
3
4
5
6
7
8
9
10
11
12
13
PUT _ilm/policy/auto_delete_policy   
{
"policy": {
"phases": {
"delete": {
"min_age": "7d",
"actions": {
"delete": {}
}
}
}
}
}
1
2
3
4
5
创建一个自动删除策略(auto_delete_policy)

delete:删除阶段,7天执行删除索引动作

查看策略:GET _ilm/policy/

创建索引模板

创建了生命周期策略以后需要创建索引模板,索引模板本身需要对应匹配索引,还需要绑定上面创建的生命周期策略

1
这里网上一般也是采取PUT的方式进行,但我这里测试发现如果向上面一样采用put会创建成旧索引模板(无法生效),这里不研究其原因,直接从kibana中创建模块。

management-》stack management-》data-》index management-》index templates

image-20221018155656721

创建模板(create template):

logistacs

image-20221018155856675

index settings
1
2
3
4
5
6
7
8
{
"index": {
"lifecycle": {
"name": "auto_delete_policy",
"indexing_complete": "true"
}
}
}

Component templates、Mappings、Aliases跳过,不做设置。最后保存模板即可。