【レポート】CUS-44:「このファン」におけるマネージドサービスドリブンなアーキテクチャへの挑戦 #AWSSummit

2020.10.03

こんにちは、CX事業本部の夏目です。

今回はAWS Summit Onlineのオンデマンドセッション 「CUS-44:「このファン」におけるマネージドサービスドリブンなアーキテクチャへの挑戦」 についてのセッションレポート(文字起こし)になります。

導入

  • 株式会社サムザップ 信田より発表させていただきます。
  • それでは内容に入っていきます。

タイトル

  • 今回のセッションでは、『このファン』におけるマネージドサービスドリブンなアーキテクチャへの挑戦、と題しまして、弊社における AWS サービスの活用事例を発表させていただきます。
  • どうぞよろしくお願いいたします。

アジェンダ

  • 今回は、弊社の運用中サービスである「この素晴らしい世界に祝福を! ファンタスティックデイズ」におけるアーキテクチャの紹介とマネージドサービスの活用、性能検証とその際の事例の共有、 マネージドサービスに寄せたアーキテクチャを構築、運用を行っての所感などをお話させていただきます。

自己紹介/会社概要

  • まずは自己紹介と、 サムザップについての概要説明、および運用中のサービスの紹介をさせていただきます。

  • 私、株式会社サムザップ SREチーム所属のエンジニアをやっております信田悟至と申します。
  • 2014年4月にサイバーエージェントに入社し、同年6月よりサムザップへ出向、これまでは運用中のプロダクトおよび新規開発チームのサーバーサイドエンジニアを担当しており、2018年よりサムザップ SREチームに参加しました。
  • 現在は、「この素晴らしい世界に祝福を! ファンタスティックデイズ」のシステム設計構築運用などを担当しております。

  • 株式会社サムザップは、2009年5月にサイバーエージェント子会社として設立しました。
  • 事業内容としましては、主にスマートフォン向けゲームアプリの企画運営、配信を行っております。

  • 弊社で現在配信中のサービスについて紹介します。
  • 一つ目は、「戦国炎舞 絆」です。
  • 2013年4月より配信を開始し、今年で7周年を迎えました。
  • 戦国時代をモチーフにし、最大20人台20人のリアルタイムバトルを楽しめるゲームで、現在も多くのユーザー様に遊んでいただいております。
  • もう一つは、「この素晴らしい世界に祝福を! ファンタスティックデイズ」、通称このファンです。
  • 2020年2月より配信を開始しました。

  • このファンはラノベやアニメで大人気の「この素晴らしい世界に祝福を」の初のスマホゲームタイトルです。
  • 原作の世界観をしっかり踏襲しつつ、ゲームオリジナルのストーリーをフルボイスで楽しめるほか、オリジナルキャラクターの登場やバトルアニメーションなどにもこだわった RPG です。
  • 今回はこの このファン をテーマとして発表させていただきます。

アーキテクチャの紹介とマネージドサービスの活用

  • それでは、このファンにおけるアーキテクチャの紹介と、その中でのマネージドサービスの活用事例の紹介をさせていただきます。

  • このファンでは、アーキテクチャの設計におけるコンセプトを設けておりました。
  • コンテナを使用した、イミュータブルなインフラストラクチャの構築を行う。
  • 高可用性を目指すために、マルチ AZ でシステムを組む。
  • 管理上の作業コストを削減できるように、マネージドサービスに頼る。

  • この三つのコンセプトをもとに設計を行っており、特にマネージドサービスを率先して導入するというコンセプトは、従来の VM インスタンスを用いた運用と比較して、システム管理における作業コストを減らし、その分の時間を更なる検証や開発に充てられると考えたため、かなり注力しておりました。

  • 今回構築したアーキテクチャは、一部を除き、多くをAWS サービスを用いて構築しておりました。
  • そもそもなぜ AWS を選んだかですが、 MySQL と互換性があり、今まで試行錯誤して行っていたフェイルオーバーやバックアップを自動で実現できる Aurora の存在が決め手でした。
  • 弊社では、 AWS 以外のパブリッククラウドの運用経験もあったのですが、Auroraを使用するメリットを享受したく、このファンドでは AWS を選択しました。

  • 全体構成ですが、 AWSのアカウントは、本番、ステージング、検証用の三つのアカウントを作成し、それぞれの用途で使い分けを行っております。
  • これは本番のリソースや費用の管理は、アカウントから完全に独立させて管理したいという意図で分けました。
  • また、ステージング環境のリソースの台数やスペック以外は、本番とほぼ同じ構成をとるという意図で、アカウント単位で独立させました。
  • それぞれのアカウントは、 VPCピアリングを用いて、一部総相互通信を可能とさせております。
  • 構成管理はHashiCorp社のTerraformを用いてコードベースで管理しております。

