Redis接続エラーの効果的なトラブルシューティング

Redisの接続問題に悩んでいませんか?この実践ガイドでは、「接続拒否」「タイムアウト」「認証失敗」などの一般的なエラーを診断・解決するための明確な手順を提供します。サーバーステータス、ネットワーク設定、ファイアウォール、Redisパフォーマンスメトリクスの確認方法を学びます。`redis-cli`やクライアントライブラリを使用した実践的な例を含み、Redis接続を効率的に復旧させます。

Redis接続エラーの効果的なトラブルシューティング

Redisの接続エラーは、通常、次の3つの質問に分けると明確になります。クライアントはホストとポートに到達できるか、Redisは接続を受け入れるか、接続後にクライアントはコマンドを実行できるか。

この順序で行ってください。Redisが停止しているときにアプリケーションコードに飛びつくと時間を無駄にします。パスワードが間違っているときにファイアウォールルールを再構築するのは時間の無駄です。小さくて再現可能なチェックリストを使えば、実際の障害に早くたどり着けます。

まず、アプリケーションと同じ場所からテストする

ラップトップからのテストは有用ですが、Kubernetesポッド、VM、コンテナ、CIランナーがRedisに到達できることを証明するものではありません。障害が発生しているアプリケーションと同じネットワーク内から開始してください。

redis-cli -h redis.example.internal -p 6379 PING

期待される出力:

PONG

RedisがTLSを必要とする場合は、デプロイメントで期待されるTLSオプションを使用します:

redis-cli --tls -h redis.example.internal -p 6380 PING

Redisが認証を必要とする場合:

redis-cli -u redis://app-user:[email protected]:6379 PING

シェル履歴にパスワードが残らないように注意してください。本番環境では、可能な限り一時的な認証情報または環境変数を使用してください。

接続拒否

ECONNREFUSEDConnection refused、またはCould not connect to Redisは、通常、TCP接続がターゲットホストに到達したが、そのポートで何も受け入れなかったことを意味します。最も一般的な原因は単純です:

  • Redisが実行されていない。
  • クライアントが間違ったホストまたはポートを使用している。
  • Redisがlocalhostにのみバインドされている。
  • コンテナまたはサービスマッピングが間違ったポートを指している。
  • ファイアウォールが接続を積極的に拒否している。

Redisホストで、プロセスとリスナーを確認します:

redis-cli PING
ps aux | grep '[r]edis-server'
ss -ltnp | grep redis

Redisが期待されるアドレスとポートでリッスンしていることを確認します。一般的には127.0.0.1:63790.0.0.0:6379、またはプライベートインターフェースアドレスです。

redis.confまたは有効な設定を確認します:

redis-cli CONFIG GET bind
redis-cli CONFIG GET port
redis-cli CONFIG GET protected-mode

bind127.0.0.1の場合、リモートクライアントは直接接続できません。これは意図的であることが多いです。Redisが認証、ACL、ファイアウォールルール、プライベートネットワークで保護されていない限り、簡単な修正として0.0.0.0に変更しないでください。パブリックインターネットに公開されたRedisは、重大なセキュリティインシデントの発生を待っているようなものです。

Dockerでは、コンテナポートとホストポートの違いを覚えておいてください:

docker ps
docker port <redis-container>

Docker Composeネットワーク内では、アプリケーションは通常、サービス名と内部ポートに接続します:

redis://redis:6379

ホストからは、マッピングに応じてlocalhost:6379localhost:6381などの公開ポートに接続する場合があります。

接続タイムアウト

タイムアウトは、クライアントが待機し、時間内に操作を完了できなかったことを意味します。接続拒否とは異なり、タイムアウトは多くの場合、パスの問題またはサーバーのビジー状態を示します。

TCPパスを確認します:

nc -vz redis.example.internal 6379
ping -c 5 redis.example.internal

pingは完全ではありません。ICMPがブロックされていてもTCPが機能する可能性があるためですが、明らかなDNSやルーティングの誤りを明らかにすることができます。ncはRedisクライアントが必要とするものに近いです。

TCPが接続してもRedisコマンドがタイムアウトする場合は、Redisがビジーかどうかを確認します:

redis-cli INFO clients
redis-cli INFO stats
redis-cli INFO memory
redis-cli SLOWLOG GET 10
redis-cli LATENCY DOCTOR

ブロックされたクライアント、高い接続クライアント数、maxmemoryに近いメモリ、ホスト上のスワップ、低速コマンド、レイテンシイベントを探します。KEYS *のような単一の高価なコマンド、大規模なHGETALL、または長いLuaスクリプトは、Redisのコマンド実行がほぼシングルスレッドであるため、無関係なクライアントを遅延させる可能性があります。

