はじめに
こんにちは。エモーションテック SREチームのおかざきです。
以前の記事で紹介した弊社プロダクト「EmotionTech」の実行基盤について、今年2月にAWS Elastic BeanstalkからAWS Fargateに全面的に移行しました。
この記事では移行により解決した課題と移行プロセスについてご紹介したいと思います。 Fargateの導入についてはすでに多くの事例があると思いますが、これから導入する方の参考になればと思い執筆しました。
なお、本記事で触れるプロダクト「EmotionTech」とは、直近でいくつか記事を掲載している「Google CloudでRustおよびNestJSを利用したマイクロサービスアーキテクチャのプロダクト」とは別のプロダクトです(以下、Google Cloudを利用したプロダクトのことを新プロダクトと呼びます)。
移行前後のシステム構成
移行前後のシステム構成イメージを示します。 変更した要素を黄色の枠で示します。
移行前
移行後
アプリケーションがElastic Beanstalk Docker 単一コンテナで稼働していたところを、全てFargateに変更しました。 また、ロードバランサーがElastic Beanstalk管理のClassic Load Balancerであったため、 これを新規構築したApplication Load Balancerに変更しました。
移行前はEC2の監視エージェントやログ転送エージェントのインストールをAnsibleで実施していましたが、 移行後は全てサイドカーコンテナとして実装しました。
インターネット上に公開するURLやデータストアは変更せず、ユーザーから見ると何も変わっていないように振る舞う形にしました。
移行した理由
現在、新プロダクトの開発を盛んに行っていますが、「EmotionTech」は多くのユーザー企業に利用して頂いていることもあり、引き続き安定稼働させる必要があります。そのためには以下の課題に向き合う必要があると考えたため移行を実施しました。
課題について
移行に伴い解決したいと考えた課題を以下に示します。
- インフラのセットアップをECSの仕組みに統一して、安定起動させたい
- スケールアウトにかかる時間を安定させたい
- インフラの構成をコードで管理して、全体像の把握と設定ミスの混入リスクを低減したい
各課題の説明とどう解決したかを示します。
*Elastic Beanstalk 自体に問題があると言いたいわけでは無く、あくまでプロダクトの運用の中で生じた課題を解決したかったというスタンスですので、その点ご了承頂ければと思います。
インフラのセットアップをECSの仕組みに統一して、安定起動させたい
Elastic Beanstalk 単一コンテナ構成により、アプリケーションはEC2上のコンテナとして稼働していましたが、 EC2インスタンスのロギング、監視エージェントのインストール等はAnsibleで行っていました。
Ansibleでは外部要因による処理の失敗がしばしば見られました。例えば、yumによるパッケージの取得元リポジトリが一時的にアクセス不可になるなどの原因で、Ansibleが急に失敗することが発生していました。このような事象が商用環境でスケールアウトを行った際に発生したこともあり、不安を感じていました。
解決方法
Fargateを利用することでEC2インスタンスの設定を不要にしました。 また、監視エージェントなどの運用機能はサイドカーコンテナとして実装しました。 コンテナにすることで開発環境で動作確認をしたものを商用環境でも利用できるため、急にパッケージのインストールが失敗するようなケースを防止できるようになりました。
スケールアウトにかかる時間を安定させたい
EC2インスタンスをスケールアウトする時にInsufficientInstanceCapacityエラーが発生する場合がありスケールアウトに30分程度かかる事象が発生していました。 また、エラーが発生しない場合も、インスタンスが起動してからAnsibleの適用までを含めると4-5分程度かかっていました。
スケールアウトは、計画的に行うこともありますが、急なトラフィック増に対応するためにAutoscalingを行うこともあるので、可用性を高める意味ではスケールアウト時間に幅のある状況は解消したいと考えました。
解決方法
こちらもFargateを利用できることで改善できました。 Fargateではバックエンドのインスタンスを自動的に確保してくれるためか、InsufficientInstanceCapacityのようなエラーには今のところ遭遇していないです。 また、通常の起動時間も4-5分程度から1分程度に短縮されました。
インフラの構成をコードで管理して、全体像の把握と設定ミスの混入リスクを低減したい
元々インフラがコードで管理されておらず、AWS上のリソースを一つずつ確認してインフラ構成を把握する必要がありました。 また、設定変更を行う時にどうしても人の手が介入することにより、設定ミスだったり環境間での設定の差分が発生しやすく、差分が発生していることに気が付きづらい状況が発生していました。 Elastic Beanstalkは、画面からの設定だけで様々なリソースをセットアップでき、サービスをスモールスタートしたいようなケースで有用と考えます。 ただ、立ち上げたインフラを数年間に渡り保守することを考えると、インフラを極力コードで管理したいと考えました。 今回のようなインフラの更改を行うことになった場合も、コード管理されていれば修正箇所の調査や検証環境の構築をスムーズに行えるようになると考えました。
解決方法
Terraformを利用しました。Terraform moduleを活用し環境間で共通な要素をまとめて定義することで、環境間の意図しない設定差分を混入させづらくすることを目指しました。
moduleの構成イメージは以下の通りです。 書籍Terraform: Up & Runningを参考にmoduleを構成しました。
├── development │ ├── <terraformコード> ├── staging │ └── <terraformコード> ├── production │ └── <terraformコード> ├── modules ├── aws_acm ├── aws_alb ├── aws_ecs_cluster ├── <他AWS系リソースの汎用モジュール> ├── app-dashboard └── <ダッシュボード画面機能のモジュール群> ├── app-survey └── <回答画面機能のモジュール群> ├── app-worker └── <worker(非同期処理サーバ)のモジュール群>
Terraformを利用するにあたっては、Terragruntをセットで導入しこちらの例にあるようにremote stateの記述をDRYにして設定ミスを防止しました。
また、新たに用意したFargateのECSタスク定義もgit管理にし、GitHub Actionsでデプロイに利用できるようにしました。
上記によりサービス提供に必要な一通りの要素をコード管理できるようになりました。
AWSにはインフラを管理できる製品にCloudFormationもありますが、弊社ではAWS以外のGoogle Cloudや今後異なるプラットフォームも選定する可能性があるためTerraformを選択しました。 TerraformはパブリッククラウドだけでなくNew Relicのような製品にも対応しているので助かっています。
改善を諦めた点
あえて優先度を落とした課題もあるので開示してみます。
既存アプリケーションではアプリケーションログをローカルディスクに出力する実装になっていました。 ログがコンテナの標準出力に出ていればローカルディスクの容量を考慮しなくても良くなり、より運用がしやすくなると考えますが、 アプリケーションの改修に工数がかなり掛かる見込みがあったため、改修は行わずFargateのエフェメラルストレージ上にログを出力するようにしました。 そしてサイドカーとしてfluentbitとlogrotatedコンテナを導入して、ストレージ上のログの転送とログローテーションを実施するようにしました。
The Twelve-Factor App的にはベストなやり方ではないと考えますが、 Fargateはデプロイ等のタイミングでECSタスクが入れ替わるとエフェメラルストレージも初期化されるので、 短期的なログローテーションとログ保全が上手くいっていれば大きな問題になることは無いのではと考え、ログ出力の変更は優先度を落としました。
移行プロセスについて
移行プロセスは以下のような形にしました。事前試験と本番切替についてどのように行ったかを述べます。
新環境の事前試験
新しく構築したFargate環境は、現行環境と同じデータストアを参照させる形で起動しました。 移行を無事に完遂するため、事前試験は入念に行いました。
インフラ面の試験観点としてはざっと以下の形です。
- ECSタスクの設定変更
- 比較的変更しそうなメモリ制限値などを変更して反映できることの確認。変更手順の確立も兼ねる
- ECSタスクと連携するAWSリソースとの疎通確認
- Aurora, OpenSearch, Redis, SQS等が連携先であるため、これらを利用する機能をピックアップして自動テストを実行
- ロードバランサー
- スケーリング
- Terraformを用いての起動タスク数の増加、減少
- k6で負荷を発生させた上で、Autoscalingによる起動タスクの増加、減少の挙動確認
- デプロイ
- ローリングアップデートの戦略が想定通りであるかの確認 (デプロイ中のHealthyなタスク数の確認等)
- デプロイの影響で対ユーザー通信に影響が出ないことの確認
- ECSタスク定義の記述が誤っていた時にデプロイが失敗し、その失敗した旨がアラートとして通知されることの確認
- 各種ログの転送確認
- 監視メトリクスの可視化、アラート動作確認
- Container Insights
- New Relic
開発環境、ステージング環境を新規構築してそれぞれ試験を実施しました。
商用環境ではさらに次のようなことを実施しました。 フロント画面、バックエンドAPIを提供するFargateに対し試験用のDNSレコードを払い出し、Product Teamによる動作確認と、カスタマーサポートチームによる動作確認を実施しました。 カスタマーサポートチームも当社システムを利用して業務を行っているので、ユーザーの一人として動作確認に協力してもらいました。
動作確認は1ヶ月ほどの期間を設け、その間アプリケーションの挙動に問題がないこと、デプロイを何度か行ったりログ保存状況を見たりと運用機能に問題がないことを確認しました。
本番切替
Route53上にある本番URLのAレコードの値の変更を行いました。
現行環境ではClassic Load BalancerがElastic Beanstalkと紐づいて管理されていたため、 Fargate環境向けに新たにApplication Load Balancerを構築しました。 ロードバランサーの変更が不要な場合は、Target Groupを用いて新旧環境の切替ができると考えますが、 今回はロードバランサを含めた構成変更となったので、DNS切替による移行を実施しました。
切替作業自体はterraform applyのみの数分程度の作業にすることができたため、切替後の正常性確認に集中できました。 作業自体は安全のためメンテナンス時間帯に実施しましたが、理論的には無停止で切替を行うことができました。
なお、DNS切替を行う場合はクライアント側のDNSキャッシュに注意する必要があり、Route53上で設定したTTLよりも長い時間キャッシュを持つクライアントもいる可能性があることに注意しました。 実際、DNS切替翌日に旧サーバへのアクセスログを見たところ、一部ユーザーからのアクセスが確認されました。 切替後も旧サーバは暫くの間稼働させていましたが、その点は正解だったと思います。
結果
サービス影響のあるトラブルを発生させず切替を行うことができました。 また、切替作業日から記事執筆時点で3ヶ月ほど経過していますが、今のところ安定稼働しています。 デプロイやスケールアウトの所要時間が短縮されたことにより精神衛生的にも良い状況になっています。
トラブル無く実施できた要因としては以下があると思います。
- 既存環境と並行運用して動作確認を入念に行った
- 社内メンバーにも協力してもらって長期安定確認をした
- Terraform化による設定ミスが混入するリスクの低減
今回の移行では技術的に目新しいことをしたわけではないですが、 移行により解決にしたい課題や一つ一つの工程で目指すことを明確にし、余裕をもって実施していったことで安定運用に貢献できたと考えます。
おわりに
この一年は新プロダクトをGoogle Cloudで構築しつつ「EmotionTech」のFargate移行や運用保守も同時に行っていたので大変でしたが、サービスの信頼性に関わる部分を改善できたのでよかったです。 運用負荷の低減により新プロダクトに対してもさらに注力できると期待しています。
エモーションテックでは顧客体験、従業員体験の改善をサポートし、世の中の体験を変えるプロダクトを開発しています。 直近の事例としてはChatGPTのモデルを活用したテキスト特化のAI分析サービスも2023年4月より開始していきます!
この記事や他の記事を見て少しでも弊社に興味をもっていただけましたら、カジュアル面談も行っていますのでご応募お待ちしております。