SCSKの畑です。
今回は実案件における Redis から Elasticashe(Redis OSS)への移行において直面したいくつかの仕様差異について、どのような対策を取ったのかも合わせて紹介したいと思います。ちなみに移行に関する初回のエントリは以下となります。

差異その1:Elasticache は停止ができない
まずはベーシックな内容から。
よく知られていることだと思うのですが、Redis と異なり Elasticache は停止ができません。メモリ上にデータを保持する以上、インスタンスを停止するとデータが失われてしまうが故の仕様かと類推するのですが、停止できないということは一度インスタンスを立ち上げた後は常時課金が発生してしまうことになります。特に性能試験用の環境のように、使用中は本番環境と同様の大きいインスタンスを必要とする環境ではその弊害もより大きくなってしまいます。
そこで、大きなインスタンスサイズを必要とする環境については、環境を使用していない時間帯はインスタンスサイズを縮小することで課金額を低減しようと考えていたのですが・・ふと、縮小先のメモリサイズより大きなデータが Elasticache 上に存在したらどういう挙動になるんだろう?という疑問が。実運用時においても同じような状況になることが予想できたため事前に検証しておくことにしました。
試しに 10 GB のキャッシュデータを cache.r7g.xlarge のインスタンスにロードした後、t3.micro にサイズを変更してみたところ・・
Failed to scale down to cache node type Replication Group redis-downsize-test because the node has insufficient memory. Please select a different node type or reduce current memory usage and retry
のようなエラーが出力されてしまったため、(ある意味予想通りでしたが)縮小できませんでした。よって、Elasticache の(再)作成・削除により起動・停止を代替する方法が唯一の回避策という結論となりました。削除時にデータをバックアップとして残しておいて、それを(再)作成時に使用するような流れです。先述した Redis の挙動を Elasticache で実現しようとするとこうなります、とも言えますね。
直近にリリースした Elasticache で早速この対応が必要となったため、一旦は AWS CLI で実装することで解決しました。
差異その2:一括移行の場合 Elasticache をバックアップから新規作成する必要がある
続いてはこちらのテーマ。上記内容(差異その1)とも関連する内容でもあります。
先般のエントリでも言及していた通り、本案件における Elasticache のデータ移行は一括移行で実施する旨決定したのですが、一括移行において現行環境で取得したバックアップを新環境に移行する際において、そのタイミングでバックアップから Elasticache を新規作成する必要があります。言い換えると、作成済みの Elasticache に対して特定のバックアップをリストアするというオペレーションが行えません。
よって、一括移行のタイミングまで新環境(Elasticache)のエンドポイントが不明な状態になってしまうという懸念点がありました。当日移行(切替)のタイミングでアプリケーション側の各種設定を変更する必要がある訳ですが、その直前まで新環境(Elasticache)のエンドポイントが分からないというのは作業への影響が大きいと考えたためです。
一方で、過去の経験や上記(差異その1)の対応において Elasticache を同じ設定で再作成した場合、再作成前後で必ず同じエンドポイント名となったことから、裏取りも兼ねてエンドポイントの命名規則が分かれば(少なくとも現在の仕様において)事前に新環境でのエンドポイント名を導出することができると考えました。
AWS のドキュメントには該当するような内容がなさそうだったので、大人しく AWS サポートに確認しました。その結果、命名規則に関する公開情報はないという前提において現在の仕様を確認頂くことができたので、以下に共有します。クラスターモードや転送中の暗号化の差異によりエンドポイントの FQDN が異なるというのは正直気付かなかったところです・・
◆クラスターモード無効・転送中の暗号化が有効
・プライマリエンドポイント
master.<クラスター名>.<6文字のランダム文字列(※).<リージョン名>.cache.amazonaws.com
・リーダーエンドポイント
replica.<クラスター名>.<6文字のランダム文字列(※).<リージョン名>.cache.amazonaws.com
・ノードエンドポイント
<ノード名>.<クラスター名>.<6文字のランダム文字列(※)>.<リージョン名>.cache.amazonaws.com◆クラスターモード無効・転送中の暗号化が無効
・プライマリエンドポイント
<クラスター名>.<6文字のランダム文字列(※)>.ng.0001.<リージョン名>.cache.amazonaws.com
・リーダーエンドポイント
<クラスター名>-ro.<6文字のランダム文字列(※)>.ng.0001.<リージョン名>.cache.amazonaws.com
・ノードエンドポイント
<ノード名>.<クラスター名>.<6文字のランダム文字列(※)>.0001.<リージョン名>.cache.amazonaws.com◆クラスターモード有効・転送中の暗号化が有効
・設定エンドポイント
clustercfg.<クラスター名>.<6文字のランダム文字列(※)>.<リージョン名>.cache.amazonaws.com
・ノードエンドポイント
<ノード名>.<クラスター名>.<6文字のランダム文字列(※)>.<リージョン名>.cache.amazonaws.com◆クラスターモード有効・転送中の暗号化が無効
・設定エンドポイント
<クラスター名>.<6文字のランダム文字列(※)>.clustercfg.<リージョン名>.cache.amazonaws.com
・ノードエンドポイント
<ノード名>.<6文字のランダム文字列(※)>.0001.<リージョン名>.cache.amazonaws.com(※) 「6文字のランダム文字列」は、同一アカウント・同一リージョンであれば同じ値となる
今回使用するのは「クラスターモード無効・転送中の暗号化が無効」のパターンに該当するので、こちらの命名規則を連携して一旦解決となりました。
差異その3:通信暗号化方式と認証方式の組み合わせにおける制約がある
さて、ある意味本題というか、相対的に影響が大きかったトピックがこちらとなります。
現行の Redis では
- 通信暗号化方式:暗号化なし
- 認証方式:AUTH 認証
という設定だったため Elasticache でも踏襲しようとしたところ、なんと Elasticache の仕様として設定できないことがわかりました。設定可能な組み合わせは
- 通信暗号化方式:暗号化あり、認証方式:AUTH 認証
- 通信暗号化方式:暗号化あり、認証方式:認証なし
- 通信暗号化方式:暗号化なし、認証方式:認証なし
のどちらかに限定されており、どれを選択した場合においても現行 Redis との非互換が発生してしまいます。また、現行 Redis との互換性を考えると、上記 3 つの組み合わせの内、2 についてはどちらの項目も現行 Redis からの変更になるため採用するメリットがないと判断し、それ以外の方式(1 or 3)のメリット・デメリットを整理した上でどちらの組み合わせを採用するかお客さんも交えて検討しました。
- 通信暗号化方式:暗号化あり、認証方式:AUTH 認証
- メリット:AUTH 認証に関連するアプリケーションコードの変更が不要
- 通信暗号化に関連するアプリケーションコードの変更は必要
- デメリット:Elasticache への移行により、通信暗号化に伴う性能への影響が生じる可能性あり
- メリット:AUTH 認証に関連するアプリケーションコードの変更が不要
- 通信暗号化方式:暗号化なし、認証方式:認証なし
- メリット:Elasticache への移行により性能への影響が生じる可能性を相対的に抑えられる(通信暗号化しないため)
- デメリット:AUTH 認証に関連するアプリケーションコードの変更が必要
結論から言いますと、本案件では性能への影響を重視して「通信暗号化方式:暗号化なし、認証方式:認証なし」の組み合わせを選択するになりました。また、現在 AWS へのマイグレーションを先行して実施している別の案件でも同一方式が選択されていたことも理由の一つとなりました。
補足:今回のケースにおける、上記変更に伴うアプリケーションへの影響について
上記にまとめた通り、通信暗号化を有効にする場合・AUTH 認証を無効にする場合どちらもアプリケーションコードの変更はいずれにせよ必要になりますが、実際にどのような変更が必要かはアプリケーション側の実装に依存します。
今回のケースの場合、通信暗号化関連についてはいわゆるアプリケーションのコンフィグ変更が必要でしたが、いずれにせよ移行(切替)時に接続先を Elasticache のエンドポイントに変更する必要があることも鑑みると、影響はほぼなしという判断になりました。
対して、AUTH 認証の無効化については一部のライブラリを使用しているアプリケーションコードにおいて影響がありました。例えば、phpredis ライブラリを使用した以下のような php のコードで AUTH 認証なしのElasticache (Redis) に対して接続しようとすると
$redis = new Redis(); $redis->connect($host, $port, $timeout); $authenticated = $redis->auth($password);
以下のように、3行目の $redis->auth($password) 実行部分においてエラーが出力されてしまいます。AUTH 認証が無効であるにも関わらず AUTH コマンドが実行されているという内容ですね。よって、このようなコードについて、対象処理の削除、Elasticache (Redis) に接続できた時点で各種操作ができるようなロジックへの変更などの修正が必要となりました。
PHP Fatal error: Uncaught RedisException: ERR AUTH <password> called without any password configured for the default user. Are you sure your configuration is correct? in /home/ec2-user/redis_test.php:14
ちなみに、AUTH 認証無効の Elasticache (Redis) に接続した状態でこの処理を実行したとしても、もしライブラリ側でエラーを返さないような実装になっていればコード変更が不要になった可能性もあります。そのあたりは使用しているライブラリに依存するところですが、上記については redis-cli のような公式ツールでも同じ挙動になったので、(言い方が適切がどうかはさておき)この実装自体は正しいのかなと思います。
まとめ
1点目・2点目については仕様だけ見てこういうものなのね、と一旦流していたのですが、よくよくユースケースを考えると困るみたいな内容でした。幸いどちらも事前に気づけてひとまず対策できたので良かったです。
トータルで一番影響が大きかったのはやはり3点目です。パラメータレベルでの互換性は事前にチェックしていたのですが、Elasticache においてはパラメータで設定できない内容だったことに加えて、個々の設定(通信暗号化/ユーザ認証)としては可能だけど組み合わせパターンに制約がある、という非互換性はちょっと予見できておらず面食らいました。先述の通りその意図は理解できなくはないんですけど、Redis との互換性を考えるとできるようにしておいても損しないんじゃないかと思うんですけどどうなんでしょうか。Elasticashe(Redis OSS)を選択する以上は、Redis との互換性を最優先するという意図があるでしょうし。
本記事がどなたかの役に立てば幸いです。
