ウン十万接続のALB SSL証明書を平和に更新したい

こんにちはSREの黒田です。 これは第2回 Nature Engineering Blog 祭9日目のエントリです。

昨日はCorporate ITのマロニーによる GASを使って社内のSaaSアカウントを可視化しよう - Nature Engineering Blog でした。

昨日に続いて今日のお話も、話題の新製品Remo nanoやMatterとは関係ありません。


TL;DR

WebSocketで大量に永続接続されているALBのSSL証明書を更新すると、接続がばっこんばっこん切られて大変なので、ALBを二台用意して緩やかに接続を移行するようにしたら、大変平和になって僕もみんなもハッピーになった。

背景

そもそもNatureではどこに何のためにWebSocketを使ってるの?って話から始めると長いので、詳しくはこちらを見ていただければと思います (結構前の資料なので今とは違う部分も色々ありますのであしからず)。

Nature Remoの裏側

簡単にいうと、

  • Nature Remoデバイス群がお家のネットワークからAWS上のシステムに接続し、その後システムと双方向メッセージ交換をするため

です。 つまり、世にあるNature Remo達が稼働している限りずっとALBを介してサーバーに常時接続しており、それが今だとウン十万あるのです (大変だー)。

一方、ALBではSSLターミネーションをしているのですが、SSL証明書というのは有効期限がありますので、定期的に入れ替えを行う必要があります。詳しい背景は端折りますが、本件で使っているALB (以下「stream ALB」という) では一ヶ月に一回入れ替えを行っています。

ALBの証明書を入れ替えるとどうなるのか?

多分普通のHTTPサーバを運用している場合はあまり気にしたことがないと思います。今後も多分気にしなくていいだろうし、もっというとこれから書くことも何のお役にも立たない可能性は高い!

証明書入れ替えから1時間後くらいにALBの内部ノード的なものが順次リロードして、既存の接続が一度バッサリ切られます*1。ノードの数 (Public IPの数) はトラフィックに応じて自動で増減しますが、stream ALBの場合は大抵3つなので、ウン十万接続の1/3ずつが短時間の間にバッサリ切られることになります。

Remoデバイス群は接続が切れれば自動で再接続しに来るので切れっぱなしになるということはないのでそれは問題ないのですが、その数が問題です。ウン十万が一度に切れれば、当然ウン十万が一度に再接続しに来るわけです。 単にWebSocket接続するだけであれば大した負荷ではないのですが、接続時には色々と初期化処理も走るため、streamサーバだけでなく、他の周辺コンポーネントでもそれはもう大変なバーストトラフィックが発生してしまうんですね。

このバーストを軽減するために、streamサーバでは接続処理のスロットリングを行っているのですが、システム目線で安全に倒しすぎたチューニングだと接続待ちが発生してしまいます (ユーザが使えない状態) し、逆だとシステム全体がサチってしまいます。大変だ。

というわけで、このバーストをなんとかしたいってことで導入したのが、ALB blue-green deploymentです

ALB blue-green deploymentってなんぞや

ALBにそういう機能があるわけじゃないです。

同一バックエンドを2つのALBに接続しといて、ALBの証明書とPublic DNSの向き先を順に入れ替えるように自前で作り込んだものです。

図にするとこういうこと。

交互に切り替わる2つの状態

処理の流れ

  1. 月次バッチ処理で新しい証明書が発行され、ACMに登録される
  2. Terraform管理リポジトリのワークフローで新しい証明書の登録が検出され、Pull Requestが作られる
  3. 人間がPull Requestにコメントされたplanのdiffを確認後にシステムに適用する
  4. Remoは規定の範囲内でランダムな時間経過後に自動的に接続が切れるようになっているため、再接続時には常に最新証明書を持つ側のALBに接続される

です。ちなみに証明書はLet's Encryptです。Let's Encrypt便利です。ありがとうありがとう。

1はいいとして、それ以降がこの仕組みの肝です。 TerraformではData resourceやらResource tagやらを駆使して、特に人が手を加えなくても新しい証明書が登録されたときには以下の変更が発生するようになっています。

  1. 現在ActiveではないALB (blue ALB) の証明書を最新の証明書に変更
  2. 2つのALBにつけられているtagDeploymentColor:(blue|green)の値を逆にする
  3. DNSレコードの値を現在DeploymentColor:greenとなっているALB (green ALB) のDNS nameに更新

これらはすべて冪等となるように実装しているため、証明書の更新が発生していなければ何度applyしても何も起きないようになっています。具体的な実装内容も紹介したいところですが、うまく説明できる自信がないのでやめておきます。

3の適用については、terraform applyまで自動化しようと思えばできなくもないのですが、ここは念のため人の眼による確認を行ってからapplyするようにしています (手元で実行するわけではないですが)。意図しないドリフトが発生している可能性もありますので完全自動化はあえてしていないってことですね。

terraformのOutputとしてplanでこんな感じの出力をするようにして差分をわかりやすくしています。

Changes to Outputs:
  ~ current_stream_alb_color     = {
      ~ a = "green" -> "blue"
      ~ b = "blue" -> "green"
    }
  ~ current_stream_listener_cert = {
      ~ b = "arn:aws:acm:xxx" -> "arn:aws:acm:yyy"
        # (1 unchanged element hidden)
    }

最後4がRemoデバイス群の接続移行になるんですが、「規定の範囲内でランダムな時間経過後に自動的に接続が切れる」という仕組みは以前からあったものです (例によって詳細は端折ります)。証明書の更新と同時にPublic DNSも更新されているため、規定範囲の最長時間をかけて既存接続が緩やかにblue ALBからgreen ALBに移行されていくというわけです。

ちなみに証明書の期限は3ヶ月ですので、なんらかの問題でDNSの更新を無視してblue ALBに再接続し続けるRemoが少数いたとしても問題になりません。翌月の更新時にはblueがgreenになるので再度最新の証明書を使った接続になります。

グラフでみるBefore、After

ALB blue-green deplymentの導入によって、接続や負荷は以下のように変わりました*2。平和だ!!

Before

SSL証明書更新のタイミングで大きなスパイクが発生

After

スパイクがなく平和

以上、多分あまりお役に立たない事例紹介ではありますが、WebSocketを大量にさばくシステムならではの珍しい苦労話としてお楽しみいただけたなら幸いです。


お知らせ

Nature エンジニアコミュニティ

エンジニアコミュニティはじめました。Matter を使った Nature Remo nano の活用方法から Nature Remo could API の疑問など、Nature のエンジニアがお答えします! Discord への参加はこちらから。 https://discord.gg/3Ep57Muuuc

Nature Remo nano 好評発売中!

Matter 対応の Nature Remo nano が定価3,980円で発売中!

*1:正確な仕組みは公開されていませんが、IPは変わっていないことをみるとおそらくプロセスのリロード的なことをしている気がします。

*2:Active connections以外は1分間インターバルでの最大値でロールアップしています