igozhang

——

    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=rUNASSIGNED 为单节点副本无法分配(预期现象);主分片 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 进一步控制长期磁盘增长

    后续预防

    1. 确认 cron 已生效:cat /etc/cron.d/es-retention-10.igo.5.20
    2. 每周检查:df -h /data/_cat/nodes 磁盘字段
    3. 不建议重置 elastic 密码(Logstash 配置依赖该账号,重置会导致写入中断)
    4. 若日志量继续增长,考虑缩短保留至 21 天或扩容 /data

    报告日期:2026-07-02

    MP3