«

Elasticsearch 问题大全

myluzh 发布于 阅读:5 Kubernetes


0x00 前言

本文整理几个 Elasticsearch 日常运维里比较常见的问题。

主要包含:

我这里示例以 Elasticsearch 7.x 为主,8.x 大部分 API 也能参考,但认证和安全默认配置可能会有差异。

0x01 通用变量

下面命令里统一使用这几个变量,后面直接复制比较方便。

ES_URL="http://10.84.38.43:9200"
ES_USER="elastic"
ES_PASS="你的密码"

如果没有开启账号密码认证,把 -u "$ES_USER:$ES_PASS" 去掉即可。

先确认 ES 能访问:

curl -u "$ES_USER:$ES_PASS" "$ES_URL"

查看集群健康状态:

curl -u "$ES_USER:$ES_PASS" "$ES_URL/_cluster/health?pretty"

0x02 License 过期

现象

日志里出现类似关键字:

current license is non-compliant for [security]

常见情况是试用许可证过期,导致部分安全功能不可用。

查看当前 License

curl -u "$ES_USER:$ES_PASS" "$ES_URL/_license?pretty"

也可以在 Kibana 的 Dev Tools 里执行:

GET /_license

切换到 Basic License

如果只是想恢复免费 Basic License,可以执行:

curl -u "$ES_USER:$ES_PASS" \
  -X POST "$ES_URL/_license/start_basic?acknowledge=true"

Kibana Dev Tools:

POST /_license/start_basic?acknowledge=true

验证

curl -u "$ES_USER:$ES_PASS" "$ES_URL/_license?pretty"

确认返回里 typebasicstatusactive

注意:Basic License 是免费许可证,但不包含所有付费能力。如果原来用了付费功能,切换后需要确认业务是否依赖这些功能。

0x03 分片数量达到上限

现象

创建索引或写入数据时报错:

this action would add [10] shards, but this cluster currently has [3000]/[3000] maximum normal shards open

意思是当前集群打开的 normal shard 数已经达到上限。

查看当前分片数量

curl -u "$ES_USER:$ES_PASS" "$ES_URL/_cat/shards?v"

只统计分片数量:

curl -s -u "$ES_USER:$ES_PASS" "$ES_URL/_cat/shards?h=index,shard,prirep,state" | wc -l

查看当前集群配置:

curl -u "$ES_USER:$ES_PASS" \
  "$ES_URL/_cluster/settings?include_defaults=true&pretty" | grep -A5 max_shards_per_node

临时提高分片上限

如果确认磁盘、内存、节点负载都还能支撑,可以临时提高 cluster.max_shards_per_node

Kibana Dev Tools:

PUT _cluster/settings
{
  "persistent": {
    "cluster.max_shards_per_node": 10000
  }
}

curl:

curl -u "$ES_USER:$ES_PASS" \
  -X PUT "$ES_URL/_cluster/settings" \
  -H 'Content-Type: application/json' \
  -d '{
    "persistent": {
      "cluster.max_shards_per_node": 10000
    }
  }'

注意

这个只是放大上限,不是根治。

真正要处理的是:

如果小索引太多,只提高上限会让集群状态越来越重,后面 master 压力也会变大。

0x04 给索引添加生命周期规则

说明

ILM 可以自动处理索引生命周期,比如日志索引保留 7 天后自动删除。

这里用 k8s-* 这类日志索引做示例。

1、创建生命周期策略

Kibana Dev Tools:

PUT _ilm/policy/7d-delete
{
  "policy": {
    "phases": {
      "delete": {
        "min_age": "7d",
        "actions": {
          "delete": {}
        }
      }
    }
  }
}

curl:

curl -u "$ES_USER:$ES_PASS" \
  -X PUT "$ES_URL/_ilm/policy/7d-delete" \
  -H 'Content-Type: application/json' \
  -d '{
    "policy": {
      "phases": {
        "delete": {
          "min_age": "7d",
          "actions": {
            "delete": {}
          }
        }
      }
    }
  }'

2、给现有索引绑定 ILM

下面脚本会把 7d-delete 策略应用到所有 k8s-* 索引。

#!/bin/bash

ES_URL="http://10.84.38.43:9200"
ES_USER="elastic"
ES_PASS="你的密码"

POLICY_NAME="7d-delete"
INDEX_PATTERN="k8s-*"

indices=$(curl -s -u "$ES_USER:$ES_PASS" "$ES_URL/_cat/indices/$INDEX_PATTERN?h=index")

if [ -z "$indices" ]; then
  echo "没有匹配到索引: $INDEX_PATTERN"
  exit 0
fi

for index in $indices; do
  echo "给索引绑定 ILM: $index -> $POLICY_NAME"

  curl -s -u "$ES_USER:$ES_PASS" \
    -X PUT "$ES_URL/$index/_settings" \
    -H 'Content-Type: application/json' \
    -d "{
      \"index.lifecycle.name\": \"$POLICY_NAME\"
    }"

  echo
done

3、给新索引自动绑定 ILM

只给现有索引加 ILM 不够,新建出来的索引还要通过模板自动带上策略。

