この記事は、はてなエンジニア Advent Calendar 2017の 20 日目の記事です。19 日目の記事は yasuhiro_onishi さんのScratchを使った子どもへのプログラミング教育 — 大西ブログでした。
今年の 2 月にはてなに入社して、あと 1 ヶ月と少しで 1 年を迎えようとしています。はてなに入社してから Mackerel のインフラと時系列データベースの AWS 移行プロジェクトを任されたり、移行を終えた後も後片付けや移行後の運用を担当したりと、主に Mackerel にコミットしていた 1 年でした。自分が Mackerel にコミットしたところは Mackerel のイベントで登壇したいくつかの登壇資料にもあるとおり数多いのですが、この記事では、運用する中で試行錯誤を重ねて苦労した Mackerel を支える時系列データベースのコンポーネントとして利用している Redis Cluster の運用と Mackerel を使ったモニタリング/監視について書いています。
Redis Cluster は複数台の Redis ノードから構成される分散 KVS です。
ひとまず触ってみたいという方にはチュートリアルがオススメです。より内部の実装に精通したいという方は詳細なドキュメントがオフィシャルで提供されていますがかなり内容的にも重いので、要所をかいつまんで Redis Cluster の運用とモニタリング/監視について書いています。
Redis Cluster のキーの分散配置
Redis Cluster はクラスターを構成する各ノードにキーが分散配置されていますが、ノードに対してシャードが割り当てられ、シャードに対してスロットが割り当てられ、スロットに対してキーが割り当てられており、キーの分散配置を実現しています。スロットは 16384 個存在し、これは CRC16(key) mod 16384
と式でハッシュ関数と剰余演算によって、キーが割り当てられるスロットが決まります。スロットは各シャードに割り当てられるのですがそのシャードの中では master, slave となる Redis ノードがある構成をとっていて、 分散 KVS としては理解しやすいアーキテクチャになっています。イメージとしては複数の Redis を利用してクライアントでそれぞれの Redis ノードにキーを分散させる Redis の活用として Redis Ring に似ています。
Redis Ring と異なるところでは Redis Cluster はスロットに偏りが発生した場合にはこれを別のシャードへと移動することでスケールアウトさせることができます。また、アプリケーション側でも {bar}{zap}
というようにキー zap
のハッシュスロットを bar
と同じとなるよう指定することで特定のシャードにキーを配置することができ、スケーラビリティを確保したまま運用することができます。
Redis Cluster のモニタリング
Redis Cluster を構成するノードは単一の Redis と変わらないため、Redis と同じメトリックを取得してモニタリングを行うことができます。 Mackerel では公式プラグイン mackerelio/mackerel-agent-plugins: Plugins for mackerel-agent の mackerel-plugin-redis を使うことで Redis に様々なメトリックを取得できます。以下は mackerel-plugin-redis によるモニタリングの様子です(この記事のための環境なので負荷はないですが)。
Redis の CPU 使用率
Redis のメモリ使用量
Redis のレプリケーション遅延
Redis Cluster のモニタリングで主に見ているメトリックは上に挙げた cpu_user、 used_memory, lag の 3 つです。
Redis はシングルスレッドで動作するため、ホストがマルチコア CPU を搭載していても CPU リソースをフルに使用することができません。そのため、 CPU 使用率はボトルネックになりやすく注視すべきメトリックの一つです。CPU リソースが原因で特定のシャードがシステム全体のボトルネックになっている場合、スロットを移動して重い処理を他のシャードに逃がすしかありません。
redis.conf の max_memory で指定されている値を基準にして Mackerel のメトリック監視機能を使ってアラートラインを設定しています。溢れそうになれば cpu_user の対応と同様に他のシャードにスロットを移動するか、RAM を積んだ slave をシャードに追加して F/O させる方法があります。
Redis Cluster では読み込みの処理を slave ノードだけに向けることができます。これを利用している場合、レプリケーション遅延が大きく発生している場合にアプリケーションが意図しているデータが読み取れない可能性があるため、メトリック監視を used_memory 同様に行っています。レプリケーション遅延と関連して変動しているメトリックを探ることでレプリケーション遅延の原因調査を楽に行うことができます。
Redis Cluster の監視
Reids ノード単体は Redis Redis Cluster の内部状態は次のコマンドで取得できます。
このコマンドのレスポンスの読み方は CLUSTER NODES — Redis に記載されていますが、1 行 1 ノードの情報を含んでおり、以下のフォーマットに従っています。
一覧してわかりづらい項目を取り上げて解説します。
master ノードであれば “-” となり、 slave ノードであれば master ノードの ID を表します。
クラスタ間の近隣ノードでの疎通に関して gossip protocol による疎通パケットの送信時間、受信時間(ミリ秒)です。
ノードの状態のバージョンをエポックで表現しています。 master と slave の関係にあるノードが共通の値を持ち、シャードごとではユニークな値を持っています。クラスター内に新しくノードが追加されたり、フェイルオーバが発生したり、スロットの移動などのイベントが発生すると、操作が行われたノードのこの値が増加しレスポンスが更新されます。複数のノードに同じスロットが割り当てられるような状態になると、大きい config-epoch を持つシャードが優先されるようクラスター内の状態が変更されます。
Redis Cluster を監視するにあたり、監視したいのはクラスターの状態や状態変化の発見です。そこで、先の項目を見ると一番有効利用できそうなのは config-epoch です。一定間隔でクラスター内のノードの config-epoch を取得し、更新されたタイミングでアラートを発すると、クラスター内のイベントについて検知できそうです。 Mackerel ではチェック監視という機能があり、この機能に基づいて config-epoch の更新を監視するチェックプラグインが check-redis-cluster プラグインになります。
設定は以下のように mackerel-agent.conf に書くだけで他のチェックプラグインと同様にイベントを検知します。
利用上の注意としては check-redis-cluster プラグインは実行時に CLUSTER NODES
コマンドを送信して、受け取ったレスポンスの config-epoch と前回 CLUSTER NODES
コマンド送信時のレスポンスの config-epoch を比較して変更があれば exit status 2 で終了します。check-redis-cluster プラグインは直前の CLUSTER NODES
コマンド送信時のレスポンスだけを扱うため、 config-epoch の更新が検知されアラートが送信された場合でも自動的にクローズされます。これを防ぐため prevent_alert_auto_close = true
を指定することをおすすめします。アラートが送信されたときには CLUSTER NODES
コマンドのレスポンスを担当者が見ることで問題を把握することができます。
Redis Cluster の運用
Redis Cluster の操作は redis-trib.rb
で行います。 Debian 8 の場合 redis-tools パッケージに含まれており /usr/share/doc/redis-tools/examples/redis-trib.rb
にあります。 redis-trib.rb
を使ったオペレーションの例を書いています。
非常に便利なコマンドなのですが、これだけでは把握しづらいノード ID をコマンドラインに書く必要があり、オペレーションが把握しづらいです。運用を楽にするために rcc というコマンドセットを golang で作成しています。この中で個人的に気に入っているのは Redis Cluster のノードの関係を木構造で表現したり、 IP アドレスの代わりにホストの FQDN を出力してくれる rcc-tree コマンドと master ノードに対して slave ノードを追加する rcc-add-slave コマンドです。
CLUSTER NODES
コマンドではわかりづらいノードの関係がパッと分かるのでとても便利です。
このコマンドは先に紹介した redis-trib.rb
による slave ノードの追加操作と同じ操作を行います。ノード ID を指定せずに簡単に slave ノードが追加できるので便利です。
Redis Cluster を EC2 上で運用すると決まったところから手を付けはじめたため、内部は大雑把な作りになっていますが実用は充分なため現役で役立っています。一方でまだできる操作が少ないことやノード追加時のクラスターの状態をハンドリングできていないため、今後の実装の課題となっています。
さいごに
いまのところ、Mackerel で運用している Redis Cluster では大きなトラブルは起きていません(Redis Cluster コワクナイヨ)。
最近では AWS Elasticache でも Redis Cluster がサポートされていてマネージドサービスを使って簡単に Redis Cluster を運用できます。ただ、作成できるシャードに 15 シャードの制限があったり、特定のシャードだけインスタンスタイプを変更したりできず、多少かゆいところに手が届かない思いをするところがあるので Mackerel では使用するに至っていません。しかし、そういった要件を気にせずに、ひとまずは Redis のスケーラビリティを担保したい場合には Redis Cluster の導入として Elasticache は利用しやすい選択肢かと思います。この記事が Elasticache 上の Redis Cluster のモニタリング/監視にも役立ち、より多くの Redis Cluster の運用事例が出てくると嬉しい思いです。
明日のアドベントカレンダーの担当は takuya-a さんです。昨年は文字列アルゴリズムの学びかたという超大作でしたが、今年も楽しみですね!!