StackTraces
OpenSearch已驗證

OpenSearch 單節點集群建索引後健康狀態一直 yellow:副本分片 unassigned 排障

@debug_master今天更新閱讀 6 min00

單節點 OpenSearch 建索引後,_cluster/health 返回 status: yellow 且 unassigned_shards 不為 0,等多久都不會轉綠。根因是 index.number_of_replicas 預設 1,而副本分片不允許與主分片放在同一節點——單節點只有一個節點,副本永遠無處可去。解法是把副本數設為 0:建索引時顯式指定,或對已建索引動態更新 _settings,不用重建。本地體驗裡 yellow 本身無害。適用 OpenSearch 全版本,本文實測 3.9.0。

Issue & Env(案發現場與環境)

  • 業務場景:跟著 從零跑通 OpenSearch 用 Docker 起了單節點容器,建 movies 索引時沒指定副本數,之後查集群健康一直是 yellow。
  • 環境:OpenSearch 3.9.0,Docker 單節點(discovery.type=single-node),macOS / Linux 均可復現。
  • 關鍵區分:yellow 不是故障。主分片都在,數據照常讀寫;red 才是主分片缺失。但單節點的 yellow 也不會自己消失——它是確定性狀態,不是過渡狀態。

StackTrace(堆疊日誌)

建索引時不帶 settings,副本數走預設值:

curl -sk -u admin:Devfix@2026 -X PUT "https://localhost:9200/movies?pretty" \
  -H "Content-Type: application/json" \
  -d '{ "mappings": { "properties": { "title": { "type": "text" } } } }'

查集群健康,status 變成 yellow,unassigned_shards 是 1:

{
  "cluster_name" : "docker-cluster",
  "status" : "yellow",
  "timed_out" : false,
  "number_of_nodes" : 1,
  "number_of_data_nodes" : 1,
  "active_primary_shards" : 1,
  "active_shards" : 1,
  "relocating_shards" : 0,
  "initializing_shards" : 0,
  "unassigned_shards" : 1,
  "delayed_unassigned_shards" : 0,
  "number_of_pending_tasks" : 0
}

看分片明細,主分片 STARTED,副本分片掛在 UNASSIGNED:

curl -sk -u admin:Devfix@2026 "https://localhost:9200/_cat/shards/movies?v"
index  shard prirep state      docs store ip          node
movies 0     p      STARTED       0  220b 172.17.0.2    a937e018cee5
movies 0     r      UNASSIGNED

官方 Cluster Health API 文檔的示例響應就是這個場景,原文註明:

The response shows a yellow status because this is a single-node
cluster with replica shards that cannot be allocated.

Reproduce(復現程式碼)

最小復現,從起容器到變黃:

docker run -d -p 9200:9200 -p 9600:9600 \
  -e "discovery.type=single-node" \
  -e "OPENSEARCH_INITIAL_ADMIN_PASSWORD=Devfix@2026" \
  opensearchproject/opensearch:3.9.0

# 等 JVM 启动几十秒,先确认此时还是 green(没有索引就没有分片)
curl -sk -u admin:Devfix@2026 "https://localhost:9200/_cluster/health?pretty"

# 建索引,不带 number_of_replicas
curl -sk -u admin:Devfix@2026 -X PUT "https://localhost:9200/movies?pretty" \
  -H "Content-Type: application/json" \
  -d '{}'

# 再查健康:yellow,unassigned_shards 为 1
curl -sk -u admin:Devfix@2026 "https://localhost:9200/_cluster/health?pretty"

等 30 秒再查,狀態紋絲不動。這不是分配慢,是永遠分配不了。

Root Cause(根因分析)

副本的備份語義決定分配規則

副本分片的作用是給主分片做備份:主分片所在的節點掛了,副本頂上。如果主副兩份存同一個節點,節點一掛兩份一起丟,備份失去意義。所以 OpenSearch 的分片分配決策器 same_shard 會直接拒絕這種放置。

官方 Cluster Allocation Explain API 文檔的單節點示例裡,same_shard 決策器的原文輸出:

{
  "decider" : "same_shard",
  "decision" : "NO",
  "explanation" : "a copy of this shard is already allocated to this node [[research_papers][0], node[KfEEGG7_SsKZVFqI4ko2FA], [P], s[STARTED], a[id=SfxnuSLESQSI0Htcv0N3vA]]"
}

文檔對這個結果的定性說明,原樣引用:

This is a typical situation in single-node clusters where replica
shards cannot be allocated because OpenSearch does not allow a
primary and its replica to coexist on the same node.

你可以讓集群親口解釋這個分片為什麼分配不出去:

curl -sk -u admin:Devfix@2026 -X GET "https://localhost:9200/_cluster/allocation/explain?pretty"

