單節點 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(參考來源)
- Cluster Health API — OpenSearch 官方文檔opensearch-docs
- Cluster Allocation Explain API — OpenSearch 官方文檔opensearch-docs
- Index settings — OpenSearch 官方文檔opensearch-docs
- Error log: Unassigned Shards – Why Your Cluster is Showing Yellow or Red — OpenSearch 官方博客opensearch-blog
- Number of replicas in Opensearch single node setup — OpenSearch 論壇opensearch-forum
- Single node yellow cluster ISM index — OpenSearch 論壇opensearch-forum
- Yellow cluster status on single-node fresh install · Issue #87 · wazuh/wazuh-indexergithub-issue