基本構成

  • このファンのアーキテクチャーの基本構成を紹介します。

  • VPC 内に設けられた、パブリックサブネットにはロードバランサーを配置し、プライベートサブネットにはAPI サーバーとしてECS on Fargate、 DB サーバーとして、Aurora、CacheサーバーとしてElastiCacheを配置しております。
  • ElastiCacheでは、Redisクラスターモードをオフで使用しております。
  • また、静的ファイルの配置は、 S3に行い、 CDN としてクラウドフロントを使用しております。

  • ECS on Fargateでは、一つのタスクに、開発言語であるPHPのコンテナ、Web サーバーとしてのNginxのコンテナ、そしてPHPが出力するログを転送する役目のfluentbitのコンテナ、以上の3種類のコンテナを起動させております。

  • ここでAPIサーバーとして、 ECS on Fargateを選択した理由をお話します。
  • 設計当時はコンテナを使用することが確定してから、 ECS on EC2、 EC2 ECS on Fargate、EKSの三つの選択肢がありました。
  • その中で、 ECS on Fargateは唯一インスタンスをこちらで管理する必要がなく、タスクという単位でスケールをさせればいいという仕様があり、運用上の変更管理箇所を最小限にできるサービスでした。
  • ゲーム系のサービスでは、期間限定のイベントや、ガチャの更新など、日付や時間によってリクエスト量が大きく変動することが多くあり、それに応じて、 API サーバーなどのリソースをあらかじめ多くスケールさせておくこと、といった準備を行うのですが、Fargateを使用すると、スケールの対象がタスク数のみであるため、スケールの観点を簡略化できることが、運用上かなり効率的と判断しました。
  • また、Fargateは、2019年1月より料金が値下げし、使いやすくなったことも理由の一つでした。
  • それでも現状、on EC2よりまだ少し高いのですが、このファンでは、マネージドサービスを利用し、インスタンスを管理しないことで、管理上の作業コストを減らせることの方が大きいと判断しました。
  • EKSに関しては、アーキテクチャの設計を行っていた当初は、Fargateでの稼動は出来なかったことと、当時のチームの状況的にKubernetesの学習コストが高いと考え、選択肢から除外しておりました。
  • ECS はタスク定義もシンプルでわかりやすいですし、他の AWS サービスとの親和性も高いコンテナサービスです。
  • このファンでは、 APIサーバーやバッチをコンテナ化しているのみで、その他に複雑なことはしていないので、システム要件としてはECSが必要十分なサービスだったと考えております。

  • 基本構成において、その他で使用しているサービスとしては、リクエスト制御にWAFを使用しております。
  • WAF では主に、アクセス元の IPアドレスやリクエストヘッダーなどの情報からAllow/Blockの制御を行っております。
  • また、 WAF では、 AWS が提供するマネージドルールも存在しており、このファンにおいても、その一部を適用させております。

  • また、秘匿データの格納先としてSecrets ManagerおよびSystems ManagerのParameter Storeも使用しております。
  • Secrets Managerでは、Auroraクラスターの接続情報を格納しており、定期的なパスワードローテーションを実行することでセキュリティを担保しております。
  • Parameter Storeでは、Redisの接続情報や外部サービスなどのクレデンシャル情報を格納しております。
  • API サーバーの PHP コンテナから API リクエストを送って、これらのデータを取得し、取得後は、コンテナ内にキャッシュさせることで、Secrets ManagerおよびParameter Storeへの API リクエスト料を軽減させております。

