上一篇〈OpenSearch 無法取消的 Zombie Task 處理與預防〉最後是靠重新啟動節點解決的,但節點回來之後,叢集一直停在 yellow,有 5 個 shard 一直是 unassigned,等了很久都沒有自己分配回去。
原因是這幾個 shard 在節點出問題期間已經重試分配失敗 5 次,達到上限,之後就算原本卡住的原因排除了,OpenSearch 也不會再自動重試,要手動觸發。這篇整理處理方式,以及幾個相關的分配指令。
確認 unassigned 的原因
以下指令的 $ES 代表 OpenSearch 位址,有開安全外掛的話改用 https 並加上帳密(建議用 .netrc,不要把密碼寫在指令裡)。
ES=https://opensearch.example.com:9200
先列出哪些 shard 沒分配,以及原因:
curl -s "$ES/_cat/shards?v&h=index,shard,prirep,state,node,unassigned.reason" | grep -v STARTED
prirep 欄位的 p 是 primary、r 是 replica。unassigned.reason 常見的有 NODE_LEFT(節點離開)、ALLOCATION_FAILED(分配失敗)、INDEX_CREATED(剛建立還沒分配)。
再用 allocation explain 看詳細原因。不帶參數會解釋第一個找到的 unassigned shard:
curl -s "$ES/_cluster/allocation/explain?pretty"
指定某個 shard:
curl -s -X POST "$ES/_cluster/allocation/explain?pretty" \
-H 'Content-Type: application/json' -d '{
"index": "graylog_120",
"shard": 0,
"primary": false
}'
這次看到的重點是這幾行:
"unassigned_info" : {
"reason" : "ALLOCATION_FAILED",
"failed_allocation_attempts" : 5,
...
},
"can_allocate" : "no",
"allocate_explanation" : "cannot allocate because allocation is not permitted to any of the nodes",
再往下看 node_allocation_decisions,每個節點的 decider 會寫出不能分配的理由,這次是 max_retry:
shard has exceeded the maximum number of retries [5] on failed allocation attempts -
manually call [/_cluster/reroute?retry_failed=true] to retry
OpenSearch 自己就在訊息裡告訴你要怎麼做了。
手動觸發重試
curl -s -X POST "$ES/_cluster/reroute?retry_failed=true&pretty" \
-H 'Content-Type: application/json' -d '{}'
retry_failed=true
把所有因為分配失敗而停住的 shard 重試次數歸零,重新嘗試分配-d '{}'
reroute 本來可以在 body 帶分配指令,這裡不需要,送一個空的 JSON 就好,帶著Content-Type比較不會遇到格式錯誤
執行後觀察叢集狀態,shard 會進入 INITIALIZING,再變成 STARTED:
# 分配與復原進度
curl -s "$ES/_cat/recovery?v&active_only=true"
# 叢集狀態,等到 green
curl -s "$ES/_cluster/health?pretty&wait_for_status=green&timeout=5m"
這次 5 個 shard 一下就分配回去,叢集回到 green。
使用時機
- allocation explain 看到
failed_allocation_attempts達到 5,而且決策理由是max_retry - 原本卡住的原因(shard lock、磁碟空間、節點離線)已經排除
注意:如果原因還沒排除就重試,只會再失敗 5 次又停住。例如磁碟使用率超過水位線(watermark)造成的分配失敗,要先清出空間,重試才有意義。
重試上限可以依 index 調整(index.allocation.max_retries,預設 5),但一般不需要改,與其調高上限,不如找出一直失敗的原因。
其他分配相關指令
預覽 reroute 結果
加上 dry_run=true 只模擬、不實際執行,有疑慮時先跑一次:
curl -s -X POST "$ES/_cluster/reroute?retry_failed=true&dry_run=true&pretty" \
-H 'Content-Type: application/json' -d '{}'
檢查分配是否被關閉
維護時常會暫時關閉 shard 分配,結束後忘了打開,shard 也會一直 unassigned:
# 看目前設定
curl -s "$ES/_cluster/settings?pretty&include_defaults=false"
# 恢復分配(設回 null 就是預設值 all)
curl -s -X PUT "$ES/_cluster/settings" -H 'Content-Type: application/json' -d '{
"persistent": { "cluster.routing.allocation.enable": null }
}'
檢查磁碟水位線
curl -s "$ES/_cat/allocation?v"
磁碟使用率超過 low 水位線(預設 85%)就不會再分配新的 replica 到該節點,超過 high(預設 90%)會開始把 shard 搬走。Graylog 環境最常見的就是保留期限設太長、磁碟滿了。
強制分配 primary(最後手段)
如果 primary shard 遺失、所有正常的副本都救不回來,才考慮強制分配。有兩種:
# 用節點上「過期的副本」當 primary,可能遺失最近寫入的資料
curl -s -X POST "$ES/_cluster/reroute?pretty" -H 'Content-Type: application/json' -d '{
"commands": [{
"allocate_stale_primary": {
"index": "graylog_120",
"shard": 0,
"node": "opensearch-node2",
"accept_data_loss": true
}
}]
}'
# 分配一個「全新的空 primary」,該 shard 原本的資料全部遺失
curl -s -X POST "$ES/_cluster/reroute?pretty" -H 'Content-Type: application/json' -d '{
"commands": [{
"allocate_empty_primary": {
"index": "graylog_120",
"shard": 0,
"node": "opensearch-node2",
"accept_data_loss": true
}
}]
}'
node 填的是節點名稱(_cat/nodes 看得到的 name)或節點 ID,不是 IP。
注意:這兩個指令都會遺失資料,accept_data_loss 一定要設 true 才能執行,就是要你確定自己在做什麼。能用 allocate_stale_primary 就不要用 allocate_empty_primary;如果有快照,從快照還原會比強制分配好。replica 遺失時不需要用這兩個,primary 還在就會自動重建 replica。
Graylog 的另一個選項
如果卡住的是 Graylog 已經不在寫入的舊 index,而且資料不重要,也可以考慮直接在 Graylog 的「System → Indices」刪掉那個 index,比強制分配單純。寫入中的 index 則不要這樣做。
結論
shard 一直 unassigned 時,先用 allocation explain 看原因,大部分情況它會直接告訴你下一步。像這次 max_retry 的狀況,排除原本的問題後一行 retry_failed=true 就解決了,不要一開始就去用 allocate_empty_primary,那是真的救不回來才用的。
參考資料
- OpenSearch 文件:Cluster reroute
https://docs.opensearch.org/latest/api-reference/cluster-api/cluster-reroute/ - OpenSearch 文件:Cluster allocation explain
https://docs.opensearch.org/latest/api-reference/cluster-api/cluster-allocation/ - OpenSearch 文件:CAT shards
https://docs.opensearch.org/latest/api-reference/cat/cat-shards/ - OpenSearch 無法取消的 Zombie Task 處理與預防
https://blog.jason.tools/2026/04/opensearch-zombie-tasks.html



