一度のRedisデータベース構成による接続数の漏洩の問題を記す


問題の背景


昨年のクリスマス当日、突然私が手がけたプロジェクトの警告メールが届き、「Redis::CommandError:ERR max number of clients reached」というエラーメッセージが表示されました.
どんな状況ですか.まさかこのプロジェクトは転覆したのですか?第一反応はこのサーバーが自作のRedisデータベースを実行しているが、クライアントは同じイントラネットのRuby on Railsのアプリケーションしかないのに、どうして接続数が爆発する可能性があるのだろうか.

りろんせつぞくすうけいさん


老いぼれは指をつまんで計算する.
  • sidekiqクライアントに必要な接続数:対向Railsアプリケーションには10個のUnicornワークプロセスがあり、unicornプロセスごとにsidekiqクライアントを初期化し、sidekiqクライアントのデフォルト接続プールサイズは5であり、怠惰なポリシーであり、必要に応じて接続され、最大値は10 x 5=50である.
  • 明示的なRedis接続:プログラムコードに$redisグローバル変数があり、redis接続、10ワークプロセス、すなわち10接続を初期化した.
  • sidekiqサービス側に必要な接続数:sidekiq server側concurrency構成は10であり、公式ドキュメントに従って、2つの接続、すなわち12の接続を追加する.
  • Rails cacheに必要な接続数:redis-store gemソースコードに従って、デフォルトの接続プールサイズは5、10個のunicornワークプロセスで、オンデマンド接続で、最大値は10 x 5=50です.

  • 他にもRedis接続が使用される可能性があることを考慮しない場合、現在知られている最大Redis接続数の需要は122であり、この数はRedis理論の最大接続数よりはるかに小さいでしょう.そして、接続数が万に達したことを示しています.しかもこのプロジェクトはもうアクセスが少なく、圧力が極めて小さく、理論に必要な接続数に達する可能性は低いでしょう.
    きっと何か神秘的な力がこのすべてを主導しているに違いない!!!

    モニタリングと分析


    以上の理論の最大接続数分析は定性分析にすぎず、いくつかの奇妙なものが存在することを大まかに説明するしかなく、本当に問題の根源を確認したいなら、定量分析をしなければならない.データだけがすべてを説明することができる.

    初歩的な観察:Redisデータベースサーバー側監視


    遅ればせながら、データを収集するには、最初のステップは監視を加えることなので、その時、Redisクライアントの数(redis内蔵CLIENT LISTコマンドを使用)をタイミング的に収集するスクリプトを緊急に書き、crontabと組み合わせてタイミング的に実行し、結果をファイルに書き込み、後続の分析の基礎とした.
    スクリプトを監視することで、いくつかの興味深い現象を発見します.
  • Redisデータベースサービス側から採取したデータを見ると、ずっと1台のイントラネットマシン、つまり私が前に言ったRailsプログラムがあるサーバーからの接続しかなく、このRedisデータベースが他のアプリケーションに共有される可能性がないことを示しています.
  • は3日程度のモニタリングを経て、12.25から12.28まで、3日連続でRedis接続数が着実に上昇し、平均して毎日70-80増加した.典型的なシステムリソース漏洩クラス(メモリ漏洩など)の問題のシーンでは、このような線が特によく知られているように見えますが、本当に接続数が漏洩していますか?

  • さらなる分析:Redisデータベースサーバ側とクライアント接続数の比較分析


    前回の発見があった後、私は引き続きシステム命令sudo netstat -apnt6379ポートの接続数を検査して発見して、クライアントの機械もやっと42個ぐらいredisサーバーの端に接続して、最初の理論の接続数の分析を結びつけて、この数は比較的に合理的です.
    でも!でも!逆にサービス側機器は同じコマンドでチェックしますが、サービス側の視点では300+個のクライアントが確立した接続があり、ESTABLISHED状態です!この数は上の別の監視方式で得られた数と一致しています!
    いったいどんな状況ですか.このような操作はまだありますか?

    問題の根源.


    これで、Redis接続数が漏れたのは板に釘を打ったことだが、なぜだろうか.そのため、私はネット上で多くの質問と文章を検索して、それからやっと答えを見つけて、案の定、やはりデフォルトの配置の問題です.

    Redisのデフォルト設定


    redisは、クライアント接続数が多すぎることを避けるために、接続の空き時間がtimeoutの値を超えた場合、接続を閉じることを意味するtimeout構成がある。デフォルトの構成は0です。タイムアウト制限がなく、接続を閉じないことを意味します。生産上は明らかに0は配置されていませんが...

    OMG!急いで私たちのredisのプロファイルを開いて、そうかどうかを検証します.案の定、redisはデフォルトの構成を維持しています.
    これで,なぜ接続数が漏洩したのか,多くの空きがあるか,実際にクライアントが切断した接続がサーバ側に残っているため,説明が容易になる.では、どのような状況がこのような状況を引き起こすのでしょうか.
    私の推測:
  • ネットワーク通信差:TCPプロトコルに従って、クライアントが接続を切断した時、サーバー側にFIN信号を送信したが、サービス側は受信せず、クライアントがタイムアウトした後、待つことを放棄し、直接切断した.サービス側は通信障害のため、ESTABLISHED状態を維持したが、両端の機械が同じイントラネットにあるため、ネットワーク品質が理由がなくだめである.
  • クライアント異常:クライアント接続後、コード実行中に異常が発生したため、正常に接続を解放または閉じることができず、sidekiqのworkerにこのような問題が発生する可能性があります.これの可能性は非常に大きく、結局私は日常的にバグを書いています(/ω╲).

  • 問題の修正


    問題の根源を見つけてから、修復するのは簡単すぎる.実際、開発分野では、ほとんどの時間をバグ探しに費やしていますが、バグを直すには1分もかからないかもしれません.
    まず、次のredisデータベース構成を変更しました.
    redisを正常に再起動した後、前のモニタリングスクリプトを再実行して、修復後の状況を観察し、サーバー側とクライアントの接続数が一致していることを確認します.
    さらに数日のスクリプトが自動的にデータを収集した後、分析すると、システムは安定した運行を回復し、接続数は理論最大接続数の下に安定していた.

    まとめ


    この問題の根源は実は小さいが、調査過程には多くの時間がかかり、主に十分なデータが収集されて分析に使用されるのを待つ必要がある.その他の心得:
  • 「事件現場」の保護は重要で、問題の根源を掘り起こすには、環境を再現しなければならない.
  • オープンソースソフトウェアを使用するには、デフォルト構成に警戒しなければならない.redisが0.0.0.0ソース要求をデフォルトで傍受したセキュリティ・ホールを聞いたことがある人がいると信じている.
  • このプロジェクトは開始が早いため、当時はRedisクラウドデータベースを使用することを考慮していなかったが、自己構築データベースにはリスクがあり、慎重に対応する必要があり、できるだけ専門的なことは、専門的な人に任せる必要がある.