ロギング構成

  • 続きまして、ロギングの構成の紹介をします。
  • リリースを行った当初は、 ECS で機能するコンテナは PHP と Nginx の2種類でした。
  • ログドライバーとして AWS ログドライバーを使用し、それぞれのコンテナが出力したのが、CloudWatch Logsに転送させるされるように構成しておりました。
  • コストの関係上、CloudWatch Logsでログの保持期間を設けており、 PHP のコンテナのログに関しては、CloudWatch Logsのサブスクリプションフィルターを使用して、Kinesis Data Firehoseに転送し、さらに Firehoseから S3へ転送させ、S3上で長期保存を行う構成をとりました。

  • CloudWatch Logsで転送されたログは、CloudWatch Logs Insightsを用いて検索を行います。
  • Insightsは、エラー発生時などの緊急度の高い調査を行う場合のケースとして使用しております。

  • S3に保存されたログはAthenaを使用して検索するようにしております。
  • こちらは、ユーザーの問い合わせやAPIのリクエスト分布などの調査を行うというケースで用いることが多いです。
  • ALB のログは、 S3へ直接する出力されるようになっているため、同様にAthenaで検索しております。
  • 以上のように、リリース時のログの転送を行っていっていたのですが、大きな落とし穴がありました。

  • それはログの転送料金がかなり膨れ上がってしまうということです。
  • PHP コンテナから出力されるログは、エラー系のログだけでなく、先ほども話した通り、ユーザーからの問い合わせの調査などでも使用するために、INFO系のログも仕込んでおりました。
  • このINFO系のログが容量としてかなり膨大で、かつ東京リージョンにおけるCloudWatch Logsへのデータ取り込み料金は1 GB あたり0.76 USドルとなっており、 PHP のログの転送料金だけで、かなり高額な出費になってしまいました。

  • その後、ロギングの構成を見直し、改修を行いました。
  • 改修としては、FireLensの導入をすることで、 PHP のログを仕分けし、適した場所へログを転送させるようにしました。
  • FireLensは、fluentdおよびfluentbitを動作させることができる ECS 対応のログルーティングシステムです。
  • 先ほど基本構成の紹介でfluentbitコンテナとして紹介していたのは、この FireLensのことでした。
  • fluentbitとして動作する FireLens用のコンテナを新たにサイドカーとして追加して、 PHP コンテナが出力するログを仕分けるようにし、

  • エラーのような緊急度の高い調査のためのログは引き続きCloudWatch Logsへ転送、

  • 問題となっていたINFO系のログをFireLensからFirehoseに転送して、 S3へ保存するように変更しました。
  • 東京リージョンでのFirehoseのデータ取り込み料金は1 GB あたり0.036 USドルとなっており、オペレーション上では、Athenaで検索することが変わらず、ログの転送料金を約80%削減することができました。

デプロイ構成

  • 続いて、デプロイの構成を紹介します。
  • デプロイでは、CodeBuildでコンテナのビルド、CodeDeployでECS に対してのBlue/Greenにデプロイを行い、Code Pipelineでこれらのフローをパイプライン化させております。
  • 図の紫の枠で囲った部分がパイプライン化させたリソースを表しております。
  • パイプライン内のCodeBuildでは、コンテナのビルドの他に、 DB のマイグレーションの実行も行っております。
  • CodePipelineでは、実行の開始と終了のタイミングで、 Slack へ通知させるようにしております。

  • CodePipelineのそのままの実行でも、デプロイの実現は可能なのですが、デプロイの開始は、CodeBuildのビルドプロジェクトを別途設けて行っております。
  • 理由としましては、CodePipelineでは、対象のソースを落としてくる際のバージョンは、パイプラインであらかじめ設定されたGitブランチで固定になってしまうんですが、CodeBuildの場合は、実行ごとにバージョンを選択できるからです。
  • デプロイを行う際は、CodeBuildからバージョンを指定してビルドを実行させます。
  • ビルド内処理では、取得したソースを圧縮して S3保存し、CodePipelineを実行させています。

