Zenn
👾

ElastiCache Serverless for Redisが採用できなかった話

2025/03/23に公開

はじめに

こんにちは。都内でソフトウェアエンジニアをしている tomori です。

先日、ElastiCache Serverless for Redis 関連でハマったことがあったので、備忘録も兼ねて記事にまとめました。

現在は Valkey の方が主流だと思いますが、今回の内容は Redis / Valkey どちらにも当てはまるものになっています。

※記事の構成上、実際の経緯とは一部異なる形で記載していますが、技術的な検証内容と結論に影響はありません。

先に要点まとめ

諸々の経緯を端折って、本記事の技術的な要点をまとめます。

  1. ElastiCache Serverless は TLS 接続が必須
    • 従来型の Redis(provisioned)で TLS を無効にしていた場合、Serverless に移行する際は注意が必要
    • クライアント側で TLS の有効・無効を適切に設定する必要がある
  2. Twemproxy などのプロキシを挟んでいる場合、プロキシが TLS に対応している必要がある
    • Serverless Redis は TLS 接続が必須のため、最終的に Redis に接続するプロキシまたはクライアントが TLS に対応していないと通信できない

事象

私が携わっているプロダクトでは、マスタをデプロイする際に一部のデータを Redis にキャッシュとして登録する処理があります。

この処理は Laravel の artisan コマンドとして実装されており、Laravel がクライアントとなって Redis サーバーに接続します。

一部の環境だけ Redis を Serverless 版に切り替えていたのですが、なぜかその環境だけマスタデプロイ時にエラーが発生するようになりました。

実際のエラーはこちらです:

[Failed] Execute command master:xxx is finished, 60.144446 seconds, max memory: 35.171875 MB.
read error on connection to xxxxx.serverless.apne1.cache.amazonaws.com:6379
{"message":"read error on connection to xxxxx.serverless.apne1.cache.amazonaws.com:6379","context":{"code":0,"exception_class":"RedisException","file":"/var/www/vendor/laravel/framework/src/Illuminate/Redis/Connections/Connection.php","line":116,...}

エラーが発生した当初は、

  • 「サーバーレスだからスケールが間に合ってないのかな?」
  • 「コールドスタート的な問題かな?」

と考え、とりあえずクライアントのタイムアウト時間を伸ばして様子を見ました。

しかし、設定を変えても状況は変わらず。

ここで、コマンド失敗までの時間が毎回ほぼ60秒で一定 だったことに気づき、「これは接続情報が間違っているのでは?」と疑い始めました。

ところが、ElastiCache のコンソール画面でメトリクスを確認してみると、デプロイ実行時に 接続数が1つ増えている ことが確認でき、少なくとも コネクションは確立できている ことがわかりました。


ElastiCache Serverless の接続数メトリクス

その後もエラーメッセージを手がかりに調査を続けましたが、ヒットするのは以下のようなタイムアウト関連の情報ばかりでした。

https://stackoverflow.com/questions/18072407/php-redis-timeout-read-error-on-connection

https://github.com/phpredis/phpredis/issues/492

https://github.com/phpredis/phpredis/issues/1543

「クライアントのタイムアウトを調整してもダメだった」「そもそも60秒もかかる処理じゃないはずなのでタイムアウト設定の問題ではない気がする」と思い始めた頃には完全にハマっていました。

解決

もともと ChatGPT に壁打ちしながらエラーの原因を探っていたのですが、「ここはこうだったから違うっぽい」といった情報や状況を逐一与えていたところ、あるとき次のようなメッセージが返ってきました。

🔸(原因候補②)Redis側(Serverless Redis)のTLS設定

  • AWSのElastiCache Serverless Redisは、常にTLS通信が必須です。
  • Laravel側でRedisクライアントが「非TLSモード」で接続すると、接続直後に通信ができず無限に応答待ちになり、一定時間後(典型的には60秒)でタイムアウトになることがあります。

「おっ?」となってソースを探したところ、実際に AWS のトラブルシューティングページにTLSに関する記載がありました。

Using TLS: If you are experiencing a hung connection when trying to connect to your ElastiCache endpoint, you may not be using TLS in your client. If you are using ElastiCache Serverless, encryption in transit is always enabled. Make sure that your client is using TLS to connect to the cache.

https://docs.aws.amazon.com/AmazonElastiCache/latest/dg/wwe-troubleshooting.html

どうやら ElastiCache Serverless では TLS 接続が常に必須 のようです。

そこで、Laravel 側の Redis 設定を確認してみたところ、案の定 TLS は無効になっていました。

ということで、設定に以下のような変更を加えて TLS を有効化してみます。

     'redis' => [
         'default' => [
             'url' => env('REDIS_URL'),
             'host' => env('REDIS_HOST', '127.0.0.1'),
             'password' => env('REDIS_PASSWORD'),
             'port' => env('REDIS_PORT', '6379'),
+            'scheme' => 'tls',
         ],
     ],

その状態で再度マスタデプロイを実行してみると、今度は無事に処理が完了し、エラーも発生しませんでした 🎉

ひとまずここでエラーは解決です。


今回の問題を整理すると、

  • Serverless に切り替えたことで Redis 側は TLS 接続を必須としていた
  • 一方でクライアント側(Laravel)は 非 TLS 接続 を行っていた
  • そのため、コネクション自体は確立しているが、データの送受信ができない状態 になっていた
  • 結果、60秒後にサーバー側のタイムアウトで接続が切断され、エラーとなっていた

という状況だったようです。

(うーん。それにしてもエラーメッセージがわかりにくい。)

解決できなかった

マスタデプロイ時に発生していたエラーは解消できたのですが、もう1つ問題があることを見落としていました。

先ほどのエラーはマスタデプロイ時、つまり Laravel と Redis が直接通信している構成 において発生していたもので、そこでは Laravel 側の TLS を有効化することで解決できました。

しかし、本プロダクトの 現在の API アーキテクチャ では、Laravel と Redis の間に Twemproxy というプロキシを挟んでいます。

つまり、TLS が必須である Serverless Redis とデータの送受信を行うためには、Laravel ではなく Twemproxy 側が TLS 接続に対応している必要 があります。

しかし、ドキュメントや Issue 等を確認してみたところ、どうやら 2025年3月時点で Twemproxy は TLS をサポートしていない ようでした。

https://github.com/twitter/twemproxy/issues/583

というわけで、ここまであれこれやったものの、現行アーキテクチャでは TLS をサポートしていない Twemproxy がボトルネックとなるため、Serverless Redis の採用は困難 という結論に至りました。

残念ながら、この話は無に帰すことになりました。

今は Serverless をやめ、従来型の Redis(provisioned)に戻しています。

あとがき

今回発生したエラーは、内容こそは大したことのないものだったのですが、エラーメッセージが若干わかりづらいという点で解決に時間がかかってしまいました。

また、エラーこそ解決したものの、結局別の理由で Serverless は使えないね、という結論になってしまったことは残念ですね。

「意地でも Serverless を使いたいんだ!」という強い意志があれば、Twemproxy ではなく TLS 対応のプロキシに移行するなど対応の余地はあると思います。

とはいえ、今回はそこまでのモチベーションはなかったので、素直に見送るという結論に至りました。

本記事が同様の箇所にハマった方の助けになれば嬉しい限りです。

Discussion

ログインするとコメントできます