ELKF日志系统搭建(二)进阶——使用 Kafka 作为日志消息缓存
说明:
在一些比较大的业务使用场景中,因为应用繁多,需要收集的日志也很多,通过 filebeat 或者logstash 收集上来的日志如果全都直接发送给 ES,那么就会对 ES 集群产生一定的压力,为了避免出现日志接收不过来的问题,于是引入了消息队列作为缓存,比如常见的使用 Redis 或 Kafka 作为消息缓存。本篇讲的是以 kafka 作为消息缓存的架构,收集上来的日志先发送给 Kafka,然后再发送给 ES集群,当然因 Kafka 没办法直接和 ES 对接数据,需在这两者之间使用 Logstash 来传输。于是架构就有了如下:
我这里的 Kafka 部署也是 3 个节点部署,都安装在之前的 ES 服务器上,即分别安装在 elk01(192.168.200.21)、 elk02(192.168.200.22)、 elk03(192.168.200.23)上。
一、安装JDK
安装 JDK,因为部署 Kafka 需要依赖 java 环境。登录 3 台 ES 集群服务器,先检查系统是否已经装有 java 了,然后进入安装包目录,执行安装即可。(如装有 java 了可以跳过此步骤)
[root@elk01 ~]# java --version
openjdk 11.0.19 2023-04-18 LTS
OpenJDK Runtime Environment (Red_Hat-11.0.19.0.7-1.el7_9) (build 11.0.19+7-LTS)
OpenJDK 64-Bit Server VM (Red_Hat-11.0.19.0.7-1.el7_9) (build 11.0.19+7-LTS, mixed mode, sharing)
java 安装完成即可,无需再做其他配置
二、 安装 Zookeeper
部署 Kafka 之前还需要先安装 zookeeper。 zookeeper 是一个集群调度的工具,可以用来调度Kafka 集群(三台机器都要部署)。
安装配置如下:
#解压
tar zxf apache-zookeeper-3.9.0-bin.tar.gz
mv apache-zookeeper-3.9.1-bin /usr/local/zookeeper-3.9.1
#创建数据目录和日志目录
mkdir /usr/local/zookeeper-3.9.1/data
mkdir /usr/local/zookeeper-3.9.1/logs
#修改配置文件
cp /usr/local/zookeeper-3.9.1/conf/zoo_sample.cfg /usr/local/zookeeper-3.9.1/conf/zoo.cfg
cat /usr/local/zookeeper-3.9.1/conf/zoo.cfg | grep -v '#' | grep -v '^$'
tickTime=2000 #通信心跳时间,Zookeeper服务器与客户端心跳时间,单位毫秒
initLimit=10 #Leader和Follower初始连接时能容忍的最多心跳数(tickTime的数量),这里表示为10*2s
syncLimit=5 #Leader和Follower之间同步通信的超时时间,这里表示如果超过5*2s,Leader认为Follwer死掉,并从服务器列表中删除Follwer
dataDir=/usr/local/zookeeper-3.9.1/data ●修改,指定保存Zookeeper中的数据的目录,目录需要单独创建
dataLogDir=/usr/local/zookeeper-3.9.1/logs ●添加,指定存放日志的目录,目录需要单独创建
clientPort=2181 #客户端连接端口
#添加集群信息
server.1=192.168.200.21:2888:3888
server.2=192.168.200.22:2888:3888
server.3=192.168.200.23:2888:3888
说明:
server.服务器ID=服务器IP地址:服务器之间通信端口:服务器之间投票选举端口
客户端和服务器通信默认端口:2181
服务器之间通信默认端口:2888
服务器之间投票选举默认端口:3888
ZooKeeper中的AdminService服务默认占用端口:8080
[根据这里的服务器ID编号的大小进行选举]
【server】的配置含义说明:
server.A=B:C:D
其中 A 是一个数字,表示这个是第几号服务器;
B 是这个服务器的 ip 地址;
C 表示的是这个服务器与集群中的 Leader 服务器交换信息的端口;
D 表示的是万一集群中的 Leader 服务器挂了,需要一个端口来重新进行选举,选出一个新的 Leader,而这个端口就是用来执行选举时服务器相互通信的端口。
如果是伪集群的配置方式(也就是在一台服务器上搭建多个ZK服务节点),由于 一台服务器下的IP地址肯定一样,所以不同的 Zookeeper 实例通信端口号不能一样,故需要给它们分配不同的端口号。
相反,如果是在不同的服务器下(不同的虚拟机,IP地址不同)搭建ZK的话,由于IP地址不同,则C与D对应的通信端口可以保持一致。当然也可以配置成其他的端口。
【注意点】:
其中的server.1里面的数字1,是我们在myid文件中指定的数值。代表不同的zk节点。
同时,IP地址配置也要与server.1所在的服务器节点的IP地址一致。
后面的2个通信端口,随意指定,只要不与其他端口冲突就行。
elk02 和 elk03 两台机子也都同样进行 zookeeper 的安装
#将拷贝配置好的 Zookeeper 到其他机器上
scp -r /usr/local/zookeeper-3.9.1 root@192.168.200.22:/usr/local/
scp -r /usr/local/zookeeper-3.9.1 root@192.168.200.23:/usr/local/
在每个节点的dataDir指定的目录下创建一个 myid 的文件
[root@elk01 ~]# echo 1 > /usr/local/zookeeper-3.9.1/data/myid
[root@elk02 ~]# echo 2 > /usr/local/zookeeper-3.9.1/data/myid
[root@elk03 ~]# echo 3 > /usr/local/zookeeper-3.9.1/data/myid
配置 Zookeeper 启动脚本
cat /etc/systemd/system/zookeeper.service
[Unit]
Description=zookeeper server
Requires=network.target remote-fs.target
After=network.target remote-fs.target
[Service]
Type=forking
User=root
Environment=JAVA_HOME=/usr/lib/jvm/java-11-openjdk-11.0.19.0.7-1.el7_9.x86_64
ExecStart=/bin/sh -c '/usr/local/zookeeper-3.9.1/bin/zkServer.sh start /usr/local/zookeeper-3.9.1/conf/zoo.cfg'
ExecStop=/usr/local/zookeeper-3.9.1/bin/zkServer.sh stop
Restart=on-failure
RestartSec=2s
[Install]
WantedBy=multi-user.target
#将zookeeper.service文件发送到另外2台服务器
scp /etc/systemd/system/zookeeper.service root@192.168.100.22:/etc/systemd/system/
scp /etc/systemd/system/zookeeper.service root@192.168.100.23:/etc/systemd/system/
systemctl daemon-reload # 重载配置
systemctl enable zookeeper.service # 设置zookeeper开机启动
systemctl start zookeeper.service # 启动zookeeper
systemctl status zookeeper.service # 查看zookeeper启动状态
三、安装kafka
#解压
tar zxvf kafka_2.13-3.6.0.tgz
#移动到/usr/local/目录下
mv kafka_2.13-3.6.0 /usr/local/kafka_2.13
cd /usr/local/kafka_2.13/config/
cp server.properties{,.bak}
修改配置文件如下:
cat server.properties | grep -v '#' | grep -v '^$'
broker.id=1
listeners=PLAINTEXT://192.168.200.21:9092
num.network.threads=3
num.io.threads=8
socket.send.buffer.bytes=102400
socket.receive.buffer.bytes=102400
socket.request.max.bytes=104857600
log.dirs=/usr/local/kafka_2.13/logs
num.partitions=1
num.recovery.threads.per.data.dir=1
offsets.topic.replication.factor=1
transaction.state.log.replication.factor=1
transaction.state.log.min.isr=1
log.retention.hours=24
log.retention.bytes=1073741824
log.retention.check.interval.ms=300000
zookeeper.connect=192.168.200.21:2181,192.168.200.22:2181,192.168.200.23:2181
zookeeper.connection.timeout.ms=18000
group.initial.rebalance.delay.ms=0
配置 Zookeeper 启动脚本
cat /etc/systemd/system/kafka.service
[Unit]
Description=kafka server
Requires=zookeeper.service
After=zookeeper.service
[Service]
Type=simple
User=root
Environment=JAVA_HOME=/usr/lib/jvm/java-11-openjdk-11.0.19.0.7-1.el7_9.x86_64
ExecStart=/bin/sh -c '/usr/local/kafka_2.13/bin/kafka-server-start.sh /usr/local/kafka_2.13/config/server.properties'
ExecStop=/bin/sh -c '/usr/local/kafka_2.13/bin/kafka-server-stop.sh'
Restart=on-failure
RestartSec=2s
SuccessExitStatus=0 143
[Install]
WantedBy=multi-user.target
发送kafka到elk02和elk03服务器
scp -r /usr/local/kafka_2.13 192.168.200.22:/usr/local/
scp -r /usr/local/kafka_2.13 192.168.200.23:/usr/local/
scp -r /etc/systemd/system/kafka.service 192.168.200.22:/etc/systemd/system/
scp -r /etc/systemd/system/kafka.service 192.168.200.23:/etc/systemd/system/
修改elk02和elk03的broker.id
vim /usr/local/kafka_2.13/config/server.properties
elk02的修改为:
broker.id=2
listeners=PLAINTEXT://192.168.200.22:9092
elk03的修改为:
broker.id=3
listeners=PLAINTEXT://192.168.200.23:9092
注意:listeners的IP为本机IP
之后启动服务
systemctl daemon-reload
systemctl enable kafka.service
systemctl start kafka.service
systemctl status kafka.service
四、使用logstash将Kafka日志发送到ES
因为 Kafka 无法把接收到的日志发送给 ES,需要使用 Logstash 作为管道进行发送,所以我们也在三台集群节点上安装一下 Logstash(其实不装也行,直接用前面在 192.168.200.21 装的Logstash 也可以演示了,实际应用中为了更好的分工协作,还是都安装以区分开来用)。
三台机器都安装Logstash
#安装
rpm -ivh logstash-8.9.1-x86_64.rpm
安装完成后,将 ES 的证书放到 Logstash 目录下以供使用
cp -r /etc/elasticsearch/certs /etc/logstash/
创建软连接
#创建软链接
ln -s /usr/share/logstash/bin/logstash /bin/
ln -s /usr/share/logstash/bin/logstash-plugin /bin/
ln -s /usr/share/logstash/bin/logstash.lib.sh /usr/bin/
创建一个非常简单的从 kafka 到 ES 的 logstash 配置文件。
cat kafka_to_es.conf
input {
kafka {
codec => plain {
format => "%{message}"
}
bootstrap_servers =>
"192.168.200.21:9092,192.168.200.22:9092,192.168.200.23:9092"
topics => ["kafkalogstash"]
}
}
output {
elasticsearch {
hosts =>
["https://192.168200.21:9200","https://192.168.200.229200","https://192.168.200.23:9200"
]
index => "from-kafka-syslog-%{+YYYY.MM}"
user => "elastic"
password => "086530qwe"
34 / 55ssl => "true"
cacert => "/etc/logstash/certs/http_ca.crt"
}
}
创建完后,我们可以尝试执行一下这个 logstash 配置文件,看有没有报错,注意这时候要确保kafka 是启动的状态,而 kafka 启动还需要依赖于 zookeeper 的启动,而 logstash 发送日志给 ES 也要确保 ES 是启动的,不然都会有报错。
[root@elk01 ~]# /usr/share/logstash/bin/logstash -f /etc/logstash/conf.d/kafka_to_es.conf
Using bundled JDK: /usr/share/logstash/jdk
WARNING: Could not find logstash.yml which is typically located in $LS_HOME/config or /etc/logstash. You can specify the path using --path.settings. Continuing using the defaults
Could not find log4j2 configuration at path /usr/share/logstash/config/log4j2.properties. Using default config which logs errors to the console
[WARN ] 2024-02-28 15:11:52.497 [main] runner - NOTICE: Running Logstash as superuser is not recommended and won't be allowed in the future. Set 'allow_superuser' to 'false' to avoid startup errors in future releases.
[INFO ] 2024-02-28 15:11:52.515 [main] runner - Starting Logstash {"logstash.version"=>"8.9.1", "jruby.version"=>"jruby 9.3.10.0 (2.6.8) 2023-02-01 107b2e6697 OpenJDK 64-Bit Server VM 17.0.8+7 on 17.0.8+7 +indy +jit [x86_64-linux]"}
[INFO ] 2024-02-28 15:11:52.532 [main] runner - JVM bootstrap flags: [-Xms1g, -Xmx1g, -Djava.awt.headless=true, -Dfile.encoding=UTF-8, -Djruby.compile.invokedynamic=true, -XX:+HeapDumpOnOutOfMemoryError, -Djava.security.egd=file:/dev/urandom, -Dlog4j2.isThreadContextMapInheritable=true, -Djruby.regexp.interruptible=true, -Djdk.io.File.enableADS=true, --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED, --add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED, --add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED, --add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED, --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED, --add-opens=java.base/java.security=ALL-UNNAMED, --add-opens=java.base/java.io=ALL-UNNAMED, --add-opens=java.base/java.nio.channels=ALL-UNNAMED, --add-opens=java.base/sun.nio.ch=ALL-UNNAMED, --add-opens=java.management/sun.management=ALL-UNNAMED]
[WARN ] 2024-02-28 15:11:53.033 [LogStash::Runner] multilocal - Ignoring the 'pipelines.yml' file because modules or command line options are specified
/usr/share/logstash/vendor/bundle/jruby/2.6.0/gems/sinatra-2.2.4/lib/sinatra/base.rb:938: warning: constant Tilt::Cache is deprecated
[INFO ] 2024-02-28 15:11:54.465 [Api Webserver] agent - Successfully started Logstash API endpoint {:port=>9600, :ssl_enabled=>false}
[ERROR] 2024-02-28 15:11:54.674 [Converge PipelineAction::Create<main>] agent - Failed to execute action {:action=>LogStash::PipelineAction::Create/pipeline_id:main, :exception=>"LogStash::ConfigurationError", :message=>"Expected one of [ \\t\\r\\n], \"#\", \"=>\" at line 20, column 6 (byte 407) after output {\n\telasticsearch {\n\t\thosts =>\n[\"https://192.168200.21:9200\",\"https://192.168.200.229200\",\"https://192.168.200.23:9200\"\n]\n\t\tindex => \"from-kafka-syslog-%{+YYYY.MM}\"\n\t\tuser => \"elastic\"\n\t\tpassword => \"086530qwe\"\n\t\t34 ", :backtrace=>["/usr/share/logstash/logstash-core/lib/logstash/compiler.rb:32:in `compile_imperative'", "org/logstash/execution/AbstractPipelineExt.java:239:in `initialize'", "org/logstash/execution/AbstractPipelineExt.java:173:in `initialize'", "/usr/share/logstash/logstash-core/lib/logstash/java_pipeline.rb:48:in `initialize'", "org/jruby/RubyClass.java:911:in `new'", "/usr/share/logstash/logstash-core/lib/logstash/pipeline_action/create.rb:50:in `execute'", "/usr/share/logstash/logstash-core/lib/logstash/agent.rb:386:in `block in converge_state'"]}
[INFO ] 2024-02-28 15:11:54.739 [LogStash::Runner] runner - Logstash shut down.
没有报错说明我们的配置文件没什么大问题,当然,现在是没有数据发送到 ES 的,因为现在还没配置把日志发送给 Kafka,现在 Kafka 里是空的,啥都还没有。
配置没有问题,我们把 elk01 的 kafka_to_es.conf 文件也都发给另外两台机子,另外两个logstash 就可以直接使用了。
scp kafka_to_es.conf 192.168.200.22:/etc/logstash/conf.d
scp kafka_to_es.conf 192.168.200.23:/etc/logstash/conf.d
现在三台节点都有 logstash 的 kafka_to_es.conf 文件文件了,三台都可以同时启动这个脚本发送日志(当然现在 kafka 是空的,现在还是没有数据发送到 ES 的)
systemctl restart logstash.service
systemctl enable logstash.service
systemctl status logstash.service
3 台节点的 logstash 都已经启动了,接下来配置发送日志到 Kafka。
五、配置 Logstash 发送日志到 Kafka
下面以 192.168.200.21(kibana)上的 logstash 作为 Kafka 的输入为例。登录 192.168.200.21(kibana),配置 logstash 文件