バッチ構成

  • 続きましてバッチサーバーの構成です。
  • こちらはかなりシンプルで、基本的に AWS Batchを用いて実行しております。

  • AWS Batchは、指定のコンテナイメージを使用してのバッチ実行が基本です。
  • このファンでは API用にビルドしたコンテナイメージを流用して実行しております。
  • また、実行の際に立ち上がるインスタンスの稼働時間分の重要課金制なので、料金面でも効率的にバッチ実行を行います。
  • 稼働するインスタンスのスペックは、実行するジョブごとに決められるので、バッチの処理内容に応じて変化させることが可能です。
  • このファンでは、分析用のデータをExtract/Transform/Loadする処理や、ユーザーランキングの生成処理などの用途で使用しております。

分析用データETL処理構成

  • 今お話した分析用データの、 ETL 処理の構成も紹介させていただきます。
  • CloudWatch Eventsをトリガーとして、1日1回Lambdaが実行され、Lambda内でバッチの実行を行います。
  • バッチ内の処理ではAuroraクラスターのPoint In Time Recoveryを行い、その後、あらかじめ用意しているGlueの情報を実行しています。
  • 実際に ETL 処理を行っているのは、このGlueのJobです。

  • CloudWatch Eventsでバッチを直接実行せずに、 Lambda を挟んでいる理由は、複数のバッチジョブを実行するに当たり、依存関係を持たせるように実行したかったのと、バッチのJobのリビジョンを常に最新のものを実行させるようにしたかったためです。

  • また、バッチ内の処理でAuroraのPoint In Time Recoveryを用いて、クローンクラスターを作成している理由は、GlueのJobで ETL 処理を行う際に、本番環境で稼働しているクラスターに影響を与えないようにするため、また仮にGlue Jobが失敗したとしても、クローンクラスターを残していれば、再実行の際もデータの同一性を担保できるからです。

  • その他にも、Route53、 AWS Certificate Manager、 API Gateway、 DynamoDB など、用途に応じて適材適所で AWSマネージドサービスを組み合わせて設計構築を行っておりました。
  • 以上が、この版におけるアーキテクチャの紹介とマネージドサービスの活用事例でした。

