ZOZOTOWNにおけるキャッシュストアのリプレイス

OGP

こんにちは、ECプラットフォーム部の濱砂とSRE部の杉山、柴田です。普段はZOZOTOWNのリプレイスや運用に携わっています。

ZOZOTOWNでは、アプリケーションレイヤーで使用しているキャッシュストアをAmazon ElastiCache(以下、ElastiCache)にリプレイスしました。本記事では、リプレイスに至った背景や方法、発生した課題などについてご紹介します。

プロジェクトの概要

キャッシュストアのリプレイスとは

ZOZOTOWNでは、現在システムリプレイスを進めています。その中で、Web(IIS)サーバーのメモリ領域に保持しているセッション情報を外部メモリストアにオフロードするプロジェクト(以下、セッションオフロード)があります。キャッシュストアのリプレイスはセッションオフロードのフェーズ1でターゲットとしていました。

後続のフェーズに本丸であるセッションオフロードや、Cookieやセッションに保持している情報のデバイス間共有などがあります。

image

リプレイス前のキャッシュストア

キャッシュストアとクライアント側のライブラリは、パートナー企業様にご提供頂いている製品を使用しており、VBScriptからそのライブラリを呼び出してキャッシュの機能を実現していました。

キャッシュするデータの種類は主にDBから取得したレコードセットや参照系APIのレスポンスなどがあります。シンプルな構成で、長年運用してきたこともあり、非常に高速で安定したシステムでした。

image

リプレイスした背景

では、なぜ安定していたシステムをリプレイスする必要があったのかについてご紹介します。

運用コストの削減

リプレイス前は、自前でキャッシュストアを構築、運用していました。現時点で大きな課題ではありませんでしたが、今後サービスの規模が大きくなり、マイクロサービス化も進むことで運用するキャッシュストアの数や規模が増え、運用コストも増え続けていくことが予想されました。

そこで、フルマネージドサービスであるAmazon ElastiCacheやSaaS、PaaSなどのクラウドのサービスを活用して、運用の負担を抑えられるようにしました。

一般的な製品、技術への移行

先述の通り、リプレイス前は、パートナー企業様にご提供いただいているキャッシュストアを使用していました。安定して運用ができていましたが、一般的な製品や技術に移行できると、以下のような理由で今後のリプレイスが進めやすくなると考えていました。

  • VBScriptから別の言語に移行しやすい
  • サードパーティ製の便利なツールやサービスと連携しやすい
  • 新規参入メンバーがキャッチアップしやすい

そこで、これを機に一般的な言語やツールでも扱えて、性能や機能面で要件を満たしていたRedisを採用することにしました。

セッションオフロードの検証

セッションオフロードが実現できると、以下のようなメリットがあり、今後のリプレイスがより進めやすくなります。

  • スティッキーセッションを外せるため、Webサーバーをクラウドに移行しやすくなる
  • Webサーバーがステートレスになるため、Webサーバーの増減やリリースの作業が自動化しやすくなる
  • セッションを各Webサーバーで共有できるようになるため、カート内の商品など、デバイス間で共有できていない情報を共有できるようになる

しかし、実現するためには規模も大きく、ログイン認証や購入などのZOZOTOWNにとって重要な機能と密接に関わっているため、大きなリスクが伴います。

そこで、比較的規模も小さく、セッションよりも移行もしやすいキャッシュのElastiCacheへのオフロードを先に行い、セッションオフロードを実現できるかの検証も兼ねて、開発や運用の体制を整えることにしました。

これらの背景により、キャッシュストアのリプレイスを進めることにしました。

リプレイス後の構成

キャッシュストア

キャッシュストアはElastiCacheにリプレイスしました。キャッシュエンジンはRedisを使用しています。構成は、後述する課題があったため、1つのRedisクラスターで各シャードにレプリカノードを持たせるような標準的な構成ではありません。2つのRedisクラスターを使用して、各シャードにプライマリノードを1つだけ持たせる構成にしています。なお、Redisクラスターはシャードを増やしてスケールアウトできるようにクラスターモードを有効にしています。

