[システム運用] Spring Boot + ElastiCache + DynamoDBで構成されたシステムのメンテナンス -解答・知見の共有編-

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

こむろ@さっぽろです。

全国的に雨天が続いていますが、札幌も例外ではなく非常に寒い日々が続いています。7月なのに20℃を下回っていたり、湿度100%超のような気候だったりで快適な北海道はどこへ・・・という気持ちです。

さてすでに遠く過去になってしまいましたが、前回ElastiCacheのクラスタがメンテナンス対象になったとき、どのような手法で環境を更新するかといった選択肢をいくつか出したかと思います。それぞれの手法にメリット・デメリット、そして落とし穴があるかを考察していきます。

どの手法が正しいのか

自分もどんな手法を紹介したか忘れかけているので手始めに、前回3つの手法を紹介しましたがそれぞれを再度確認してみます。

  1. TTL定義を更新。RedisのデータはDynamoDBのテーブル(マスタデータ)のCacheになっているので、DynamoDBのテーブルアイテムを全件削除すればOK
  2. TTL定義を更新。DynamoDBのテーブル(マスタデータ)の空テーブルを作成してアプリケーションの設定を変更、Redisのデータは手動で削除すればOK
  3. TTL定義を更新。DynamoDBのテーブル(マスタデータ)の空テーブルを作成、Redisのクラスタも空のものを作成し、アプリケーション設定を一気に変更すればOK
  4. その他

1. DyanamoDBのデータを全部ふっ飛ばせばOK?

DynamoDBのテーブルの中のアイテムをすべて削除するときに全件削除は、おそらく特殊な事情がない限り(テーブル名が固定でハードコーディングされているため、絶対に変更されてはならない等)は選べないのではないでしょうか。割と引っ掛けみたいなものですが、デメリットが多く目につく手法です。

メリット

  1. アプリケーションへの変更がない

デメリット

  1. 基本的にDynamoDBのテーブルのデータ削除はWrite Capacity Unitを消費して実行されるため値段がお高い
  2. DynamoDBのマスタデータを削除したとしても、異常に長いTTLで設定されたCacheは生き残っているため、マスタデータが削除されたからと言ってCacheが削除されるかというと疑問

長い時間がかかってCacheのTTLが消えてCacheが削除され始めると、徐々にマスタデータが作成し直されると意図した状況に戻っていきますが、お高いお金を払った割にはメリットが少ない手法です。

2. DynamoDBのデータは空テーブルに。Redisノードは手動で削除すればOK?

DynamoDBのテーブルにそこそこのデータ量が入っていて全データをクリアしたい場合、空テーブルを作成してそちらを利用し、利用していたテーブルを破棄する方が圧倒的にコストが抑えられます。テーブルの各種設定を再設定しなおす手間はありますが。またCacheとしてRedisノードにはTTLが異常なデータが格納されてしまっているので、こちらは手動で削除します。ただ、Spring Cacheが内部でどのようにデータを利用しているかを無視して外部から変更を加える形になるため、アプリケーション内部で不整合が起きないかを確認する必要があります(インフラ側だけ見てると失敗する)

メリット

  1. DynamoDBテーブルの削除のコストが大幅に下げられる

デメリット

  1. DynamoDBのテーブルが新規で作成されるためアプリケーションの設定更新が必要になる
  2. そもそもSpring Cacheで利用しているデータを直接書き換えて不整合は起きないのかが不明
  3. 一時的にマスタデータ(DynamoDB)とCache(Redis)に差異が生まれ、矛盾が生じてしまう
  4. Redisの削除コマンドは稼働環境で実施したらいけない。 今回のオペレーションでの最大のミス。後述。

3. DynamoDBもRedisも全部新規作成すればOK?

DynamoDBもRedisもいずれもKVSなので同じように空テーブルを新たに作成します。こうすれば手法2であったようなマスタデータとCacheデータの間に一時的な矛盾が生じることはありません。マスタデータの構築とともにCacheも構築されていきます。

メリット

  1. DynamoDBテーブルの削除のコストが大幅に下げられる
  2. マスタデータとCacheデータの間の矛盾がほとんどなくなる

デメリット

  1. DynamoDBテーブルの設定値の復元に加え、Redisのノード作成時に設定値の復帰が必要になる
  2. 利用しなくなったテーブルやRedisノードの掃除をする必要がある
  3. アプリケーションの設定更新が必要になる

4. その他

上記以外にもいくつか手法を検討したので共有しておきます。

アプリケーションのDelete処理を全件分実行する