アーキテクチャの性能検証と事例共有

  • 続きまして、構築したアーキテクチャの性能検証と、その過程での事例の共有を紹介します。

  • まずは負荷試験でのお話をさせていただきます。
  • 設計をもとに構築したシステムに対して負荷試験を行うにあたり、事前準備として、これらのことを行っておりました。

  • また、負荷試験用の環境においては、アプリケーションパフォーマンス管理サービスである New Relicを導入し、主に PHP のプロファイリングを行っておりました。
  • New Relicを導入するにあたり、 ECS の全てのタスクにNew Relicを適用させると費用がかさんでまうということを懸念し、New Relic用の ECS サービスを別途作成し、そのサービス内にNew Relicを適用させたタスクを最小限で起動させ、 ALB でリクエストを配分させることにしました。

  • ここで負荷試験を行っていく上で、いくつかつまずいたことと対処法を紹介させていただきます。
  • 一つ目は、Fargateで立ち上がる ECS タスクが起動ごとにパフォーマンスのばらつきがあったことでした。
  • New Relicを用いて PHP のパフォーマンスを確認していたのですが、同じバージョンのソースでもタスクを一度落としてもう一度起動させた場合にNew Relic上に表示させるプロセスタイムに大きく差があることが確認できました。
  • これは、コンテナが稼働するためのホストマシンのスペックが同一サイズのタスクでも起動ごとに違うことが起因するんではないかと推察しました。
  • これに関しては、1タスクあたりのおおよそのリクエスト許容限度を把握し、想定負荷に対しての余裕を持ってタスクを用意するという対象で開始しました。

  • 二つ目は、ECSの数タスクが急激な過負荷によって落ち始めると、それに呼応して、なし崩し的に他のタスクが落ちていき、リクエストが落ち着くまで、タスクの復旧がされない状態に陥ることでした。
  • これに関しては、タスクが落ちる原因は単純に1タスクにおける許容リクエストの限界を超えた超えていたことなのですが、その後、自動復旧するタスクも復旧するや否や過負荷で落ちるというループに入るため、数タスクが落ちることは、イコールシステムの障害という状況になりました。
  • こちらは、 PHP コンテナへの瞬間的な高負荷を緩和させるために、 Nginx上の設定に流量制限を設けることで対処しました。
  • また、それに合わせて、流量制限で引っかかった際の HTTP ステータスコードをALBのヘルスチェックの正常コードに登録しておき、クライアントアプリケーションでも、ステータスコードで帰った際のハンドリングをするように改修しました。

  • 負荷試験を行った結果、このファンのリリース直後の稼働リソースはこちらの通りになりました。
  • ECS on Fargateに関しては、 CPU 使用率をしきい値としてのオートスケールを設定しており、それに応じたスケールインアウトを可能とさせております。

  • 続きまして、障害試験を行った話です。
  • 障害試験では、リソースごとに障害を意図的に起こして、サービスが正常に継続するかどうか、もしくはサービスがどのように稼働不能になるのかを検証します。
  • まず準備として、図のように、障害試験を行える前提条件をリストアップし、これらが全て達成できていることを確認した上で、障害試験を開始することと定義しました。

  • また、障害試験で大事になってくるのは、実際に障害を起こす対象のリソースと障害の起こし方および障害を起こした際の想定挙動をリストアップすることです。
  • 例えば、対象リソースをAuroraクラスターとし、フェイルオーバーの障害試験を行う際は、一定数のリクエストを送信してる間に、手動でフェイルオーバーを実行します。
  • このファンでは、その際の想定挙動は、readerインスタンスが昇格するまではエラーが発生するが、昇格後は正常挙動になるというように想定しました。

  • マネージドサービスを使用していると、障害を意図的に起こせないサービスは、どのように障害試験を行うかということに直面します。
  • こちらに関しては、サービス自体の設定や、そのサービスに対してアクセスをするリソースの設定をいじることで、擬似的に障害を起こっている状態を再現するしかありませんでした。
  • 例えば S3に接続できないという障害ですとしたい場合、対象バケットにアクセスできないように、バケットポリシーでブロックの設定をわざと入れることで、当該バケットにアクセスするリソース上でエラーが発生することを確認するというふうに試験を行っておりました。

  • 障害試験においてもつまずいたことと対処法を紹介させていただきます。
  • 障害試験を進めていくと、一定量のリクエストを送信している最中に、Blue/Green Deployを実行した場合、エラーが瞬間的に多く発生するという事象が確認できました。
  • これは ECS のタスクが起動した直後は、Parameter Storeなどのデータを、まだコンテナ内にキャッシュしておらず、Blue/Green Deployで、 ALB のターゲットグループが切り替わった直後のリクエストをトリガーとして、パラメータストア API リクエストを送信するような処理になっており、 ECS タスクが一斉にデータを取得するための API をリクエストすることで、Parameter Storeのスループットの上限に到達していたことが原因でした。
  • そして、Parameter Storeは最大1000リクエスト毎秒でリクエストを受け付けられるのですが、スループットの上限をそれ以上緩和できない仕様でした。

  • これらの対処方法としましては、 ALB のヘルスチェックで使用している API の処理内でParameter Storeのデータ取得を行うように改修し、Blue/Greenが再ルーティングで切り替わる前に必要なデータをキャッシュできている状態を実現させることにしました。
  • そうすることで、一定数のリクエストを受け取っているあん間にデプロイを行ったとしても、デプロイ完了後に、パラメーターストアへのリクエストが集中することはなくなり、エラーは発生しなくなりました。
  • 以上、アーキテクチャの性能検証と事例共有でした。

