背景: 真夜中の呼び出し – 'CLUSTERDOWN Hash slot not served'
午前2時、あなたのポケットベルがけたたましく鳴り響きました。重要なアプリケーションがダウンし、ログにはRedisからCLUSTERDOWN Hash slot not servedエラーが大量に出力されています。これは単なる警告ではありません。Redisクラスターが失敗状態にあり、未割り当てのスロットにマッピングされているキーに対するデータ操作の一部またはすべてが停止していることを意味します。クラスターはこれらのスロットのデータ可用性や一貫性を保証できないため、リクエストの処理を完全に停止します。
このエラーは通常、Redisクラスターを構成する16384個のハッシュスロットのうち1つ以上が、どのプライマリノードにも割り当てられていない場合に発生します。これにつながる一般的なシナリオは以下の通りです。
- 正常なレプリカが引き継ぐことなくプライマリノードが永久に障害を起こした場合、または自動フェイルオーバーが正常に完了しなかった場合。
- プライマリノードがクラスターから不適切に削除され、そのスロットが孤立した場合。
- 不完全または失敗したリシャーディング操作。
- すべてのスロットをカバーするのに十分なノードがない状態でクラスターが起動した場合。
あなたの当面の目標は、どのスロットが未割り当てになっているかを特定し、それらを健全なプライマリノードに再割り当てして、クラスターをOK状態に戻すことです。
デバッグプロセス: 孤立したスロットの特定
まず、クラスターの状態を確認し、問題のあるスロットを特定する必要があります。redis-cliを使用して、クラスター内の任意のノードに接続します。
1. クラスターの状態を確認する
最も基本的なチェックで、クラスターが健全であるかどうかがすぐにわかります。
redis-cli -h <any_cluster_ip> -p <any_cluster_port> cluster info
cluster_stateとcluster_slots_assignedのフィールドを探してください。おそらく次のような表示になるでしょう。
cluster_state:fail
cluster_slots_assigned:16380
cluster_slots_ok:16380
cluster_slots_pfail:0
cluster_slots_fail:4
...
ここでのポイントは、cluster_state:failとcluster_slots_assignedが16384(スロットの総数)未満であることです。cluster_slots_failは、未割り当てのスロットの数を示します。
2. 未割り当てのスロットと失敗したノードを特定する
次に、どのスロットが実際に問題を引き起こしているのか、また、失敗としてマークされているノードがあるかどうかを確認しましょう。
redis-cli -h <any_cluster_ip> -p <any_cluster_port> cluster slots
このコマンドは、すべてのスロット範囲と、それらを担当するプライマリ/レプリカノードを一覧表示します。プライマリノードが関連付けられていないエントリ、またはプライマリノードがfailやPFAILステータスを表示しているエントリを探します。
また、個々のノードのステータスも確認してください。
redis-cli -h <any_cluster_ip> -p <any_cluster_port> cluster nodes
failまたはPFAILフラグを持つノードを示す行を探してください。それらのIDとIP:Portをメモします。プライマリノードがfailとしてマークされ、そのスロットがアクティブなレプリカによってカバーされていない場合、それらが孤立したスロットです。
3. redis-cli --cluster checkで検証する
redis-cli --cluster checkユーティリティは、より詳細な調査に非常に役立ちます。
redis-cli --cluster check <any_cluster_ip>:<any_cluster_port>
このコマンドは、スロットのカバー範囲、ノード接続、レプリケーションステータスなど、クラスターの健全性を包括的にチェックします。多くの場合、具体的な推奨事項を提供したり、サービスが提供されていない正確なスロットを強調表示したりします。
# 欠落したスロットを示す出力例
[ERR] Node <node_id> is not part of the cluster, but still has some slots. Please use CLUSTER RESET to fix this.
[ERR] Missing 4 slots.
[ERR] The following slots are not covered by any node: 1024-1027
!!! Some of the cluster hash slots are not covered. Rebalance is advised. !!!
解決策: サービスが提供されていないスロットの再要求
問題が分かったので、修正しましょう。アプローチは、スロットを提供していた元のノードが復旧可能か、または永久に失われたかによって異なります。
シナリオA: ノードが一時的にダウンしている、または復旧可能である場合
以前にサービスが提供されていないスロットを所有していたノードが単にダウンしているだけの場合(例: 再起動、ネットワーク障害など)、最も簡単な解決策はそれをオンラインに戻すことです。ノードがクラスターに再参加すると、そのスロットを再要求し、クラスターの状態はOKに戻るはずです。
# 例: Redisサービスの再起動
sudo systemctl start redis-server@<port> # systemdシステムの場合
# または、サービスマネージャーを使用していない場合は、redis-serverプロセスを手動で起動します
再起動後、cluster infoとcluster nodesを監視します。ノードが復旧し、そのスロットを再要求すれば、問題ありません。
シナリオB: ノードが永久に失われた、または手動での介入が必要な場合
プライマリノードが完全に失われた場合、またはオンラインに戻しても問題が解決しなかった場合(例: データが破損していたなど)は、そのスロットを他の健全なプライマリノードに再割り当てする必要があります。ここでredis-cli --clusterが役立ちます。
オプション1: redis-cli --cluster fixを使用する(旧バージョン/より単純なケース)
より単純なケース、特に古いRedisバージョンでは、fixは一貫した状態を再確立しようとすることで、未割り当てのスロットを含む軽微な不整合を解決できる場合があります。
redis-cli --cluster fix <any_cluster_ip>:<any_cluster_port>
このコマンドは、一般的なクラスターの問題を自動的に修正しようとします。深く断片化されたクラスターや永久に失われたプライマリに対しては常に機能するとは限りませんが、最初の試みとしては良いでしょう。
オプション2: 既存のノードへのスロットのリシャーディング(最も一般的な修正方法)
これは、孤立したスロットを再割り当てする最も堅牢な方法です。残りの健全なプライマリノード間で、未割り当てのスロットを分散させます。
redis-cli --cluster reshard <any_cluster_ip>:<any_cluster_port>
このコマンドは、いくつかの情報の入力を求めます。
- 移動するスロットの数(1~16384)はいくつですか?
cluster infoまたはcluster checkで見つかった未割り当てのスロットの数を入力します。例えば、cluster_slots_fail:4の場合は4と入力します。 - 受信ノードIDは何ですか? これは、これらの新しいスロットを引き受ける既存の健全なプライマリノードのIDです。ノードIDは
redis-cli cluster nodesから取得できます。すでに過負荷になっていないノードを選択してください。 - ソースノードIDは? これらのスロットは未割り当て(孤立)であるため、スロットが現在保持されている任意のソース(未割り当てスロットの場合は「none」であっても)から取得できることを示すために、おそらく
allと入力するでしょう。特定のノードIDを求められた場合で、スロットが失敗したノードからのものであれば、クラスターにまだ認識されている場合は失敗したノードのIDを入力する必要があるかもしれません。または、単にallを使用します。真に未割り当てのスロットの場合、通常はallが正しいアプローチです。
計画を確認すると、リシャーディングプロセスが開始されます。これにより、これらのスロットに関連付けられたデータ(回復されたものや存在していたものがあれば)が新しいプライマリノードに移動されます。
オプション3: 新しいプライマリノードを追加してリシャーディングする
クラスターがすでに容量不足である場合、またはスロットとデータの均等な分散を維持したい場合は、まず新しいプライマリノードを追加し、次に未割り当てのスロットをそれにリシャーディングすることを検討してください。
ステップ1: 新しいノードをプライマリとして追加する
redis-cli --cluster add-node <new_node_ip>:<new_node_port> <any_existing_cluster_ip>:<any_existing_cluster_port> --cluster-master
これにより、新しいノードが最初はスロットなしのプライマリとして追加されます。
ステップ2: 新しいプライマリにスロットをリシャーディングする
redis-cli --cluster reshard <any_existing_cluster_ip>:<any_existing_cluster_port>
プロンプトが表示されたら:
- 移動するスロットの数(1~16384)はいくつですか? 未割り当てのスロットの数を入力します。
- 受信ノードIDは何ですか? 先ほど追加した新しいプライマリノードのIDを入力します。
- ソースノードIDは?
allと入力します。
検証手順: 修正の確認
修正を試みた後は、クラスターが完全に動作していることを確認することが重要です。
1. クラスターの状態を再度確認する
redis-cli -h <any_cluster_ip> -p <any_cluster_port> cluster info
これで次のように表示されるはずです。
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
...
2. スロットのカバー範囲を確認する
redis-cli --cluster check <any_cluster_ip>:<any_cluster_port>
これにより、未割り当てのスロットがなく、健全なクラスター構造であることが報告されるはずです。
3. データ操作をテストする
いくつかの基本的な読み取りおよび書き込み操作を実行して、異なるキー間でデータが正しく保存および取得できることを確認します。これにより、さまざまなスロットが使用されます。
redis-cli -h <any_cluster_ip> -p <any_cluster_port> SET mykey "hello from fixed cluster"
redis-cli -h <any_cluster_ip> -p <any_cluster_port> GET mykey
redis-cli -h <any_cluster_ip> -p <any_cluster_port> SET anotherkey "another value"
redis-cli -h <any_cluster_ip> -p <any_cluster_port> GET anotherkey
これらのチェックがすべて合格すれば、おめでとうございます!Redisクラスターは正常に稼働しています。
学んだ教訓と予防策
-
堅牢な監視: 個々のノードの状態だけでなく、Redisクラスター全体に対して包括的な監視を実装してください。
cluster_state、cluster_slots_assigned、およびノードフラグ(fail、PFAIL)を追跡します。okではないクラスター状態でのアラートは非常に重要です。 -
適切なノードの削除: スロットを適切に移行するか、レプリカがフェイルオーバーできることを確認せずに、プライマリノードを単に停止しないでください。スロットが移動/レプリケートされていることを確認した後、
redis-cli --cluster del-nodeを使用してください。 -
レプリケーションファクター: プライマリノードがダウンした場合に自動フェイルオーバーが発生するように、十分なレプリケーションファクター(少なくとも1、つまり各プライマリに少なくとも1つのレプリカがあること)を確保してください。
-
自動バックアップ: 定期的なRDBスナップショットまたはAOF永続化は、壊滅的なシナリオにおけるデータ損失に対する最後の防衛線です。
-
スロット分散の理解: スロット範囲を手動で扱ったり、クラスター内のキー分散を理解しようとしたりする場合、特定のキーがどのスロットに属するべきかを検証するために、簡単なハッシュ計算が役立つことがあります。そのために、私はよくToolCraftのハッシュジェネレーターのようなブラウザベースのツールを使用します。これは、データをどこにも送信せずに迅速なチェックを行うのに便利で、特にキーのスロットを確認したり、複雑なリシャーディング操作中にキーが期待されるノードにヒットしない理由をデバッグしたりする場合に役立ちます。