また、クライアントのタイムアウト設定も確認してください。一部のライブラリは、接続タイムアウトまたはコマンドタイムアウトに短いデフォルト値を使用します。タイムアウトを増やすと、低速ネットワークでの誤った障害が減る可能性がありますが、過負荷のRedisインスタンスを隠すべきではありません。アプリケーションホストからの単純なPINGに数秒かかる場合は、リトライを調整する前にその問題を修正してください。

名前解決とエンドポイントの誤り

すべての接続エラーがRedisに起因するわけではありません。DNSとサービスディスカバリーも多くの原因となります。

アプリケーションホストから:

getent hosts redis.example.internal
nslookup redis.example.internal

Kubernetesの場合:

kubectl exec -it deploy/my-app -- sh
getent hosts redis.default.svc.cluster.local
nc -vz redis.default.svc.cluster.local 6379

アプリケーションが読み取りレプリカエンドポイント、センチネルエンドポイント、クラスターエンドポイント、または直接ノードエンドポイントのいずれを使用しているかを確認してください。Redis Clusterクライアントは、キーが異なるスロットに属する可能性があり、コマンドがリダイレクトを受ける可能性があるため、クラスター対応のライブラリが必要です。クラスター非対応のクライアントは正常に接続しても、実際のコマンドを送信するとMOVEDまたはASKエラーで失敗する可能性があります。

認証エラー

認証失敗は次のように表示されます:

  • NOAUTH Authentication required
  • WRONGPASS invalid username-password pair
  • NOPERM this user has no permissions
  • クライアントライブラリ固有の認証例外

Redis 6以降では、ACLユーザーが一般的です。接続文字列にはユーザー名とパスワードの両方が必要な場合があります:

redis://app-user:[email protected]:6379/0

デフォルトユーザーの場合、一部のクライアントはパスワードのみを使用します:

redis://:[email protected]:6379/0

管理者アクセス権がある場合は、アクティブなユーザー設定を確認します:

redis-cli ACL LIST
redis-cli ACL GETUSER app-user

NOAUTHは、クライアントがコマンドを発行する前に認証しなかったことを意味します。WRONGPASSは、認証が試行されたが拒否されたことを意味します。NOPERMは、認証は成功したが、ユーザーにコマンド、キーパターン、またはPub/Subチャネルの権限がないことを意味します。

シークレットがローテーションされた場合、実行中のすべてのプロセスが実際に新しい値を受け取ったことを確認してください。コンテナプラットフォームでは、シークレットオブジェクトを更新しても、既存のポッドやプロセスが常に再起動されるとは限りません。よくある実際の障害は、アプリケーションの半分が新しいパスワードを使用し、残りの半分が古いパスワードを使用していることです。

TLSの不一致

TLSの誤りは、接続リセット、タイムアウト、または解読不能なプロトコルエラーのように見えることがあります。

ポートを確認してください。マネージドサービスでは、TLS用と非TLS用のRedisで異なるポートを使用することがよくあります。たとえば、あるエンドポイントはプレーンなRedisプロトコルを期待し、別のエンドポイントは最初のバイトからTLSを期待する場合があります。

以下でテストします:

redis-cli --tls -h redis.example.internal -p 6380 PING
redis-cli -h redis.example.internal -p 6379 PING

組織がプライベート証明書を使用している場合、クライアントはCAファイルも必要になる場合があります:

redis-cli --tls --cacert /path/to/ca.pem -h redis.example.internal -p 6380 PING

アプリケーションログでは、証明書エラーは多くの場合、トップレベルのRedis例外よりも明確です。未知の認証局、期限切れの証明書、ホスト名の不一致、またはハンドシェイクの失敗に関するメッセージを探してください。

接続数が多すぎる

Redisにはmaxclients制限があります。オペレーティングシステムにもファイル記述子の制限があります。いずれかが使い果たされると、新しいクライアントが失敗したり、既存のクライアントの動作が不安定になったりする可能性があります。

確認:

redis-cli INFO clients
redis-cli CONFIG GET maxclients
ulimit -n

有用なフィールドには、INFO statsからのconnected_clientsblocked_clientsrejected_connectionsが含まれます。

接続数が多すぎるのは、通常、次のパターンのいずれかに起因します:

  • Webリクエストごとに新しいRedisクライアントを作成する。
  • 短命なジョブでクライアントを閉じない。
  • ワーカープロセスが多すぎて、それぞれが独自の大きなプールを持っている。
  • Pub/Subサブスクリプションが通常のコマンドプールから接続を借用している。
  • Redis再起動中のリトライストーム。

制限を引き上げる前に、アプリケーションの形状を修正してください。プロセスごとに1つの共有クライアントまたは制限付きプールを使用します。ジッターを追加した再接続バックオフを追加して、障害後にすべてのインスタンスが同じミリ秒で再接続しないようにします。