マネージドサービス導入の利点と注意点

  • ここまでこのファンにおける多くの マネージドサービス の活用事例を紹介してきましたが、続きまして、このファンのアーキテクチャの設計構築運用を経て、マネージドサービス導入の利点と注意点を紹介したいと思います。

  • まずは利点の方ですが、やはり最初に述べた通り、 マネージドサービスを使用することで、運用上の管理作業を削減できることは、かなり大きいと感じております。
  • 中でも、Fargate、CodeBuildやCodeDeploy、 AWS Batchを導入したことで、従来では API サーバーもちろん、 CI サーバやバッチサーバーに対して、インスタンスを立てて構築していたのですが、その運用方法と比較して大きく効率化を図ることができました。
  • また、これは当初想定していなかったのですが、開発運用過程において、サーバーエンジニアとインフラエンジニアの共通認識の領域が増えたことも利点に感じました。
  • 今までは、弊社のエンジニア組織としては、アプリケーションレイヤーの開発はサーバエンジニア、それより下のレイヤーの開発はインフラエンジニアというように役割としてかなりわかれていたため、双方で共有するナレッジの領域はそこまで多くなかったのですが、今回、コンテナの導入および マネージドサービスの対応をしたことで、それらの仕様を両者が知っていることが前提での開発運用になるため、従来に比べて情報の共有がしやすくなりました。

  • そして注意点の方ですが、まずは マネージドサービス 自体も障害が起こる可能性はゼロではないため、障害が発生した際の自サービスへの影響範囲は、必ず把握しておく必要があります。
  • 障害試験の紹介の際にも話した通り、各リソースに対して疑似的な障害を発生させ、その際の自サービスの影響範囲をレポートとして残しておきます。
  • そして使用しているマネージドサービスにおいて、障害が発生した場合に素早く検知できるような体制や仕組みを準備しておくことも重要です。
  • このファンでは、システム上のアラート通知とは別に、 Personal Health Dashboard 上のイベントも Slack 通知させるようになっております。
  • 致命的な障害の通知から使用しているサービスのマイナーバージョンアップの推奨イベントまで細かくキャッチアップできるようにしております。
  • あとは何かあった際に、すぐサポートに問い合わせできるような体制を整えておくといいと思います。
  • マネジメントサービスで障害が起こった際、大概のことはこちらでは関与できません。
  • それでもサポートに問い合わせることで、障害の状況の詳細や復旧目途などの情報をもらえることがあります。
  • 弊社では、エンタープライズサポートプランを採用しており、開発時の技術相談から、リリース時や深夜の長時間メンテナンス時に待機いただくなど、大変お世話になっております。
  • 以上 マネージドサービスにおける利点と注意点の紹介でした。

まとめ

  • まとめです。

  • 本発表では、弊社で運用中のサービスである、このファンにおけるアーキテクチャと マネージドサービスの活用事例の紹介をしました。
  • アーキテクチャは、Fargateや AWS Batchなど、インスタンスを意識しないサービスをできるだけ多く使用するように設計しておりました。
  • また、構築したアーキテクチャに対して負荷試験と障害試験を実施し、検証時の事例を一部紹介しました。
  • そして、マネージドサービスを活用することで、運用における管理コストを従来と比較して大きく低減させることができたという利点、また、マネージドサービス を使用するにあたり、障害の件影響度の把握が必要といった注意点を発表させていただきました。

  • 最後にこの場を借りて、このファンのリリースおよび現在の運用において大変お世話になっている AWS の皆様にお礼を申し上げたいと思います。
  • 誠にありがとうございます。
  • 説明は以上となります。
  • ご清聴ありがとうございました。