ELK 分片故障排查
ENV
| 类别 | 信息 |
|---|---|
| 主机名 | igo-elk01 |
| IP | 10.igo.5.23/24,网关 10.igo.5.254 |
| 操作系统 | CentOS Linux 7 (Core),内核 3.10.0-1160.el7.x86_64 |
| 虚拟化 | KVM 虚拟机,x86_64 |
| CPU | 8 vCPU,Intel Core2 Duo T7700 @ 2.40GHz |
| 内存 | 15 GiB 总量,运行期约 11 GiB 已用 |
| 系统盘 | /dev/mapper/centos-root,XFS,35 GiB |
| 数据盘 | /dev/vdb 挂载 /data,XFS,4.0 TiB |
| ES 数据路径 | /data/elasticsearch |
| ES JVM | -Xms7933m -Xmx7933m,G1GC |
| 集群规模 | 单节点(number_of_nodes: 1) |
| 组件服务 | elasticsearch.service、kibana.service、logstash.service 均为 running |
| Logstash 索引规则 | %{[fields][ip]}-%{+YYYY.MM.dd}、syslog-%{+YYYY.MM.dd} |
| Kibana 连接 ES | 用户 logadmin,hosts http://10.igo.5.23:9200 |
| Logstash 写入 ES | 用户 elastic |
现象
Kibana 界面提示:「63 个分片有 1 个失败」。
集群健康检查为 red,问题索引 10.igo.5.20-2026.07.02 主分片未分配;同时存在大量副本分片 UNASSIGNED(集群显示 108 个未分配分片)。
结论与根因
| 项目 | 内容 |
|---|---|
| 结论 | 磁盘使用率超过 Elasticsearch flood stage 水位线,导致当日新索引主分片无法分配;Kibana 据此报「1 个分片失败」。清理历史索引释放空间后,故障索引已恢复为 yellow 并可写入。 |
| 直接根因 | /data 磁盘使用率达 96%,超过默认 flood_stage 95%,索引 10.igo.5.20-2026.07.02 主分片 UNASSIGNED。 |
| 深层根因 | 10.igo.5.20-* 按天建索引,单索引约 44–75 GiB/天,无自动清理策略,约 63 天数据占满 4 TiB 数据盘。 |
定位推导过程
1. 确认组件运行状态
ps -ef | grep -i elastic
ps -ef | grep -i kibana
ps -ef | grep -i logstash
systemctl list-units --type=service --state=running --no-pager --no-legend | grep -E "elastic|kibana|logstash"
关键输出: 三个组件进程均存在,对应 systemd 服务均为 active running。
2. 集群健康与异常分片
curl -s -u "logadmin:***" "http://127.0.0.1:9200/_cluster/health?pretty"
curl -s -u "logadmin:***" "http://127.0.0.1:9200/_cat/shards?v&h=index,shard,prirep,state,node,unassigned.reason&s=state" | grep -vE "STARTED|index"
关键输出:
"status" : "red"
"unassigned_shards" : 108
10.igo.5.20-2026.07.02 0 p UNASSIGNED INDEX_CREATED
大量 prirep=r 的 UNASSIGNED 为单节点副本无法分配(预期现象);主分片 p 未分配才是 red 的直接原因。
3. 磁盘与分配说明
curl -s -u "logadmin:***" "http://127.0.0.1:9200/_cat/nodes?v&h=name,disk.used_percent,disk.avail,disk.total"
curl -s -u "logadmin:***" -H "Content-Type: application/json" -X POST "http://127.0.0.1:9200/_cluster/allocation/explain?pretty" \
-d '{"index":"10.igo.5.20-2026.07.02","primary":true,"shard":0}'
df -h /data
关键输出:
disk.used_percent 96.22 disk.avail 154.6gb
/dev/vdb 4.0T 3.9T 155G 97% /data
分配说明返回 can_allocate: yes,但 last_allocation_status: no,结合磁盘水位判断为磁盘满导致分配受阻。
4. 索引占用分析
curl -s -u "logadmin:***" "http://127.0.0.1:9200/_cat/indices/syslog-*,10.igo.5.20-*?v&h=index,store.size&bytes=b&s=index" | awk '...'
grep -R "index =>" /etc/logstash/conf.d/
关键输出:
| 前缀 | 索引数 | 合计大小 |
|---|---|---|
| syslog-YYYY.MM.DD | 42 | 147.9 GiB |
| 10.igo.5.20-YYYY.MM.DD | 63 | 3791.1 GiB |
Logstash 配置确认按天滚动:index => "%{[fields][ip]}-%{+YYYY.MM.dd}"。
5. 问题形成原因推测
10.igo.5.20 来源日志量极大(约 60 GiB/天),自 2026 年 5 月起持续按天建索引且无保留策略。至 2026-07-02 凌晨创建 10.igo.5.20-2026.07.02 时,/data 已达 96%,触发 flood stage,当日主分片无法分配,Kibana 报分片失败。
处置方案
方案一:紧急释放磁盘(已执行)
删除 10.igo.5.20-2026.05.* 共 31 个索引(约 1998.8 GiB)。
logadmin无删除权限,需使用elastic超级用户;通配符删除被action.destructive_requires_name禁止,须用显式索引名列表。
获取 elastic 凭据:
grep -E "user =>|password =>" /etc/logstash/conf.d/logstash-sample.conf
验证 elastic 权限:
curl -s -u 'elastic:***' "http://127.0.0.1:9200/_security/_authenticate?pretty"
执行删除:
indices=$(curl -s -u "elastic:***" "http://127.0.0.1:9200/_cat/indices/10.igo.5.20-2026.05.*?h=index" \
| awk '{printf "%s%s", (NR>1?",":""), $1}')
curl -s -u "elastic:***" -X DELETE "http://127.0.0.1:9200/${indices}?pretty"
验证:
curl -s -u "elastic:***" "http://127.0.0.1:9200/_cluster/health?pretty"
curl -s -u "elastic:***" "http://127.0.0.1:9200/_cat/nodes?v&h=name,disk.used_percent,disk.avail"
curl -s -u "elastic:***" "http://127.0.0.1:9200/_cat/indices/10.igo.5.20-2026.07.02?v&h=index,health,docs.count,store.size"
df -h /data
处置后结果:
| 指标 | 处置前 | 处置后 |
|---|---|---|
| 集群状态 | red | yellow |
| ES 磁盘使用率 | 96.22% | 47.41% |
| /data 使用率 | 97% | 48% |
| 问题索引 | red,主分片未分配 | yellow,可写入 |
方案二:配置 30 天自动保留(推荐)
脚本: /usr/local/bin/es-retention-10.igo.5.20.sh
定时任务: /etc/cron.d/es-retention-10.igo.5.20,每天 02:30 执行
# 安装(在 igo-elk01 上以 root 执行)
sudo bash -c 'cat > /usr/local/bin/es-retention-10.igo.5.20.sh << '\''SCRIPT'\''
#!/bin/bash
set -euo pipefail
RETENTION_DAYS=30
ES_USER="elastic"
ES_HOST="http://127.0.0.1:9200"
LOG_FILE="/var/log/es-retention-10.igo.5.20.log"
CONF="/etc/logstash/conf.d/logstash-sample.conf"
ES_PASS=$(grep "password =>" "${CONF}" | head -1 | sed "s/.*\"\([^\"]*\)\".*/\1/")
CUTOFF=$(date -d "${RETENTION_DAYS} days ago" +%Y%m%d)
to_delete=""
while IFS= read -r idx; do
[ -z "${idx}" ] && continue
datestr=$(echo "${idx}" | sed -n "s/^10\.igo\.5\.20-\([0-9]\{4\}\)\.\([0-9]\{2\}\)\.\([0-9]\{2\}\)$/\1\2\3/p")
[ -z "${datestr}" ] && continue
if [ "${datestr}" -lt "${CUTOFF}" ]; then
if [ -z "${to_delete}" ]; then to_delete="${idx}"; else to_delete="${to_delete},${idx}"; fi
fi
done < <(curl -s -u "${ES_USER}:${ES_PASS}" "${ES_HOST}/_cat/indices/10.igo.5.20-*?h=index")
ts=$(date "+%Y-%m-%d %H:%M:%S")
if [ -z "${to_delete}" ]; then
echo "${ts} 保留${RETENTION_DAYS}天(截止<${CUTOFF}) 无需删除" >> "${LOG_FILE}"
exit 0
fi
echo "${ts} 删除索引(保留${RETENTION_DAYS}天,截止<${CUTOFF}): ${to_delete}" >> "${LOG_FILE}"
result=$(curl -s -u "${ES_USER}:${ES_PASS}" -X DELETE "${ES_HOST}/${to_delete}")
echo "${ts} 结果: ${result}" >> "${LOG_FILE}"
SCRIPT
chmod 700 /usr/local/bin/es-retention-10.igo.5.20.sh
echo "30 2 * * * root /usr/local/bin/es-retention-10.igo.5.20.sh" > /etc/cron.d/es-retention-10.igo.5.20
chmod 644 /etc/cron.d/es-retention-10.igo.5.20
touch /var/log/es-retention-10.igo.5.20.log'
预览(不删除):
RETENTION_DAYS=30
CUTOFF=$(date -d "${RETENTION_DAYS} days ago" +%Y%m%d)
echo "截止<${CUTOFF} 的索引将被删除"
curl -s -u "elastic:***" "http://127.0.0.1:9200/_cat/indices/10.igo.5.20-*?h=index&s=index" \
| while read idx; do
datestr=$(echo "$idx" | sed -n 's/^10\.igo\.5\.20-\([0-9]\{4\}\)\.\([0-9]\{2\}\)\.\([0-9]\{2\}\)$/\1\2\3/p')
[ -n "$datestr" ] && [ "$datestr" -lt "$CUTOFF" ] && echo " 删除: $idx"
done
日志查看:
tail -20 /var/log/es-retention-10.igo.5.20.log
方案三:单节点副本数优化(可选)
curl -s -u "elastic:***" -H "Content-Type: application/json" -X PUT \
"http://127.0.0.1:9200/10.igo.5.20-*,syslog-*/_settings?pretty" \
-d '{"index":{"number_of_replicas":0}}'
参数建议
| 参数 | 当前值 | 建议值 | 原因 | 预期效果 |
|---|---|---|---|---|
/data 磁盘使用率 |
曾达 97%,现 48% | < 85% | 默认 high watermark 90%、flood stage 95% | 避免再次触发只读/分片分配失败 |
10.igo.5.20-* 保留天数 |
无(曾累积 63 天) | 30 天 | 30 天 × ~60 GiB/天 ≈ 1.8 TiB,4 TiB 盘留足余量 | 自动清理,防止磁盘再次打满 |
number_of_replicas |
1 | 0(单节点) | 仅 1 个 data 节点,副本无法分配 | 集群变 green,消除 77 个无意义 unassigned 分片 |
| 磁盘告警阈值 | 未配置 | 85% 告警 / 90% 紧急 | 早于 ES 水位线介入 | 在 flood stage 前人工干预 |
syslog-* 保留 |
无策略,约 148 GiB | 60 天或按需 | 单索引仅 ~4 GiB/天,优先级低于 10.igo.5.20 | 进一步控制长期磁盘增长 |
后续预防
- 确认 cron 已生效:
cat /etc/cron.d/es-retention-10.igo.5.20 - 每周检查:
df -h /data及/_cat/nodes磁盘字段 - 不建议重置
elastic密码(Logstash 配置依赖该账号,重置会导致写入中断) - 若日志量继续增长,考虑缩短保留至 21 天或扩容
/data
报告日期:2026-07-02