PUT _index_template/k8s-log-template
{
  "index_patterns": ["k8s-*"],
  "template": {
    "settings": {
      "index.lifecycle.name": "7d-delete",
      "number_of_shards": 1,
      "number_of_replicas": 1
    }
  }
}

4、验证

查看索引是否绑定生命周期:

curl -u "$ES_USER:$ES_PASS" \
  "$ES_URL/k8s-*/_ilm/explain?pretty"

注意:给已有索引绑定删除策略时,如果索引创建时间已经超过 min_age,可能很快被删除。生产环境先找一两个索引测试,不要直接全量跑。

0x05 删除 N 天前的索引

说明

如果没有配置 ILM,也可以临时用脚本删除历史索引。

下面脚本按索引名里的日期判断,比如:

k8s-app-2025.05.01
k8s-nginx-2025.05.02

匹配规则是:

k8s.*-YYYY.MM.DD

1、安装依赖

这里使用 7.x Python 客户端:

pip3 install 'elasticsearch<8'

2、删除脚本

默认是 DRY_RUN = True,只打印不删除。确认没问题后再改成 False

from datetime import datetime, timedelta
import re
from elasticsearch import Elasticsearch, exceptions

ES_URL = "http://10.84.38.43:9200"
ES_USER = "elastic"
ES_PASS = "你的密码"

DAYS = 30
INDEX_PATTERN = re.compile(r"k8s.*-(\d{4}\.\d{2}\.\d{2})$")
DRY_RUN = True

es = Elasticsearch(
    [ES_URL],
    http_auth=(ES_USER, ES_PASS),
    timeout=30
)

expire_time = datetime.utcnow() - timedelta(days=DAYS)
indices = es.cat.indices(format="json")

old_indices = []

for item in indices:
    index_name = item["index"]
    match = INDEX_PATTERN.match(index_name)
    if not match:
        continue

    index_date = datetime.strptime(match.group(1), "%Y.%m.%d")
    if index_date < expire_time:
        old_indices.append(index_name)

print(f"下面索引超过 {DAYS} 天:")
for index_name in old_indices:
    print(index_name)

if DRY_RUN:
    print("当前是 DRY_RUN 模式,没有执行删除。")
    exit(0)

for index_name in old_indices:
    try:
        response = es.indices.delete(index=index_name, ignore=[400, 404])
        if response.get("acknowledged", False):
            print(f"删除成功: {index_name}")
        else:
            print(f"删除失败: {index_name}, response={response}")
    except exceptions.ConnectionError as e:
        print(f"连接 ES 失败: {e}")
    except exceptions.ElasticsearchException as e:
        print(f"删除索引异常: {index_name}, error={e}")

print("处理完成。")

3、执行

python3 delete_old_indices.py

确认输出没问题后,把脚本里的:

DRY_RUN = True

改成:

DRY_RUN = False

再执行一次。

0x06 统计索引大小

1、直接用 cat indices 查看

先用 ES 自带接口看,通常已经够用。

curl -u "$ES_USER:$ES_PASS" \
  "$ES_URL/_cat/indices?v&s=store.size:desc&h=index,docs.count,pri.store.size,store.size"

只看某个前缀:

curl -u "$ES_USER:$ES_PASS" \
  "$ES_URL/_cat/indices/jaeger-*?v&s=store.size:desc&h=index,docs.count,pri.store.size,store.size"

字段说明:

2、用 Python 汇总

如果想算总大小,可以用下面脚本。

pip3 install requests

es_index_size.py

import requests
from requests.auth import HTTPBasicAuth

ES_URL = "http://10.84.38.43:9200"
ES_USER = "elastic"
ES_PASS = "你的密码"
INDEX_PATTERN = "jaeger-*"

auth = HTTPBasicAuth(ES_USER, ES_PASS)

resp = requests.get(
    f"{ES_URL}/{INDEX_PATTERN}/_stats/store",
    auth=auth,
    timeout=30
)
resp.raise_for_status()

data = resp.json()
indices = data.get("indices", {})

if not indices:
    print("没有匹配到索引")
    exit(0)

items = []
total_size = 0

for index_name, index_data in indices.items():
    size = index_data["total"]["store"]["size_in_bytes"]
    total_size += size
    items.append((index_name, size))

items.sort(key=lambda x: x[1], reverse=True)

for index_name, size in items:
    mb = size / 1024 / 1024
    gb = size / 1024 / 1024 / 1024
    print(f"Index: {index_name}, Size: {mb:.2f} MB == {gb:.6f} GB")

print("-" * 80)
print(f"索引总数: {len(items)}")
print(f"总大小: {total_size / 1024 / 1024:.2f} MB == {total_size / 1024 / 1024 / 1024:.6f} GB")

执行:

python3 es_index_size.py

0x07 总结

这几个问题本质上都和日志类索引的生命周期有关。

我的建议:

如果是 Kubernetes 里跑 Elasticsearch,建议优先用 ECK 管理集群,证书、账号、滚动升级都会省很多事。

参考链接

k8s kubernetes elastic elasticsearch es 问题 排错 索引 ILM


正文到此结束
版权声明:若无特殊注明,本文皆为 Myluzh Blog 原创,转载请保留文章出处。
文章内容:https://itho.cn/k8s/604.html
文章标题:《Elasticsearch 问题大全