不帶請求體時,它自動挑第一個未分配分片給出解釋,返回裡就是上面那段 same_shard 拒絕記錄。

預設副本數是 1,單節點永遠滿足不了

官方 Index settings 文檔寫明:index.number_of_replicas 是動態設置,不指定時跟隨集群預設 cluster.default_number_of_replicas,而後者預設值是 1。

把兩條規則連起來看:建索引不設副本數 → 每個主分片配 1 個副本 → 副本必須去「別的節點」→ 單節點集群沒有別的節點 → 副本永遠 unassigned → unassigned_shards 永遠不為 0 → 狀態永遠 yellow。鏈條上沒有一步會超時重試成功,所以等多久都沒用。

yellow 在本地無害,red 才要慌

官方博客對 unassigned shards 的解釋:yellow 表示所有主分片活躍、個別副本未分配,數據是安全且可搜索的,只是暫時失去副本冗餘;red 才是主分片缺失,對應數據不可讀寫。

所以本地學習階段,yellow 不影響任何功能,不處理也行。真正要當真的是生產多節點集群:yellow 意味著部分數據此刻沒有備份,如果承載主分片的節點恰好在這時故障,那份數據就失去訪問,要等恢復才能回來。

進階:內部索引也會把集群拖黃

論壇帖子 Number of replicas in Opensearch single node setup 實測:即便在 opensearch.yml 設了 cluster.default_number_of_replicas: 0,security-auditlog、.opendistro-ism-config 這些內部索引仍按 1 副本創建,真實輸出:

index                     shard prirep state    docs store  ip            node
security-auditlog-2023.02.15 0  p      STARTED     4  60.1kb 10.65.177.46  test-01
security-auditlog-2023.02.15 0  r      UNASSIGNED

另一帖 Single node yellow cluster ISM index 是 .opendistro-job-scheduler-lock(5 分片 1 副本)拖黃單節點,wazuh-indexer 的 Issue #87 標題就叫 “Yellow cluster status on single-node fresh install”——同一個問題在不同發行版反覆出現。社區給出的解法不是逐個改副本,而是 auto_expand_replicas,見下面方案三。

The Fix(解決方案)

方案一:建索引時就把副本設為 0

本地單節點最乾淨的做法,建索引時顯式指定:

curl -sk -u admin:Devfix@2026 -X PUT "https://localhost:9200/movies?pretty" \
  -H "Content-Type: application/json" \
  -d '{
    "settings": {
      "index.number_of_shards": 1,
      "index.number_of_replicas": 0
    }
  }'

方案二:已建索引動態改副本數

索引已經建好也不用刪。number_of_replicas 是動態設置,_settings 直接改,立即生效:

curl -sk -u admin:Devfix@2026 -X PUT "https://localhost:9200/movies/_settings?pretty" \
  -H "Content-Type: application/json" \
  -d '{ "index": { "number_of_replicas": 0 } }'

返回 "acknowledged" : true 即改成功。對比記憶:number_of_shards 是靜態(final)設置,只能在創建時指定,要改分片數必須重建索引。

方案三:auto_expand_replicas 讓副本隨節點數伸縮

以後可能擴成多節點時,用這個設置替代寫死副本數:

curl -sk -u admin:Devfix@2026 -X PUT "https://localhost:9200/movies/_settings?pretty" \
  -H "Content-Type: application/json" \
  -d '{ "index": { "auto_expand_replicas": "0-2" } }'

副本數在 0 到 2 之間自動跟隨節點數:單節點 0 副本、轉綠;擴到 3 節點自動補到每分片 2 副本。論壇帖 26435 裡對內部索引的修法就是給它們套一個含 auto_expand_replicas: "0-all" 的 index template,0-all 表示不設上限、始終等於節點數減 1。

驗證轉綠

curl -sk -u admin:Devfix@2026 "https://localhost:9200/_cluster/health?pretty"
{
  "cluster_name" : "docker-cluster",
  "status" : "green",
  "active_primary_shards" : 1,
  "active_shards" : 1,
  "unassigned_shards" : 0
}

再看分片明細,r 那行 UNASSIGNED 消失:

curl -sk -u admin:Devfix@2026 "https://localhost:9200/_cat/shards/movies?v"

注意事項

  • 本地體驗可以乾脆不管 yellow:它不影響寫入、搜索和任何 API,純粹是健康條的顏色。
  • 生產多節點別習慣性把副本設 0,那是主動放棄冗餘;單節點只是「放不下」,多節點是「沒必要」。
  • 集群裡有多個索引都拖黃時,逐個 _settings 改太笨,用 index template 統一管副本策略更省心。

References(參考來源)

最後更新於 2026-10-17

這篇幫到你了嗎?

剛解決了一個棘手的報錯?花兩分鐘記錄下來,幫助下一個遇到同樣問題的開發者。

貢獻一條解法