_cluster/health、_cluster/allocation/explain 和磁盘水位设置定位原因。隔离边界:只使用本机 Docker Compose 的 lab-es 项目,HTTP 只绑定 127.0.0.1:9200;不要连接或修改任何生产 ES 集群。需要 Docker Compose v2,预留约 1.5 GB 内存。
docker version --format '{{.ServerVersion}}' && docker compose version
mkdir -p /tmp/lab-es
cat > /tmp/lab-es/docker-compose.yml <<'EOF'
name: lab-es
services:
es:
image: docker.elastic.co/elasticsearch/elasticsearch:8.15.3
container_name: lab-es
environment:
discovery.type: single-node
xpack.security.enabled: "false"
ES_JAVA_OPTS: "-Xms512m -Xmx512m"
ports: ["127.0.0.1:9200:9200"]
volumes: ["esdata:/usr/share/elasticsearch/data"]
volumes:
esdata:
EOF
docker compose -f /tmp/lab-es/docker-compose.yml up -d
for i in $(seq 1 60); do
curl -fsS 'http://127.0.0.1:9200/_cluster/health' >/dev/null 2>&1 && break
sleep 2
done
curl -s 'http://127.0.0.1:9200/_cluster/health?filter_path=status,number_of_nodes,unassigned_shards&pretty'
首次启动要拉取镜像并初始化数据目录,等待循环最多约 2 分钟。
先创建 1 主 1 副的索引。单节点无法放置副本,副本会保持未分配:
curl -fsS -XPUT 'http://127.0.0.1:9200/lab-unassigned?pretty' \
-H 'Content-Type: application/json' \
-d '{"settings":{"number_of_shards":1,"number_of_replicas":1}}'
sleep 2
curl -s 'http://127.0.0.1:9200/_cluster/health/lab-unassigned?filter_path=status,number_of_nodes,unassigned_shards&pretty'
再用分配过滤排除所有节点,制造主分片也无法分配:
curl -fsS -XPUT 'http://127.0.0.1:9200/lab-filter?pretty' \
-H 'Content-Type: application/json' \
-d '{"settings":{"number_of_shards":1,"number_of_replicas":0,"index.routing.allocation.exclude._name":"*"}}'
sleep 2
curl -s 'http://127.0.0.1:9200/_cluster/health/lab-filter?filter_path=status,number_of_nodes,unassigned_shards&pretty'
对两个索引分别执行 allocation explain,并查看磁盘水位默认值:
curl -s -XGET 'http://127.0.0.1:9200/_cluster/allocation/explain?include_disk_info=true&pretty' \
-H 'Content-Type: application/json' \
-d '{"index":"lab-unassigned","shard":0,"primary":false}'
curl -s -XGET 'http://127.0.0.1:9200/_cluster/allocation/explain?pretty' \
-H 'Content-Type: application/json' \
-d '{"index":"lab-filter","shard":0,"primary":true}'
curl -s 'http://127.0.0.1:9200/_cluster/settings?include_defaults=true&filter_path=defaults.cluster.routing.allocation.disk&pretty'
预期 lab-unassigned 为 yellow、unassigned_shards=1,explain 的 allocation_status 为 no,decider 为 same_shard,说明副本不能和主分片放在同一节点。lab-filter 为 red、unassigned_shards=1,decider 为 filter,说明节点不匹配 index.routing.allocation.exclude._name。水位默认值通常是 low 85%、high 90%、flood_stage 95%,include_disk_info=true 会附带节点磁盘的可用空间和判断信息。
_cluster/health?level=indices 看是哪个索引、多少分片未分配;用 _cat/shards?v 看主分片还是副本未分配。yellow 通常只缺副本,red 表示至少一个主分片未分配。_cluster/allocation/explain 并指定 index、shard、primary,读取 decider 和 explanation。same_shard 是副本与主分片同节点冲突,filter 是分配过滤,磁盘问题看 disk decider 和 include_disk_info。cluster.routing.allocation.disk.watermark 的三个值必须使用同一种单位(全百分比或全字节)。混用会导致设置解析失败;修改前先读取 include_defaults=true 的当前值,并在变更后确认生效。先处理副本:把副本数降为 0,索引应变为 green:
curl -fsS -XPUT 'http://127.0.0.1:9200/lab-unassigned/_settings?pretty' \
-H 'Content-Type: application/json' \
-d '{"index":{"number_of_replicas":0}}'
curl -s 'http://127.0.0.1:9200/_cluster/health/lab-unassigned?wait_for_status=green&timeout=30s&filter_path=status,unassigned_shards&pretty'
再清除分配过滤,让主分片回到节点:
curl -fsS -XPUT 'http://127.0.0.1:9200/lab-filter/_settings?pretty' \
-H 'Content-Type: application/json' \
-d '{"index.routing.allocation.exclude._name":null}'
curl -s 'http://127.0.0.1:9200/_cluster/health/lab-filter?wait_for_status=green&timeout=30s&filter_path=status,unassigned_shards&pretty'
生产环境更安全的顺序是:先确认未分配的是主分片还是副本,再检查磁盘和分配过滤;副本问题可以降副本或加节点,主分片问题必须先恢复数据或节点,不能直接删除索引。磁盘超过 high watermark 时先清理或扩容,超过 flood_stage 时写入会被阻断。
curl -fsS -XDELETE 'http://127.0.0.1:9200/lab-unassigned' >/dev/null || true
curl -fsS -XDELETE 'http://127.0.0.1:9200/lab-filter' >/dev/null || true
docker compose -f /tmp/lab-es/docker-compose.yml down -v --remove-orphans
rm -rf /tmp/lab-es
只删除本实验的 Compose 项目和 esdata 卷;不要对生产集群执行删除索引或 down -v。
same_shard、filter 和磁盘水位三种原因,explain 中分别应看哪些字段?_cluster/allocation/explain 中 same_shard 与 filter 分别说明什么?