跳转至

ELK部署2

一. 在ELK6上部署nginx+filebeat

0. 修改服务器名称并写入hosts文件,安装时间同步插件

时间同步插件略,可以参考K8s部署

hostnamectl set-hostname ELK6
cat << EOF | tee -a /etc/hosts
192.168.100.102 ELK4
192.168.100.105 ELK6
192.168.100.106 ELK7
EOF

1. 安装nginx

为了方便,我们直接安装rpm包nginx

dnf -y install nginx
systemctl start nginx
systemctl enable nginx

2. 安装filebeat

这里我们使用二进制包的filebeat

[root@ELK6 ~]# ls
anaconda-ks.cfg  filebeat-8.15.3-linux-x86_64.tar.gz

1) 解压软件包

tar xf filebeat-8.15.3-linux-x86_64.tar.gz -C /usr/local/
mv /usr/local/filebeat-8.15.3-linux-x86_64/ /usr/local/filebeat/

2) 修改配置文件

filebeat.yml配置主要有两部分,一个是日志收集,一个是日志输出

需要改的行有:

vi /usr/local/filebeat/filebeat.yml
- type: filestream
# 读取日志的每一行(默认)
  enabled: false
  # 该配置是否生效,如果为false,将不再收集该配置的日志
  paths:
  # 要抓取日志的绝对路径
    - /var/log/*.log
    # 添加收集httpd服务日志
output.elasticsearch:
  hosts: ["localhost:9200"]
  preset: balanced
# 这三行注释掉
#output.logstash:
# 取消注释,把日志放到logstash中
  #hosts: ["localhost:5044"]
  # 取消注释,输出到Logstash的地址和端口
logging.level: warning
# 新增这一行,调整日志级别

我们可以使用sed命令一次性修改(方法1)

sed -i -e 's@- type: filestream@- type: log@' \
  -e 's@enabled: false@enabled: true@' \
  -e 's@- /var/log/\*\.log@- /var/log/nginx/\*\.log@' \
  -e 's@#output.elasticsearch:@output.elasticsearch:@' \
  -e 's@hosts: \["localhost:9200"\]@hosts: ["192.168.100.102:9200"]@' \
  -e 's@preset: balanced@#preset: balanced@' \
  -e '167a logging.level: warning' \
/usr/local/filebeat/filebeat.yml

或者直接粘贴下面这一段(方法2)

filebeat.inputs:
- type: log
  id: my-filestream-id
  enabled: true
  paths:
    - /var/log/nginx/*.log
filebeat.config.modules:
  path: ${path.config}/modules.d/*.yml
  reload.enabled: true
setup.template.settings:
  index.number_of_shards: 1
setup.kibana:
logging.level: warning
processors:
  - add_host_metadata:
      when.not.contains.tags: forwarded
  - add_cloud_metadata: ~
  - add_docker_metadata: ~
  - add_kubernetes_metadata: ~

3) 启动服务

cd /usr/local/filebeat/
./filebeat -e -c filebeat.yml
# 或 ./filebeat

可以另开一个终端查看服务

[root@ELK6 ~]# ps -ef|grep filebeat
root        1441    1337  1 09:24 pts/0    00:00:00 ./filebeat -e -c filebeat.yml
root        1479    1452  0 09:25 pts/1    00:00:00 grep --color=auto filebeat

注

-e 记录到stderr(标准输出(设备)文件,对应终端的屏幕),并禁用syslog文件输出
-c 指定用于Filebeat的配置文件,如果未指定-c标志则使用默认配置文件filebeat.yml

二. 回到ELK4上修改配置

1. 配置http_logstash.conf配置文件

cat << 'EOF' | tee  /etc/logstash/conf.d/http_logstash.conf
input {
    beats {
    #从Elasticbeats接收事件
        codec => plain {charset => "UTF-8" }
        # 设置编解码器为UTF8
        port => "5044"
    }
}
output {
    stdout {
    # 标准输出,把手机的日志在当前终端显示,方便测试服务连通性
        codec => "rubydebug"
        # 指定编码器
    }
    elasticsearch {
    # 把收集的日志发送给es
        hosts => ["192.168.100.102:9200"]
        # es服务器的地址
        index => "nginx-logs-%{+yyyy.MM.dd}"
        # 创建索引
    }
}
EOF

2. 语法检测,无误后重启服务

[root@ELK4 ~]# /usr/share/logstash/bin/logstash -t --path.settings /etc/logstash && systemctl restart logstash
Using bundled JDK: /usr/share/logstash/jdk
Sending Logstash logs to /var/log/logstash which is now configured via log4j2.properties
[2026-08-06T10:03:15,777][WARN ][logstash.runner          ] NOTICE: Running Logstash as a superuser is strongly discouraged as it poses a security risk. Set 'allow_superuser' to false for better security.
[2026-08-06T10:03:15,788][INFO ][logstash.runner          ] Log4j configuration path used is: /etc/logstash/log4j2.properties
[2026-08-06T10:03:15,790][INFO ][logstash.runner          ] Starting Logstash {"logstash.version"=>"8.19.19", "jruby.version"=>"jruby 9.4.15.0 (3.1.7) 2026-06-08 6114208a31 OpenJDK 64-Bit Server VM 21.0.11+10-LTS on 21.0.11+10-LTS +indy +jit [x86_64-linux]"}
[2026-08-06T10:03:15,792][INFO ][logstash.runner          ] JVM bootstrap flags: [-Xms512m, -Xmx512m, -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, -Dio.netty.allocator.maxOrder=11]
[2026-08-06T10:03:15,844][INFO ][org.logstash.jackson.StreamReadConstraintsUtil] Jackson default value override `logstash.jackson.stream-read-constraints.max-string-length` configured to `200000000` (logstash default)
[2026-08-06T10:03:15,844][INFO ][org.logstash.jackson.StreamReadConstraintsUtil] Jackson default value override `logstash.jackson.stream-read-constraints.max-number-length` configured to `10000` (logstash default)
[2026-08-06T10:03:15,845][INFO ][org.logstash.jackson.StreamReadConstraintsUtil] Jackson default value override `logstash.jackson.stream-read-constraints.max-nesting-depth` configured to `1000` (logstash default)
[2026-08-06T10:03:16,655][INFO ][org.reflections.Reflections] Reflections took 126 ms to scan 1 urls, producing 150 keys and 530 values
[2026-08-06T10:03:17,234][INFO ][logstash.javapipeline    ] Pipeline `main` is configured with `pipeline.ecs_compatibility: v8` setting. All plugins in this pipeline will default to `ecs_compatibility => v8` unless explicitly configured otherwise.
Configuration OK
[2026-08-06T10:03:17,237][INFO ][logstash.runner          ] Using config.test_and_exit mode. Config Validation Result: OK. Exiting Logstash

检查端口占用情况

[root@ELK4 ~]# ss -tlnp | grep 5044
LISTEN 0      4096                 *:5044            *:*    users:(("java",pid=2036,fd=113)) 

3. 配置kibana

1) 查看index

elastic: http://192.168.100.102:5601

alt textalt textalt textalt textalt text

2)创建index数据视图

alt textalt textalt text alt textalt text

3) 查看日志

alt text

4) 添加过滤器

我们可以在搜索到的日志添加过滤器,过滤出我们想要的内容。比如我想看nginx的错误日志那么添加一个log.file.path为/var/log/nginx/error.log的过滤器。如果我们监控的服务器比较多,我们可以查看特定的主机的nginx错误日志

Ⅰ. 添加类型为log.file.path的过滤器

alt text

Ⅱ. 过滤出ELK6的nginx错误日志

添加类型为host.ip的过滤器

输入host.ip : "192.168.100.105"

alt text

过滤的主机字段名称叫host.hostname,添加host.hostname为elk6的过滤器,所有过滤器字段再现实的日志上可以看到

输入host.hostname : "elk6"

# 注: hostname 字段存的是小写,即使主机名是大写。搜索时候keyword 字段是严格区分大小写的。所以这里要搜小写的elk

alt text

至此,ELK+filebeat部署完成

三. 部署Kafka

为了防止收集的日志信息太多或者是服务器down机导致信息丢失,需要引入Kafka消息队列服务器。现在先搭建单节点的Kafka。生产环境中应当使用集群方式部署,详细过程参考 Kafka概述

0. 安装时间同步和修改服务器名称并写入hossts

hostnamectl set-hostname ELK7
cat << EOF | tee -a /etc/hosts
192.168.100.102 ELK4
192.168.100.105 ELK6
192.168.100.106 ELK7
EOF

1. 部署jdk-21

wget https://download.oracle.com/java/21/latest/jdk-21_linux-x64_bin.tar.gz
tar -xf jdk-21_linux-x64_bin.tar.gz -C /usr/local
tee /etc/profile.d/java.sh << 'EOF'
  export JAVA_HOME=/usr/local/jdk-21.0.12 
  export PATH=$JAVA_HOME/bin:$PATH
  export ES_JAVA_HOME=$JAVA_HOME
EOF
source /etc/profile.d/java.sh
java -version

2. 安装zookeeper

1) 下载安装包并解压

wget https://dlcdn.apache.org/zookeeper/zookeeper-3.9.5/apache-zookeeper-3.9.5-bin.tar.gz
tar xf apache-zookeeper-3.9.5-bin.tar.gz -C /usr/local/

2) 创建快照日志存放目录和事务日志存放目录

mkdir -p /data/zk/data
mkdir -p /data/zk/datalog

3) 生成配置文件

复制一份配置文件并改名

cp /usr/local/apache-zookeeper-3.9.5-bin/conf/zoo_sample.cfg  /usr/local/apache-zookeeper-3.9.5-bin/conf/zoo.cfg
vi zoo.cfg

我们需要修改的有以下几行

dataDir=/tmp/zookeeper
# 此为我们创建的快照日志存放目录
dataLogDir=
# 此为我们的事务日志存放目录

可以使用以下命令修改(方法1)

sed -i -e 's@dataDir=/tmp/zookeeper@dataDir=/data/zk/data@' \
  -e '/dataDir=/a dataLogDir=/data/zk/datalog' \
  /usr/local/apache-zookeeper-3.9.5-bin/conf/zoo.cfg

4) 添加path环境变量

cat << 'EOF'|tee -a /etc/profile
  export ZOOKEEPER_HOME=/usr/local/apache-zookeeper-3.9.5-bin
  export PATH=$ZOOKEEPER_HOME/bin:$PATH
EOF
source /etc/profile

5) 测试是否安装成功,然后启动

[root@ELK7 ~]# echo $ZOOKEEPER_HOME
/usr/local/apache-zookeeper-3.9.5-bin
[root@ELK7 ~]# zkServer.sh start
ZooKeeper JMX enabled by default
Using config: /usr/local/apache-zookeeper-3.9.5-bin/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
[root@ELK7 ~]# zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /usr/local/apache-zookeeper-3.9.5-bin/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost. Client SSL: false.
Mode: standalone

6) 添加开机自启动

我们可以在/lib/systemd/system文件夹下创建一个启动脚本zookeeper.service实现zookeeper的开机自启动

cat << 'EOF'|tee /lib/systemd/system/zookeeper.service
[Unit]
Description=Zookeeper service
After=network.target

[Service]
Type=forking
Environment="JAVA_HOME=/usr/local/jdk-21.0.12"
User=root
Group=root
ExecStart=/usr/local/apache-zookeeper-3.9.5-bin/bin/zkServer.sh start
ExecStop=/usr/local/apache-zookeeper-3.9.5-bin/bin/zkServer.sh stop

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable zookeeper

3. 安装Kafka

1) 从官网下载二进制包

wget -O kafka_2.12-3.9.1.tgz https://archive.apache.org/dist/kafka/3.9.1/kafka_2.12-3.9.1.tgz
tar xf kafka_2.12-3.9.1.tgz -C /usr/local/

2) 修改配置文件

vi /usr/local/kafka_2.13-4.3.1/config/server.properties 

我们需要改的行有

#listeners=PLAINTEXT://:9092      # 取消注释
default.replication.factor=1      # 加不加上都行

可以执行以下命令(方法1)

sed -i -e 's@#listeners=PLAINTEXT://:9092@listeners=PLAINTEXT://:9092@' \
  -e '$a default.replication.factor=1' \
  /usr/local/kafka_2.12-3.9.1/config/server.properties 

或者直接粘贴(方法2)

broker.id=0
listeners=PLAINTEXT://: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=/tmp/kafka-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=168
log.retention.check.interval.ms=300000
zookeeper.connect=localhost:2181
zookeeper.connection.timeout.ms=18000
group.initial.rebalance.delay.ms=0
default.replication.factor=1

3) 创建日志目录

mkdir -p /data/kafka/log/

4) 添加path环境变量

cat << 'EOF'|tee -a /etc/profile
export KAFKA_HOME=/usr/local/kafka_2.12-3.9.1
export PATH=$KAFKA_HOME/bin:$PATH
EOF
source /etc/profile

5) 启动Kafka

后台启动

kafka-server-start.sh -daemon /usr/local/kafka_2.12-3.9.1/config/server.properties

6) 添加开机自启动

同zookeeper添加开机自启动 ,我们也可以写入启动脚本/lib/systemd/system/kafka.service

cat << 'EOF'|tee -a /lib/systemd/system/kafka.service
[Unit]
Description=Apache Kafka server (broker)
After=network.target zookeeper.service

[Service]
Type=simple
Environment="PATH=/usr/local/jdk-21.0.12/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin"
User=root
Group=root
ExecStart=/usr/local/kafka_2.12-3.9.1/bin/kafka-server-start.sh /usr/local/kafka_2.12-3.9.1/config/server.properties
ExecStop=/usr/local/kafka_2.12-3.9.1/bin/kafka-server-stop.sh /usr/local/kafka_2.12-3.9.1/config/server.properties
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable kafka

四. 回到ELK设置filebeat

1. 修改配置文件

修改/usr/local/filebeat/filebeat.yml文件

cat /usr/local/filebeat/filebeat.yml

我们需要修改的内容有:

# ----------------------------- 以上下行需要注释掉 ----------------------------- 
output.elasticsearch:                   # es输出,因为要输出到mq上所以没用了注释掉
  hosts: ["192.168.100.102:9200"]       # 同上
filebeat.config.modules:                # ssh登录日志收集,影响观看,注释掉
  path: ${path.config}/modules.d/*.yml  # 上面所需要的模块,关掉上面就没用了
  reload.enabled: true                  # 同上
setup.template.settings:                # 给ES输出用的,不使用ES一起关掉
  index.number_of_shards: 1             # 同上
setup.kibana:                           # 注释掉,仪表盘相关
# processors 中以下三行也注释掉
  - add_cloud_metadata: ~               # 云相关
  - add_docker_metadata: ~              # 容器相关
  - add_kubernetes_metadata: ~          # k8s相关

# ------------------------------ 并添加如下几行 ------------------------------ 
# ------------------------------ KAFKA output -------------------------------
output.kafka:
# 把日志发送给kafka
  enabled: true
  # 开启fafka模块
  hosts: ["192.168.100.106:9092"]
  # kafka服务器的地址
  topic: nginx_logs
  # 填写kafka的topic(即主题),可以自定义

可以使用如下命令(方法1)

sed -i  -e 's@output\.elasticsearch:@#output.elasticsearch:@' \
  -e 's@hosts: \["192.168.100.102:9200"\]@#hosts: ["192.168.100.102:9200"]@' \
  -e 's@filebeat\.config\.modules:@#filebeat.config.modules:@' \
  -e 's@path:.*@#path: ${path.config}/modules.d/*.yml@' \
  -e 's@reload\.enabled: true@#reload.enabled: true@' \
  -e 's@setup\.template\.settings:@#setup.template.settings:@' \
  -e 's@index.number_of_shards: 1@#index.number_of_shards: 1@' \
  -e 's@setup\.kibana:@#setup.kibana:@' \
  -e 's@- add_cloud_metadata: ~@#- add_cloud_metadata: ~@' \
  -e 's@- add_docker_metadata: ~@#- add_docker_metadata: ~@' \
  -e 's@- add_kubernetes_metadata: ~@#- add_kubernetes_metadata: ~@' \
/usr/local/filebeat/filebeat.yml

cat << 'EOF'|tee -a /usr/local/filebeat/filebeat.yml
# ------------------------------ KAFKA output -------------------------------
output.kafka:
# 把日志发送给kafka
  enabled: true
  # 开启fafka模块
  hosts: ["192.168.100.106:9092"]
  # kafka服务器的地址
  topic: nginx_logs
  # 填写kafka的topic(即主题),可以自定义
EOF

grep -Ev '^#|^$|^[[:space:]]+#' /usr/local/filebeat/filebeat.yml也可以把下面的内容粘贴进去(方法2)

filebeat.inputs:
- type: log
  id: my-filestream-id
  enabled: true
  paths:
    - /var/log/nginx/*.log
logging.level: warning
processors:
  - add_host_metadata:
      when.not.contains.tags: forwarded
output.kafka:
  enabled: true
  hosts: ["192.168.100.106:9092"]
  topic: nginx_logs

# 注: - type: log在 Filebeat 8.x 里已经是弃用的,推荐用- type: filestream。教程因为是基于 8.15 写的所以用 log,目前还能用。以后重装新版注意改过来。

2. 重启filebeat

[root@ELK6 ~]# ps -elf|grep 'filebeat'|grep -v 'grep'
0 S root        1367    1334  0  80   0 - 483282 futex_ 08:30 pts/0   00:00:02 ./filebeat -e -c filebeat.yml
[root@ELK6 ~]# kill 1367
[root@ELK6 ~]# cd /usr/local/filebeat/ && ./filebeat -e -c filebeat.yml

令起一终端查看

[root@ELK6 ~]# ps -elf|grep 'filebeat'|grep -v 'grep'
0 S root        1533    1418  0  80   0 - 464785 futex_ 11:09 pts/1   00:00:00 ./filebeat -e -c filebeat.yml

五. 查看kafka上所有topic信息

1.在kafka服务器上查看filebeat保存的数据,topic为nginx_logs

在kafka较新的版本(2.2以及更改版本)不再需要ZooKeeper连接字符串,即: --zookeeper localhost:2181。需要使用Kafka Broker的 --bootstrap-server localhost:9092来替代

# 先创建nginx_logs这个topic
[root@ELK7 ~]# cd /usr/local/kafka_2.12-3.9.1/bin/
[root@ELK7 bin]# kafka-topics.sh --list --bootstrap-server localhost:9092
nginx_logs

2. 启动一个消费者获取信息

[root@ELK7 bin]# kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic nginx_logs --from-beginning
{"@timestamp":"2026-08-07T03:32:16.848Z","@metadata":{"beat":"filebeat","type":"_doc","version":"8.15.3"},"log":{"offset":0,"file":{"path":"/var/log/nginx/error.log"}},"message":"test to kafka 2026年 08月 07日 星期五 11:32:09 CST","input":{"type":"log"},"ecs":{"version":"8.0.0"},"host":{"os":{"codename":"Blue Onyx","type":"linux","platform":"rocky","version":"9.6 (Blue Onyx)","family":"redhat","name":"Rocky Linux","kernel":"5.14.0-570.17.1.el9_6.x86_64"},"id":"7f4071f6cb11498f86f7912121a71a76","containerized":false,"ip":["192.168.100.105","fe80::20c:29ff:fe49:2f40"],"mac":["00-0C-29-49-2F-40"],"name":"elk6","hostname":"elk6","architecture":"x86_64"},"agent":{"type":"filebeat","version":"8.15.3","ephemeral_id":"8d7c833e-1dae-4280-bd96-6398aed31b00","id":"f7e5c125-f35c-4417-827f-027a665e5c35","name":"ELK6"}}
{"@timestamp":"2026-08-07T03:32:31.858Z","@metadata":{"beat":"filebeat","type":"_doc","version":"8.15.3"},"agent":{"version":"8.15.3","ephemeral_id":"8d7c833e-1dae-4280-bd96-6398aed31b00","id":"f7e5c125-f35c-4417-827f-027a665e5c35","name":"ELK6","type":"filebeat"},"log":{"offset":57,"file":{"path":"/var/log/nginx/error.log"}},"message":"test to kafka 2026年 08月 07日 星期五 11:32:30 CST","input":{"type":"log"},"ecs":{"version":"8.0.0"},"host":{"name":"elk6","hostname":"elk6","architecture":"x86_64","os":{"type":"linux","platform":"rocky","version":"9.6 (Blue Onyx)","family":"redhat","name":"Rocky Linux","kernel":"5.14.0-570.17.1.el9_6.x86_64","codename":"Blue Onyx"},"id":"7f4071f6cb11498f86f7912121a71a76","containerized":false,"ip":["192.168.100.105","fe80::20c:29ff:fe49:2f40"],"mac":["00-0C-29-49-2F-40"]}}
{"@timestamp":"2026-08-07T03:32:56.863Z","@metadata":{"beat":"filebeat","type":"_doc","version":"8.15.3"},"input":{"type":"log"},"ecs":{"version":"8.0.0"},"host":{"name":"elk6","hostname":"elk6","architecture":"x86_64","os":{"codename":"Blue Onyx","type":"linux","platform":"rocky","version":"9.6 (Blue Onyx)","family":"redhat","name":"Rocky Linux","kernel":"5.14.0-570.17.1.el9_6.x86_64"},"id":"7f4071f6cb11498f86f7912121a71a76","containerized":false,"ip":["192.168.100.105","fe80::20c:29ff:fe49:2f40"],"mac":["00-0C-29-49-2F-40"]},"agent":{"id":"f7e5c125-f35c-4417-827f-027a665e5c35","name":"ELK6","type":"filebeat","version":"8.15.3","ephemeral_id":"8d7c833e-1dae-4280-bd96-6398aed31b00"},"log":{"file":{"path":"/var/log/nginx/error.log"},"offset":114},"message":"test to kafka 2026年 08月 07日 星期五 11:32:50 CST"}

启动消费者去查看filebeat发送过来的消息,能看到说明我们filebeat的output,kafka配置成功。接下类配置logstash去消费kafka的数据

六. 配置logstash

配置logstash去kafka拿去数据,进行数据格式化。然后把格式化的数据保存到ElasticSearch,通过kibana展示给用户。kibana是通过ElasticSearch进行日志搜索。

filebeat中message要么是一段字符串,要么在日志生成中拼接成json,然后再filebeat中指定为json。但是大部分系统日志无法去修改日志格式,filebeat则无法通过正则去匹配出对应的filed,这时候就需要结合logstash的grok来过滤。后面还会有logstash filter使用

1. 修改logstash配置文件

我们直接覆盖写入如下内容

cat << 'EOF'|tee /etc/logstash/conf.d/http_logstash.conf
input {
    kafka {
        codec => json
        bootstrap_servers => "192.168.100.106:9092"
        # kafka节点的地址,要带端口,默认为9092
        client_id => "httpd_logs"
        # 这里设置client和group.id是为了做标识
        group_id => "httpd_logs"
        consumer_threads => 5
        # 设置消费kafka数据时开启的线程数,一个partition对应一个消费者消费。设置多个不影响,在Kafka中一个进程对应一个线程
        auto_offset_reset => "latest"
        # 从最新的偏移量开始消费
        decorate_events => true
        # 此属性会将当前topic,offset,group,partition等信息也带到message中
        topics => "nginx_logs"
    }
}

output {
    stdout {
        codec => "rubydebug"
    }
    elasticsearch {
        hosts => ["192.168.100.102:9200"]
        index => "nginx-logs-%{+yyyy.MM.dd}"
    }
}

EOF

可以使用相同的group_id方式运行多个Logstash实例,以跨物理机分布负载。主题中的消息将发布到具有相同的所有Logstash实例group_id

2. 语法测试,无误后启动服务

[root@ELK4 ~]# /usr/share/logstash/bin/logstash -t --path.settings /etc/logstash
Using bundled JDK: /usr/share/logstash/jdk
Sending Logstash logs to /var/log/logstash which is now configured via log4j2.properties
[2026-08-07T17:29:18,997][WARN ][logstash.runner          ] NOTICE: Running Logstash as a superuser is strongly discouraged as it poses a security risk. Set 'allow_superuser' to false for better security.
[2026-08-07T17:29:19,007][INFO ][logstash.runner          ] Log4j configuration path used is: /etc/logstash/log4j2.properties
[2026-08-07T17:29:19,009][INFO ][logstash.runner          ] Starting Logstash {"logstash.version"=>"8.19.19", "jruby.version"=>"jruby 9.4.15.0 (3.1.7) 2026-06-08 6114208a31 OpenJDK 64-Bit Server VM 21.0.11+10-LTS on 21.0.11+10-LTS +indy +jit [x86_64-linux]"}
[2026-08-07T17:29:19,013][INFO ][logstash.runner          ] JVM bootstrap flags: [-Xms512m, -Xmx512m, -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, -Dio.netty.allocator.maxOrder=11]
[2026-08-07T17:29:19,076][INFO ][org.logstash.jackson.StreamReadConstraintsUtil] Jackson default value override `logstash.jackson.stream-read-constraints.max-string-length` configured to `200000000` (logstash default)
[2026-08-07T17:29:19,079][INFO ][org.logstash.jackson.StreamReadConstraintsUtil] Jackson default value override `logstash.jackson.stream-read-constraints.max-number-length` configured to `10000` (logstash default)
[2026-08-07T17:29:19,080][INFO ][org.logstash.jackson.StreamReadConstraintsUtil] Jackson default value override `logstash.jackson.stream-read-constraints.max-nesting-depth` configured to `1000` (logstash default)
[2026-08-07T17:29:20,444][INFO ][org.reflections.Reflections] Reflections took 201 ms to scan 1 urls, producing 150 keys and 530 values
[2026-08-07T17:29:21,123][INFO ][logstash.javapipeline    ] Pipeline `main` is configured with `pipeline.ecs_compatibility: v8` setting. All plugins in this pipeline will default to `ecs_compatibility => v8` unless explicitly configured otherwise.
Configuration OK
[2026-08-07T17:29:21,124][INFO ][logstash.runner          ] Using config.test_and_exit mode. Config Validation Result: OK. Exiting Logstash
[root@ELK4 ~]# systemctl restart logstash

3. 访问nginx界面,并刷新产生数据

访问nginx界面,并刷新产生数据nginx: http://192.168.100.105:80

alt text

4. 查看kibana上的数据

nginx: http://192.168.100.102:5601

alt text

此时我们在kinaba上看到了最新收集的日志,至此我们的ELK+filebeat+kafka部署完成。

七. Grok插件

grok是一个十分强大的logstash filter插件,他可以通过正则解析任意文本,将非结构化的日志数据解析成结构化和方便查询的结构内容。将其定义成我们平时容易理解的一些字段名称。

vi /etc/logstash/conf.d/http_logstash.conf

执行命令

sed -i '/^output {/i\
filter {\
    grok {\
        match => {"message" => "grok_pattern"}\
    }\
}\
' /etc/logstash/conf.d/http_logstash.conf

或者在vi编辑器中将这段复制到input和output之间

filter {
    grok {
        match => {"message" => "grok_pattern"}
    }
}

这段代码中除了grok_pattern(grok表达式)以外都是logstash的关键字。grok_pattern部分需要使用者填充自己的解析方式。

grok_pattern由零个或多个%{SYNTAX:SEMANTIC}组成

其中SYNTAX是表达式的名字,即文本匹配的模式名称。是由grok提供。例如数字表达式的名字是NUMBER,IP地址的表达式名字是IP。

SEMANTIC表示解析出来的这个字符的名字,即为匹配的文本提供的标识符,由自己定义,例如IP字段的名字可以是client。

对于下面的日志

192.168.1.2 GET /index.html 15824 0.043

可以这样解析

filter {
  grok {
    match => {
      "message" => "%{IP:client} %{WORD:method} %{URIPATHARAM:request} %{NUMBER:bytes} %{NUMBER:duration}"
    }
  }
}

得到这样的结果:

* client: 192.168.1.2
* method: GET
* request: /index.html
* bytes: 15824
* duration: 0.043

数字表达式的名字是NUMBER,%{NUMBER: duration}可以匹配数值类型。但是grok匹配出的内容都是字符串的类型。可以通过在最后指定为int或者float来强制转换类型。 %{NUMBER:duration:float}

data_type 目前只支持两个值,int和float。

grok提供的SYNTAX可以查看文件grok-patterns。其默认存放的路径是

重启时在6上开启
cd /usr/local/filebeat/
./filebeat -e -c filebeat.yml