image

フェイルオーバー機能の課題

ElastiCacheにはフェイルオーバー機能があります。当初はその機能を使う予定で検証を行っていましたが、課題が2つありました。

1. 標準のフェイルオーバー機能では、ZOZOTOWNが必要とするSLOを満たせない

SLOの確認をするために、検証を実施しました。

プライマリノード=1、レプリカノード=1の状態で、手動でフェイルオーバーをトリガーしてみました。裏でRedisのヘルスチェックシステムを動かして、フェイルオーバーしたシャードが使用可能になるまでの時間を計測したところ、弊社がZOZOTOWNに求めるSLOは満たせない結果となりました。

image

検証結果 ZOZOTOWNが求めるSLO
30秒~3分 30秒以内

2. シャード追加が終わらない事象の発生

サービスインした状態でシャード追加を実施した際に、スロットの移動が終わらず、プロセスが完了しないという事象が発生しました。その際は、サポートにご対応頂きましたが、復旧まで12時間程かかりました。

バックアップから新しいRedisクラスターをリストアして切り替える方法もありますが、データの整合性を保てないことや、復旧するまでに時間がかかってしまうという課題がありました。

以上の2つの課題を回避するために、Redisクラスターやノードに障害が発生しても、別のRedisクラスターにデータの整合性を保ちつつ、迅速に切り替える仕組みを導入することにしました。

Redisクラスターのフェイルオーバー

2つのRedisクラスターのうち、一方に障害が発生した場合は独自で構築したフェイルオーバーシステムでアプリケーション側の接続先を切り替えるようにしました。

正常時

  1. PrimaryとSecondaryのRedisクラスターを稼働
  2. Readは、Primaryにリクエスト、失敗時はSecondaryへリトライ
  3. Writeは、PrimaryとSecondaryにダブルライト(処理時間を抑えるために非同期で実行)

image

Primary障害時

  1. Primaryに障害が発生した場合は切り離す
  2. SecondaryをPrimaryに昇格させてサービスを継続
  3. Secondary障害時は、Secondaryの切り離しを行い、フェイルオーバーは行わない

image

障害復旧時

  1. 障害から復旧したクラスターはSecondaryとしてサービスイン
  2. キャッシュサービスのため、数分程度でPrimaryとSecondaryのデータ整合性は回復

image

フェイルオーバーシステム

フェイルオーバーシステムを構成する要素と利用している技術について紹介します。

スケジューラー

弊社で運用実績のあるKubernetes CronJobをスケジューラーとして利用しています。スケジュールやリトライ回数、多重起動の制御などをKubernetesのyamlファイルで管理して、Argo CDのAutoSyncを利用したGitOpsでのリリースを実現しています。

定期実行ジョブ

ヘルスチェックを実行するジョブはBash+Pythonで作成しコンテナ化しています。15秒間隔で後述するヘルスチェックAPIをキックして、レスポンスをDatadogにカスタムメトリクスとして送信しています。インターバルは、SLOに合わせて設定可能となっています。

ヘルスチェックAPI

JavaのMicroservices FrameworkであるSpring Bootを使用して、RedisクラスターのHealthCheck APIを作成しています。このAPIは、リクエストがあるとRedisクラスターの全てのノードの状態を確認して、クラスターの状態を管理情報用の冗長化したRedisに保存しています。また、Datadogへメトリクスを送信するために、Jobにヘルスチェックの結果も返すようにしています。

接続情報取得API

JavaのMicroservices FrameworkであるSpring Bootを使用しています。保存している管理情報を元にRedisクラスターの接続先情報を返します。このレスポンスをもとに、Webサーバーの設定が切り替わります。

監視・アラート発報

Datadog Integrationsで、RedisクラスターのCPUやコネクションなどのメトリクスを監視しています。フェイルオーバーシステムからのカスタムメトリクス用いて、クラスターの死活、ノードの死活、フェイルオーバーシステムの死活、接続情報の監視など、全体の監視を行っています。