保護モードとバインド設定

Redisの保護モードは、偶発的な露出による被害を減らすように設計されています。Redisが広くバインドされていて認証がない場合、保護モードはリモート接続を拒否する可能性があります。

確認:

redis-cli CONFIG GET protected-mode
redis-cli CONFIG GET bind
redis-cli CONFIG GET requirepass

リモート接続を機能させるためだけに保護モードを無効にしないでください。より安全な方法は、通常、プライベートネットワークと認証、および狭いバインドアドレスです。Redisがリモートクライアントを受け入れる必要がある場合は、プライベートサブネットに配置し、送信元IPを制限し、認証情報を要求し、必要に応じてTLSを使用してください。

実用的な操作順序

アプリケーションが接続できない場合、次の順序を使用します:

  1. アプリケーション環境から、同じホストとポートに対してredis-cli PINGを実行します。
  2. 拒否された場合は、Redisプロセス、リスナー、バインド、ポート、コンテナマッピングを確認します。
  3. タイムアウトした場合は、ルーティング、ファイアウォールルール、サーバー負荷、低速コマンド、クライアントタイムアウト設定を確認します。
  4. 認証に失敗した場合は、ユーザー名、パスワード、ACL権限、シークレットのロールアウトを確認します。
  5. 一部のコマンドのみが失敗する場合は、ACLコマンド/キー権限とRedis Clusterリダイレクトを確認します。
  6. 負荷がかかると失敗する場合は、接続数、プールサイズ、リトライ、サーバーリソースメトリクスを確認します。

接続トラブルシューティングは、主に証拠収集です。アプリケーションと同じ場所からクリーンなCLI結果を取得し、それをクライアントライブラリの動作と比較します。これら2つのパスが異なる場合、ギャップは通常、目に見えます。欠落しているTLSフラグ、古いパスワード、間違ったサービス名、またはRedisが処理できるサイズをはるかに超える接続を作成するプールです。

アプリケーションエラーの読み取りと過剰反応の回避

クライアントライブラリは、Redisエラーを独自の言語でラップします。Node.jsサービスはECONNRESETを表示し、Pythonワーカーはredis.exceptions.ConnectionErrorを表示し、Javaサービスはプール取得タイムアウトを報告する場合があります。これらはすべて、同じ問題の異なる層を説明している可能性があります。

それらを分離します:

  • 接続タイムアウト: TCP接続が十分に迅速に完了しなかった。
  • 読み取りタイムアウト: 接続は存在するが、コマンド応答が時間内に到着しなかった。
  • 接続リセット: Redis、プロキシ、ネットワーク、またはピアによって接続が閉じられた。
  • プールタイムアウト: アプリケーションが独自のプールからRedis接続を借用できなかった。
  • 認証エラー: Redisが認証情報または権限を拒否した。

プールタイムアウトは、Redisの障害と誤解されやすいです。Redisは正常でも、アプリケーションがすべてのプール接続を借用し、それらを返却しない場合があります。Pub/Subの誤用がこれを引き起こす可能性があります。また、長時間のブロッキングコマンド、クライアントを閉じるのを忘れたリクエストハンドラ、またはプロセスの同時実行性に対して小さすぎるプールも原因となります。

両側を同時に確認します。アプリケーションでは、ライブラリが公開している場合はプールメトリクスを検査します。アクティブな接続、アイドル接続、待機者、リトライ回数です。Redisでは、以下を確認します:

redis-cli INFO clients
redis-cli CLIENT LIST | head

Redisが少数のクライアントしか表示しないのに、アプリケーションがプールが枯渇したと言っている場合、問題はおそらくアプリケーションプロセス内にあります。Redisが同じデプロイメントから数千の接続を表示する場合、サービスがクライアントを作成しすぎている可能性があります。

リトライには特に注意が必要です。バックオフのない再接続ループは、短いRedis再起動をストームに変える可能性があります。すべてのアプリケーションインスタンスがすぐに再接続を試み、認証とTLSハンドシェイクが急増し、Redisはクライアントから攻撃を受けながら回復する必要があります。指数バックオフとジッターを使用してください。また、どのコマンドがリトライしても安全かを判断します。べき等なキャッシュGETのリトライは、接続が切断される前にすでに成功している可能性のある書き込みのリトライとは異なります。

インシデントノートについては、正確なエラーテキストとタイミングをキャプチャします。「Redisがダウンしていた」は多くの場合間違っています。「14:03から14:06 UTCまで、アプリポッドは読み取りタイムアウトを確認し、Redis CPUは1コアで、SLOWLOGは大規模なHGETALL呼び出しを示していた」は実行可能です。