1. はじめに
こんにちは、SWEのあかりです。
今回のテーマは、SRE NEXT 2023のCall For Proposals(CFP) に応募したものの、残念ながら不採択になってしまったものです。話せるネタとしてはまとまっていたので、テックブログとしてここに捧げます😇
2. 本記事の概要
社内で最も古くから稼働している施工管理アプリでは、主にデータ修正と有事の際のログイン環境として開発者向けのEC2インスタンス(以降、「バッチサーバ」と表現)が存在していました。この記事では、このバッチサーバの廃止1を目的として、このサーバが担っていた役割をサーバレス環境・コンテナ環境へ移行し、EC2インスタンスからの脱却を達成した取り組み2について説明します。
この記事を読んで得られることは以下の通りです。
- EC2インスタンスを廃止する取り組みの流れ
- 技術選定時に定性分析を行う事例
- 本番データを修正する環境の構成とその運用方法の事例
- 本番環境にログインする環境の構成事例
3. バッチサーバが担っていた役割
バッチサーバを廃止するには、該当インスタンスが果たしている役割を他の環境に移行することが求められます。そこでまず、関係者へヒアリングを行い、バッチサーバの役割が以下の5つであることを特定しました。
- 施工管理アプリの本番データの修正作業を行う環境(以降、「データパッチ環境」と表現)
- 他社サービスからANDPADへ移行いただいたユーザー様向けに、初期データを流し込むための環境
- 施工管理アプリで問題が発生した場合に、開発者が調査目的でログインするための環境(以降、「ログイン環境」と表現)
- 施工管理アプリのバッチ処理で、Pod上での稼働を憚るほど長時間動作するものをホストする環境
- 施工管理アプリとは関係のないプログラムを定期実行するための環境
これらの役割を別の環境に移行するタスクにおいて、4と5は他の開発者が既に取り組んでいることがわかり、2は1の環境構築後に同じものを利用する方針で合意が得られました。ですので、今回私が責務を持って取り組むべきは、上記1と3の役割を別環境へ移行することでした。
4. データパッチ環境の移行
4-1. 要件定義
移行先環境を選定するにあたっては、データパッチ環境の要件を把握した上で、これを満たす必要があります。そこで、関係者にヒアリングを行い、要件を整理しました。その結果判明したデータパッチ環境の要件は以下の通りです。
- 施工管理アプリの本番環境のDBにアクセスし、データを変更できること
- 施工管理アプリのmasterブランチ(=本番環境ブランチ)以外のコードも実行できること
- rake taskの実行とrails runnerによるプログラムの実行ができること
- データパッチプログラムがエンキューした非同期ジョブが処理されること
- データパッチプログラムの作業ログをファイルとして回収できること
- 「承認を得ていない本番更新操作を行なっていないこと」の監査ができること3
2つ目の要件が必要である理由は、本番データ修正では、一時的なデータパッチプログラムを実行することが多いからです。つまり、実行するデータパッチプログラムは一時的なものなので、本番ブランチにマージしたくないという意見があり、これを要件に加えました。
4-2. 技術選定
まず、上記の要件を満たせそうな技術を以下の通りピックアップしました。
- CodeBuild
- ECS on Fargate
- KubernetesのJobリソース
そして、これらの技術に対して評価項目を選定し、下表のように4定性的に分析を行いました5。その結果、CodeBuildが今回の目的に最も適していると判断しました。というのも、「要件の満たしやすさ」「構築の容易さ」「維持・管理の容易さ」「データパッチ作業の容易さ」の観点で利点があったからです。残りの観点については議論の余地がありましたが、以下のように考えて、CodeBuildを採用して問題ないと判断しました。
- 「緊急停止の方法」:いずれの技術を採用した場合でも、アプリケーションロジック側での制御を必要とするので、各技術に大きな差異はない。
- 「データパッチプログラムの実行時間」:CodeBuildには8時間の実行制限はあるものの、これは十分許容できる時間である。
- 「実行にかかる金銭的コスト」:他の観点での利点を覆すほどの大差はない。
評価項目 | CodeBuild | ECS on Fargate | Kubernetes Job |
---|---|---|---|
要件の満たしやすさ | 易 1. 本番環境ブランチ以外のソースコードをイメージビルドする必要がない。 2. 本番コンテナイメージをベースにした上で、本番環境以外のブランチを取得してデータパッチプログラムを実行すればよい。 |
やや難 1. 本番環境ブランチ以外のソースコードをイメージビルドする必要がある。 2. そのためのデプロイフローや監査の観点に留意した運用フローを整備する必要がある。 |
やや難 1. 本番環境ブランチ以外のソースコードをイメージビルドする必要がある。 2. そのためのデプロイフローや監査の観点に留意した運用フローを整備する必要がある。 |
構築の容易さ | やや易 同様の構成であるCodeBuildプロジェクトが既に存在しており、既存のノウハウが活用できる。 |
やや難 1. 本番環境ブランチ以外のコンテナイメージをビルドしてデプロイする必要がある。 2. データパッチを実行する際にタスクを起動・終了する方法も検討・構築する必要がある。 |
難(やや難) 1. 本番環境ブランチ以外のコンテナイメージをビルドしてデプロイする必要がある。 2. データパッチ用のHelmチャートを用意する必要がある。その場合は、データパッチ用Helmチャートのデプロイフローも整備する必要がある。 3. ただし、既存のHelmチャートに相乗りする形でJobリソースを追加できる可能性もあり、その場合は(やや難)になる。 |
維持・管理の容易さ | 易 社内ではCIツールとしてよく使われており、扱える開発者が多い。 |
普通 ECSの知識が必要。 |
普通 Kubernetesの知識が必要。 |
データパッチ作業の容易さ | 易 1. 基本的にCodeBuildを実行するのみ。 2. 社内の開発者がCodeBuildプロジェクトの実行に慣れている。 |
普通(やや易) 1. 作業の都度ECSタスクの起動と終了を行う必要がある。 2. ただし、作り込み次第で(やや易)になる可能性はある。(例えば、AWS ChatBotでSlack連携し、SlackのチャネルからECSタスクを起動するAWS CLIコマンドを叩くようにするなど。) |
普通 1. Helmチャートを新設する場合も、作り込み次第で通常のリリースフローとほぼ同じにできそう。 2. ただし、そもそも通常のリリースフロー自体が容易なのか議論の余地がある6。 |
緊急停止の方法 | 実行中の「ビルドの停止」が可能。ただし、安全に停止するためには、アプリケーションロジックでの工夫が必要。 | 実行中のタスクを停止することが可能。ただし、安全に停止するためには、アプリケーションロジックでの工夫が必要。 | KubernetesのJobリソースを停止する仕組みを別途構築する必要がある。Podの終了時には、コンテナにSIGTERMシグナルが送信されるので、シグナルを利用してデータパッチスクリプトを安全に緊急停止することも可能。 |
データパッチプログラムの実行時間 | 8時間 CodeBuildには、実行時間が8時間までというクオータ制限がある。 |
無制限 | 3時間 1. そもそもKubernetes上で特定のPodが長時間実行されるのは好ましくない7。 2. グレースフルシャットダウンの終了猶予時間を3時間に設定している8。 3. インフラとして実行時間に制限できないので、データパッチプログラムの実装者に配慮してもらう必要がある。 |
実行にかかる金銭的コスト | 高 AWS Fargateと比べると高い。 |
中 Kubernetes Jobと比べると高い。 |
低 起動済みのノード上でPodを起動してプログラムを実行するだけ。 |
4-3. 構築したデータパッチ環境とその運用・実行フロー
下図に、構築したデータパッチ環境の構成と本番データ修正の運用・実行フローを示します。多くの場合、本番データ修正として実行されるプログラムは一時的なものであり、その場合は本番ブランチにはマージしていません。ですので、下図ではデータパッチプログラムのPRを作成し、そのPRをCodeBuildで実行する場合のフローを示しています。
本番データ修正の運用フローを整理すると、以下のようになります。
- データパッチプログラムの追加: 開発者がデータパッチプログラムを追加するPRを作成し、他の開発者からのレビューと承認を受ける。
- 本番データ修正の作業承認: 上記のPRを参照し、執行役員以上の承認をもらう。
- CodeBuildの実行: 承認を受けたPRとデータパッチプログラムを指定して、CodeBuildを実行する。
- Slack通知と承認証跡の添付: データパッチの開始・終了時にSlack通知を受け取り、通知のスレッドに執行役員以上の作業承認証跡を返信として添付する。
上記のような運用フローとなるようにデータパッチ環境を構成することで、最も重要な要件である本番データ修正作業が承認を受けて行われているのかを監査できることを実現しています。これを実現するための実装上の細かい工夫としては、開発者からの要望であるrails runnerによるスクリプト実行は可能としつつも、rails runnerで指定できるのはソースコードに含まれるファイルパスに限定している点が挙げられます。このような制約を入れることで、rails runnerによるスクリプト実行の場合でも、承認を受けたデータパッチプログラムのみがCodeBuildで実行されるように制限しています。
加えて、本番データ修正の作業時間を短縮するための工夫も実施しています。CodeBuildで使用するイメージは、その時点で本番動作しているコンテナイメージを使用し、その上で、PRブランチのソースコードを取得させています。これにより、イメージビルドの待ち時間を削減しています。ただし、データパッチ用のPRブランチと本番ブランチの依存ライブラリなどが一致していることが前提です。この点については、開発者への注意喚起をドキュメントに記載し、運用で回避する方針を採用しています。
5. ログイン環境の移行
5-1. 要件定義
ログイン環境については、そもそもの必要性から議論する余地がありました。というのも、アプリケーションコードの動作確認としては、本番同等の検証用環境が別途用意されており、かつ、本番データを修正する環境としては、4章で説明したデータパッチ環境を構築したからです。そこで、ログイン環境そもそもの必要性について、施工管理アプリのエンジニアと議論を行いました。その結果、以下2点の理由で施工管理アプリではログイン環境が必要であると判断しました。
- 本番データ特有のバグに対して、直接調査を行う環境を利用することで、迅速にバグを解決できる場合がある。
- 施工管理アプリは、社内で最も古くから稼働している巨大なモノリシックアプリケーションであり、全てを詳細に把握しているエンジニアがいない。加えて、今までは本番環境にログインできる環境がある前提で運用を行なっていたので、急に本番環境にログインする環境がなくなることに漠然とした不安がある。
よって、バッチサーバと同等のことができる環境をEC2以外で構築することにしました。そして、バッチサーバで実現していたことを整理し、これを新設するログイン環境の要件として定義しました。
機能要件
- 本番環境のブランチでrails consoleが起動できること
- 起動したrails consoleから本番環境のデータベースとRedisとS3バケットにアクセスできること
- 起動したrails consoleから非同期ジョブをエンキューした場合に、これが処理されること
セキュリティ要件
- 開発者がログインした場合に、これを検知してSlack通知すること
- ログインした開発者が、危険なコマンドを実行した場合に、これを検知してSlack通知すること
- 上記のSlack通知に対して、執行役員以上から承認を受けた旨の証跡を返信せずに1日放置した場合、アラート通知がなされること
5-2. 技術選定
EC2インスタンス以外でログイン環境を構築したかったので、選択肢としてはECS on FargateとKubernetesのDeploymentリソースの2択で検討しました。本来であれば、4章で説明したような定性分析を行うべきところですが、この技術選定においてはECS on Fargateの方に大きな利点があったので、定性分析の結果を表形式でまとめるまでは行っていません。というのも、ECS on Fargateでログイン環境を構築した場合、バッチサーバがセキュリティ要件を満たすために実装されていた既存の設定が、ほぼそのまま流用できたからです。逆に、KubenetesのDeploymentリソースでログイン環境を構築した場合には、KubernetesのPodへのログインを検知する仕組みから検討する必要があります。社内にはその実績がないため、調査・議論・実装に時間がかかってしまうことが明白でした。したがって、ログイン環境はECS on Fargateにて構築することとしました。
5-3. ログイン環境の構成
構築したログイン環境の構成を下図に示します。構成はシンプルですので、文章による説明は省略します。ただし、「ECSサービスは必要ないのでは?」と疑問に思う読者の方もいらっしゃるかと思うので、その部分のみ補足説明します。ECSサービスの必要性を疑問視された方はその通りで、これはなくても問題ありません。しかし、今回は開発者がログイン環境を起動する利便性の向上を目的として、これを用意しています。つまり、ECSサービスを用意しておくことで、開発者はECSタスク数を0から1に変更すればログイン環境が起動できるようになります。逆にECSサービスがない場合には、ログイン環境の起動の都度、ECSタスクを起動するネットワークなどを指定する必要があり、作業手順が若干複雑になってしまうのです。
次に、ログイン環境のログをCloudWatch Logsに回収し、かつ、ログとメトリクスをDatadogでも回収する仕組み9について下図に示します。
ここでは、ログイン用アプリケーションコンテナの標準出力をCloudWatch Logsに転送するために、アプリケーションコンテナと同じタスク定義内にログルーティング用のFluent Bitコンテナを定義しています。こうすることで、FireLensという仕組みによって、アプリケーションコンテナのログがログルーティングコンテナに転送され、CloudWatch Logsへログが送信されます。
また、コンテナのメトリクスとログをDatadogで収集するために、アプリケーションコンテナと同じタスク定義内にDatadog Agentのコンテナを定義しています。これにより、同一タスクとして起動する2つのコンテナ(アプリケーションコンテナとログルーティングコンテナ)のメトリクスとログがDatadogに収集されます。
最後に、開発者によるログイン環境の利用と危険なコマンドの実行を検知して、Slack通知する仕組みを下図に示します。ただし、これに関しては既存の設定がほぼそのまま利用できたので、今回の移行に伴う大きな変更はしていません。
まず、開発者によるログイン環境の利用をSlack通知する仕組みですが、これはECSに対するコマンド実行やSSMを利用したコンテナとのセッション確立を検知してSlack通知するようにしています。下図に示した構成は一般的なものですので、文章による詳細説明は省略します。また、危険なコマンドを検知する仕組みについてですが、これは実行コマンドログのパターンマッチングにより実現しています。つまり、SSM経由で実行されたコマンドを全てログとして記録し、このログの中に危険な文字列が含まれている場合にSlack通知するようになっています。
6. まとめ
本記事では、バッチサーバが担っていた役割をサーバレス化・コンテナ化し、これを廃止した取り組みについて説明しました。特に、データパッチ環境をCodeBuildへ移行したことと有事の際のログイン環境をECS on Fargateへ移行したことについて詳細に説明しました。
本取り組みで行った手順を整理すると、以下のようになります。
- 関係者にヒアリングを行い、バッチサーバが担っている役割を整理する。
- 一つ一つの役割の要件を定義し、関係者の合意を得る。
- 役割一つ一つについて移行先の技術候補を列挙し、これらを定性的に分析する。
- 定性分析を行った結果と、その上で下した判断をドキュメント化する。
- 移行先環境を構築する。
- 運用フローを整えて、開発者に周知する。
本記事では、技術選定時の定性分析や構築した環境や運用フローについて詳細に説明しました。ですが、この取り組みの中で最も重要だったのは、関係者にヒアリングを行ったり、要件を定義して合意を取ったりする段取りです。よって、実際の業務で重要なのは、「技術」よりもその前の「事前準備」である点を強調して、本記事のまとめとさせていただきます😁
7. 最後に
アンドパッドでは、「幸せを築く人を、幸せに。」というミッションの実現のため、一緒に働く仲間を大募集しています。 チーム一丸となって良いプロダクトを作りたい!と思われる方はぜひぜひご応募ください!
- バッチサーバを廃止したかった理由は次の通りです:(1) 弊社設立の初期に手動構築された背景があり、このサーバの再構築が困難である。(2) 再構築が困難であるため、OSのバージョンが古いままとなっていた。(3) 単一障害点である。(4) コンテナをホストするようにはなっていなかったため、施工管理アプリのライブラリアップデート作業の工数が余分にかかっていた。↩
- このプロジェクトは2023年4月末に達成しました。↩
- 弊社はSOC1 Type1 報告書を受領しています。↩
- テックブログとして外部公開する都合上、実装当時の定性分析結果から多少変更を加えています。ただし、大枠は変更しておらず、結論も同じままです。↩
- 定量的な評価ができないので、評価者の知識の濃淡が少なからず反映されており、主観的な評価になっています。しかし、技術選定においては、できる限りの定性分析を行うこと、そして、その結果をドキュメントにまとめて後から見返せるようにしておくことが重要だと考えています。↩
- 本記事の執筆時点(2023/8/16)では、通常リリースフローが開発者にとって容易なものに改善されています。↩
- ワーカーノードのスケールインを妨げないように、Podの実行時間は極力短くするのが望ましいという意味です。↩
- terminationGracePeriodSecondsを3時間に設定しているという意味です。terminationGracePeriodSecondsのデフォルト値は30秒ですが、施工管理アプリではこれを3時間に設定しています。↩
- 弊社では、運用監視ツールとしてDatadogを利用しています。↩