これができれば一番綺麗に処理ができます。これはシステムが提供している正常な削除APIを利用しているため、DynamoDBのテーブルとRedisのCacheデータに矛盾は発生しません。マスターデータを削除し、それに伴いFrameworkの機能によってCacheも削除されます。ただし、今回のシステムではBulk APIがサポートされていません。そのため数百万件超のデータを削除したい場合、次のような準備が必要になります。

  • 一つずつ削除するための別途Workerを準備する必要がある
  • 一時的に数百万件超のリクエストを処理するためにScale Out、Capcity Unitの調整を行っておく(負荷テストと同等の準備)

ある程度時間をかけられる場合やシステムの構成を極力変更せずに全データを削除したい場合は検討できそうです。

RedisのクラスタごとBlue-Green Deployment

稼働しているシステムの環境をまるごとコピーして入れ替えます。アプリケーションの環境をそのものをまるごと複製します。そのため、一時的にリソースが全て二重になり、料金も二重になりますが正常稼働していることを確認してから公開できるため、テーブルやRedisノードを再生成する場合にはとても有効です。3の手法で紹介した手順に近いですが、3がRolling Updateをベースにしている(アプリケーションの設定更新)のに対して、こちらはBlue Green Deploymentによる環境複製によるアプリケーション更新になります。

どの手法が良いか

結果としてですが、全データを手軽に削除するならば 3. DynamoDBもRedisも全部新規作成すればOK? もしくは 4.その他 のBlue-Green Deploymentによる手法が安全です。

RedisのノードもDynamoDBのテーブル同様に削除にはそれなりに考慮すべき事由が存在します。そのため、新たに空のデータノードを作って複製してしまった方がデータの矛盾などを考える必要もないため、特別な理由がない限りはこれらを選択しておいた方が良いでしょう。作業の工数と時間、優先度、コストなどを複合的に判断し手法を選択してください。ただし、次に理由は記述しますが、 2. DynamoDBのデータは空テーブルに。Redisノードは手動で削除すればOK? は極力選択しない方が良いかと思います。

Redisのオンライン状態での操作の注意

Redisが稼働している最中に自分がやらかした操作は FLUSHDB です。Redisをきちんと理解しているならば、稼働中のノードに対してそうそう叩かないのではないかと思います。これがなぜまずかったのかはドキュメントを見て初めて理解しました。

redis - FLUSHDB [ASYNC]

あ、これはいけない。計算量がO(N)でNがキーの数になっています。そのため膨大なキーを削除しようとすればそれに伴い計算量が増大します。ソフトウェアの基本ですね・・・。非同期での実行が保証されなければ処理が完了するまでCPUのリソースをフルで使い切り他の処理をブロックすることは自明です。件数が少ない場合はさして問題ないかもしれませんが、件数が多い場合は明らかに処理が停止してしまいます。

今回自分はこれを実行してしまったが故に、アプリケーションからのCacheサーバーの接続が全て落ちるという障害を引き起こしてしまいました。猛省です。

ちなみに FLUSHDB には非同期コマンド(ASYNC)も用意されているようですが、残念ながらこれはバージョンが3.4以降でないと利用できません。現在稼働しているシステムで利用しているRedisのバージョンは2.8系であるためこれは利用できませんでした。

まとめ

メンテナンス時にはインフラのみならず、マネージドサービスの中で採用されているミドルウェアのエンジンの特性や動作も知らなければ、コマンド一つをとってもシステム全体にどのような影響を与えるか想像できません。最近は特にクラウドサービスによって各サービスを組み合わせることでより複雑なシステムを容易に構築できるようになっています。しかし、それゆえに各サービスの障害や動作が及ぼす影響はシステムに広く波及します。それは止められないドミノ倒しのように。

クラウドサービスのマネージドサービスによってミドルウェア利用における様々な作業から解放されたものの、すべてお任せ、無知で良いわけではありません。システムを構築している各サービスの使い方を知っているのはもちろんですが、それぞれのサービスで採用しているソフトウェア(ミドルウェア含め)がどのようなものであるかも詳細を知っているべきかと思いました。自分は今回ElastiCacheの中で利用されているRedisのClientのコマンド一つの知識が足りなかったばかりにアプリケーションが正常に稼働しないという状況にまで発展してしまいました。より複雑化するシステムの中では一つのミスが多大な影響を及ぼす可能性があることを改めて思い知らされた一件です。

今回の件を踏まえて、ElasitCache(Redis)のメンテナンス時には、必ずBlue-Green Deploymentによる環境の複製を行うように決めました。今回のように稼働の停止が原則認められないシステムにおいては環境複製によるメンテナンスがベストな手法のようです。改めて実感しました。複製時に一時的にリソースが環境分増加するため、費用が一時的に若干増加するのがデメリットではありますが、無停止メンテナンスを安全に行えるならばこれに代わる方法はあまりないかと思います。

ということで会社設立日の今日七夕の日。

以上ElastiCacheのメンテナンスにおける失敗ケースの共有でした。