image

このフェイルオーバーシステムを導入したことで、Redisクラスターに障害が発生しても15~30秒程度でフェイルオーバーが完了するようになりました。 また、クライアント側の接続先情報も2秒程度で自動更新されるようになっており、弊社のSLOの基準を満たすことができています。

クライアントのライブラリ選定

既存のアプリケーションはVBScriptで書かれていますが、VBScriptからReidsクラスターを容易に扱えて、導入できそうなライブラリはありませんでした。また、別のライブラリに置き換えようとすると、呼び出す関数や使い方も変わってしまうため、影響範囲が大きくなるという課題がありました。

そこで、今回は現行で使用していたライブラリにRedisの機能を追加することにしました。このようにしたことで、改修範囲は限定的となり、大幅にコストを抑えることができました。両クラスターのデータを同期させておく必要があるため、アプリケーション側で両クラスターに書き込みを行うようにしています。

image

なお、ライブラリの入れ替えが必要なWebサーバーは数百台程あり、それらに対してリリース作業を行う必要がありました。従来の運用では、ロードバランサーからの切り離し、ライブラリの配布、レジストリの操作などの作業は手動で行っていたため、ミスも起こりやすく、時間もかかるという課題がありました。

そこで、今回からはAnsibleを用いてコード化、自動化しました。そうすることで、レビューがしやすくなってミスが起こりにくくなり、1日かかっていた作業も1時間程に短縮できました。このように、システムだけではなく、運用や開発体制についても同時にリプレイスを行ってきました。

現在の状況

このような方法でリプレイスを行ってきましたが、現在の状況は以下の通りです。

  • セールなどの高負荷な状況でもスケールアウトすることで安定稼働できている
  • マネージドサービスやクラウドサービスを活用するようになったことでスケールしやすくなった
  • 一般的な技術に置き換わったことで今後のリプレイスが進めやすくなった
  • ElastiCacheへのリプレイスの知見やノウハウが得られ、開発や運用の体制も整ったため、セッションオフロードを進めやすくなった

なお、全体のスケジュールは以下の通りです。去年の11月からスタートして今年の9月にフェーズ1のリリースが全て完了しています。チームのメンバーは数名で、いくつかのプロジェクトを兼務している状況で進めていました。

image

今後の課題

今のところ大きな課題は発生していませんが、今後、運用や次のフェーズを行っていくうえで解決していきたい課題がいくつかあります。今回はその一部を紹介します。

  • 自動スケール

    現状、シャードの追加や削除は手動で行っています。サイトの負荷状況に合わせて頻繁に行う作業なので、今後スケジュールや負荷状況をトリガーに自動化したいと考えています。

  • データ不整合の解消

    2つのRedisクラスターで運用しているため、一方への書き込みに失敗すると、データの不整合が発生します。キャッシュの場合はオリジナルからも取得できるため、大きな影響はありませんが、セッションの場合は処理が中断したり、不具合が生じてしまうケースが多くあります。そのため、現在対策方法を検討しています。

  • ホットキーの解消

    ホットキーが発生すると一部のノードにリクエストが集中してしまい、負荷を分散することが難しくなります。また、一部のノードだけスケールアップすることはできないため、全ノードのリソースを増やす必要があり、余計なコストがかかります。そのため、クライアント側のリファクタリングや、キーの複製などで対策をしたいと考えています。

まとめ

ZOZOTOWNで使用しているキャッシュストアをどのようにリプレイスして、どのような課題があったかについて紹介しました。ElastiCacheへのリプレイスや使用を検討されている方の参考になれば幸いです。今後は課題への取り組みと、今回得た技術やノウハウを活かして、引き続きセッションオフロードを進めていきます。

最後に

ZOZOテクノロジーズでは、一緒にサービスを作り上げてくれるエンジニアを募集しています。ご興味のある方は、以下のリンクからぜひご応募ください。

tech.zozo.com

カテゴリー