以前查日志:SSH登录服务器,grep、tail、awk轮番上阵,10台服务器查一圈下来半小时过去了。现在:打开Kibana,输入关键词,所有服务器的日志一秒出结果。
一、为什么需要ELK?
先说说我们之前的"原始"日志管理:
bash
# 服务器1
ssh root@192.168.1.10
tail -f /var/log/app/app.log | grep "ERROR"
# 服务器2
ssh root@192.168.1.11
tail -f /var/log/app/app.log | grep "ERROR"
# 服务器3...10
# 开10个终端窗口...
痛点:
- 10台服务器要开10个终端
- 日志量大的时候grep到眼花
- 想看历史日志?先下载再分析
- 跨服务追踪?基本不可能
- 老板问"昨天有多少报错"?数到明天也数不完
搭了ELK之后:
- 所有服务器日志集中一处
- 搜索秒出结果
- 自动生成图表
- 异常告警自动通知
二、ELK是什么?
ELK是三个开源项目的首字母:
| 组件 | 作用 | 一句话解释 |
|---|---|---|
| Elasticsearch | 存储+搜索 | 日志数据库,支持全文搜索 |
| Logstash | 收集+处理 | 从各处收集日志,清洗后存入ES |
| Kibana | 可视化 | Web界面,搜索和做图表 |
现在还经常加个Filebeat,变成EFK 或ELFK:
应用日志 → Filebeat(轻量采集)→ Logstash(处理)→ Elasticsearch(存储)→ Kibana(展示)
三、快速搭建(Docker Compose)
3.1 服务器要求
ELK比较吃资源,建议配置:
| 环境 | CPU | 内存 | 磁盘 |
|---|---|---|---|
| 测试 | 2核 | 4GB | 50GB |
| 生产 | 8核+ | 16GB+ | 500GB+ SSD |
3.2 目录结构
bash
mkdir -p /data/elk/{elasticsearch,logstash,kibana}
cd /data/elk
# 目录结构
elk/
├── docker-compose.yml
├── elasticsearch/
│ └── data/
├── logstash/
│ ├── config/
│ │ └── logstash.yml
│ └── pipeline/
│ └── logstash.conf
└── kibana/
└── config/
└── kibana.yml
3.3 docker-compose.yml
yaml
version: '3.8'
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.11.0
container_name: elasticsearch
environment:
- node.name=es01
- cluster.name=elk-cluster
- discovery.type=single-node
- bootstrap.memory_lock=true
- xpack.security.enabled=false
- "ES_JAVA_OPTS=-Xms2g -Xmx2g"
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- ./elasticsearch/data:/usr/share/elasticsearch/data
ports:
- "9200:9200"
networks:
- elk
logstash:
image: docker.elastic.co/logstash/logstash:8.11.0
container_name: logstash
volumes:
- ./logstash/config/logstash.yml:/usr/share/logstash/config/logstash.yml
- ./logstash/pipeline:/usr/share/logstash/pipeline
ports:
- "5044:5044" # Beats输入
- "5000:5000" # TCP输入
- "9600:9600" # API
environment:
- "LS_JAVA_OPTS=-Xms512m -Xmx512m"
depends_on:
- elasticsearch
networks:
- elk
kibana:
image: docker.elastic.co/kibana/kibana:8.11.0
container_name: kibana
volumes:
- ./kibana/config/kibana.yml:/usr/share/kibana/config/kibana.yml
ports:
- "5601:5601"
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
depends_on:
- elasticsearch
networks:
- elk
networks:
elk:
driver: bridge
3.4 配置文件
logstash/config/logstash.yml:
yaml
http.host: "0.0.0.0"
xpack.monitoring.elasticsearch.hosts: ["http://elasticsearch:9200"]
logstash/pipeline/logstash.conf:
conf
input {
# 接收Filebeat发来的日志
beats {
port => 5044
}
# 也可以接收TCP发来的日志
tcp {
port => 5000
codec => json
}
}
filter {
# 解析JSON格式日志
if [message] =~ /^\{.*\}$/ {
json {
source => "message"
}
}
# 解析时间戳
date {
match => ["timestamp", "yyyy-MM-dd HH:mm:ss.SSS", "ISO8601"]
target => "@timestamp"
}
# 添加自定义字段
mutate {
add_field => { "env" => "production" }
}
}
output {
elasticsearch {
hosts => ["elasticsearch:9200"]
index => "app-logs-%{+YYYY.MM.dd}"
}
# 调试时可以输出到控制台
# stdout { codec => rubydebug }
}
kibana/config/kibana.yml:
yaml
server.host: "0.0.0.0"
server.name: kibana
elasticsearch.hosts: ["http://elasticsearch:9200"]
i18n.locale: "zh-CN"
3.5 启动
bash
# 设置ES数据目录权限
chown -R 1000:1000 elasticsearch/data
# 调整系统参数(ES需要)
sysctl -w vm.max_map_count=262144
echo "vm.max_map_count=262144" >> /etc/sysctl.conf
# 启动
docker-compose up -d
# 查看状态
docker-compose ps
# 查看日志
docker-compose logs -f
3.6 验证
bash
# ES健康检查
curl http://localhost:9200/_cluster/health?pretty
# Kibana访问
# 浏览器打开 http://你的IP:5601
四、配置日志收集
4.1 安装Filebeat
在需要收集日志的服务器上安装:
bash
# CentOS
rpm -ivh https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-8.11.0-x86_64.rpm
# Ubuntu
wget https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-8.11.0-amd64.deb
dpkg -i filebeat-8.11.0-amd64.deb
# Docker方式
docker pull docker.elastic.co/beats/filebeat:8.11.0
4.2 配置Filebeat
/etc/filebeat/filebeat.yml:
yaml
filebeat.inputs:
# 收集应用日志
- type: log
enabled: true
paths:
- /var/log/app/*.log
fields:
app: my-app
env: prod
fields_under_root: true
# 多行日志合并(Java堆栈)
multiline.pattern: '^\d{4}-\d{2}-\d{2}'
multiline.negate: true
multiline.match: after
# 收集Nginx日志
- type: log
enabled: true
paths:
- /var/log/nginx/access.log
fields:
app: nginx
type: access
fields_under_root: true
# 收集系统日志
- type: log
enabled: true
paths:
- /var/log/messages
- /var/log/secure
fields:
app: system
fields_under_root: true
# 输出到Logstash
output.logstash:
hosts: ["ELK服务器IP:5044"]
# 如果直接输出到ES
# output.elasticsearch:
# hosts: ["ELK服务器IP:9200"]
# index: "filebeat-%{+yyyy.MM.dd}"
# 日志处理器
processors:
- add_host_metadata: ~
- add_cloud_metadata: ~
4.3 启动Filebeat
bash
# 测试配置
filebeat test config
# 启动
systemctl enable filebeat
systemctl start filebeat
# 查看状态
systemctl status filebeat
五、Kibana使用指南
5.1 创建索引模式
- 打开Kibana:
http://IP:5601 - 进入 Stack Management → Index Patterns
- 点击 Create index pattern
- 输入
app-logs-* - 选择时间字段
@timestamp
5.2 日志搜索(Discover)
进入 Discover:
# 搜索包含ERROR的日志
message: ERROR
# 搜索特定应用
app: my-app AND level: ERROR
# 时间范围内的错误
level: ERROR AND @timestamp >= "2024-01-01"
# 组合查询
(level: ERROR OR level: WARN) AND app: my-app AND NOT message: "expected error"
5.3 常用搜索技巧
| 需求 | 查询语法 |
|---|---|
| 包含关键词 | message: "OutOfMemory" |
| 排除关键词 | NOT message: "health check" |
| 某个字段等于 | level: ERROR |
| 某个字段存在 | _exists_: traceId |
| 正则匹配 | message: /.*Exception.*/ |
| 范围查询 | response_time: [100 TO 500] |
| 通配符 | message: *timeout* |
5.4 创建可视化图表
场景:统计每小时的错误数量
- 进入 Visualize → Create visualization
- 选择 Lens 或 Aggregation based → Line
- 配置:
- Y轴:Count
- X轴:@timestamp(Date Histogram,间隔1小时)
- 过滤:level: ERROR
场景:错误类型分布饼图
- 选择 Pie Chart
- Slice by:Terms → error_type
- Size by:Count
5.5 创建Dashboard
把多个可视化组合成仪表盘:
- 进入 Dashboard → Create dashboard
- 添加已创建的可视化
- 调整布局
- 保存
推荐的Dashboard组成:
- 日志总量趋势图
- 错误数量趋势图
- 错误类型分布
- 响应时间分布
- 各服务日志量对比
- 最近错误列表
六、Logstash高级配置
6.1 解析Nginx日志
conf
input {
beats {
port => 5044
}
}
filter {
if [fields][type] == "nginx-access" {
grok {
match => { "message" => '%{IPORHOST:client_ip} - %{DATA:user} \[%{HTTPDATE:timestamp}\] "%{WORD:method} %{URIPATHPARAM:request} HTTP/%{NUMBER:http_version}" %{NUMBER:status} %{NUMBER:bytes} "%{DATA:referrer}" "%{DATA:user_agent}"' }
}
date {
match => ["timestamp", "dd/MMM/yyyy:HH:mm:ss Z"]
target => "@timestamp"
}
geoip {
source => "client_ip"
target => "geoip"
}
useragent {
source => "user_agent"
target => "ua"
}
mutate {
convert => {
"status" => "integer"
"bytes" => "integer"
}
remove_field => ["message", "timestamp"]
}
}
}
output {
if [fields][type] == "nginx-access" {
elasticsearch {
hosts => ["elasticsearch:9200"]
index => "nginx-access-%{+YYYY.MM.dd}"
}
}
}
6.2 解析Java日志
conf
filter {
if [fields][app] == "java-app" {
# 解析标准格式:2024-01-01 12:00:00.123 [thread] LEVEL class - message
grok {
match => { "message" => "%{TIMESTAMP_ISO8601:timestamp} \[%{DATA:thread}\] %{LOGLEVEL:level} %{DATA:class} - %{GREEDYDATA:log_message}" }
}
date {
match => ["timestamp", "yyyy-MM-dd HH:mm:ss.SSS"]
target => "@timestamp"
}
# 提取异常类型
if [level] == "ERROR" {
grok {
match => { "log_message" => "%{DATA:exception_class}:%{GREEDYDATA:exception_message}" }
tag_on_failure => []
}
}
}
}
6.3 日志脱敏
conf
filter {
# 脱敏手机号
mutate {
gsub => [
"message", "1[3-9]\d{9}", "***phone***"
]
}
# 脱敏身份证
mutate {
gsub => [
"message", "\d{17}[\dXx]", "***idcard***"
]
}
# 脱敏银行卡
mutate {
gsub => [
"message", "\d{16,19}", "***bankcard***"
]
}
}
七、告警配置
7.1 使用Elastalert
bash
# 安装
pip install elastalert2
# 配置 config.yaml
rules_folder: /opt/elastalert/rules
es_host: localhost
es_port: 9200
告警规则 rules/error_alert.yaml:
yaml
name: "应用错误告警"
type: frequency
index: app-logs-*
# 5分钟内超过10次ERROR就告警
num_events: 10
timeframe:
minutes: 5
filter:
- term:
level: ERROR
alert:
- dingtalk
dingtalk_webhook: "https://oapi.dingtalk.com/robot/send?access_token=xxx"
dingtalk_msgtype: "markdown"
7.2 使用Kibana自带告警(需订阅)
进入 Stack Management → Rules and Connectors
八、异地服务器日志收集
公司有多个机房,服务器分布在不同网络,怎么把日志集中到一个ELK?
方案1:公网暴露端口(不推荐)
把Logstash的5044端口暴露到公网,安全风险大。
方案2:VPN
在各机房部署VPN客户端,打通到ELK服务器的网络。
缺点:
- 配置复杂
- 经常断连
- 影响日志实时性
方案3:SD-WAN组网(我在用的)
用星空组网把所有服务器组到一个虚拟网络:
bash
# 在ELK服务器
curl -sSL https://down.starvpn.cn/linux.sh | bash
xkcli login your_token && xkcli up
# 虚拟IP: 192.168.188.10
# 在各机房的服务器
# 同样操作,获得虚拟IP
Filebeat配置直接用虚拟IP:
yaml
output.logstash:
hosts: ["192.168.188.10:5044"]
效果:
- 配置简单,5分钟搞定
- 延迟低(P2P直连)
- 稳定,用了3个月没断过
- 安全,流量加密
九、性能优化
9.1 ES优化
索引生命周期管理(ILM):
json
PUT _ilm/policy/logs-policy
{
"policy": {
"phases": {
"hot": {
"actions": {
"rollover": {
"max_size": "50GB",
"max_age": "1d"
}
}
},
"warm": {
"min_age": "7d",
"actions": {
"shrink": { "number_of_shards": 1 },
"forcemerge": { "max_num_segments": 1 }
}
},
"delete": {
"min_age": "30d",
"actions": { "delete": {} }
}
}
}
}
9.2 Logstash优化
yaml
# logstash.yml
pipeline.workers: 4 # CPU核心数
pipeline.batch.size: 1000 # 批处理大小
pipeline.batch.delay: 50 # 批处理等待时间(ms)
9.3 Filebeat优化
yaml
# 批量发送
output.logstash:
hosts: ["192.168.188.10:5044"]
bulk_max_size: 2048
# 队列设置
queue.mem:
events: 4096
flush.min_events: 2048
flush.timeout: 1s
十、效果对比
| 指标 | 传统方式 | ELK |
|---|---|---|
| 查日志耗时 | 10-30分钟 | 几秒 |
| 跨服务器查询 | 手动逐台查 | 一次搜索全部 |
| 历史日志 | 需要下载 | 直接查 |
| 统计分析 | 写脚本 | 点几下 |
| 告警 | 无 | 自动 |
实际收益:
- 排查问题效率提升10倍
- 终于能回答"昨天有多少报错"了
- 定位问题从"猜"变成"查"
总结
ELK搭建不难,难的是:
- 日志格式要规范(JSON最好)
- 字段要统一(各应用约定好)
- 索引要管理(不然磁盘爆炸)
- 网络要打通(异地服务器用组网)
建议先从单个应用开始,跑顺了再推广到其他应用。
有问题评论区交流~