プロジェクト概要

Heroku 上で稼働しているアプリケーションをAWS の ECS(Elastic Container Service)に移行するプロジェクトです。アーキテクチャやアプリの大幅な改修は行わず、既存の構成を可能な限り維持する「リフトアンドシフト」方式を採用しました。

課題

以下のように課題を3つに整理しました。

  • アプリケーション構成の把握とFargate対応:既存Herokuアプリの依存関係・設定・稼働条件を洗い出し、Fargate上で動作するようにアーキテクチャを作成する必要があります。
  • デプロイパイプラインの構築:GitHub Actionsを活用し、ビルド~デプロイまで自動化されたパイプラインを構築する必要があります。
  • コンテナイメージのビルド:Dockerfile作成やビルド手順の整備といった作業が必要となります。

アプリケーション構成の把握とFargate対応

Herokuが提供する抽象化された環境に強く依存していたため、アーキテクチャをシンプルに構成しました。システム全体の設計として、データベース(RDS)へのアクセスはコンテナ経由に限定されており、アプリケーション本体やマイグレーションもすべてコンテナを通して行う設計となっています。そのため、初期構築時やアップデート時には、専用のDBマイグレーション用コンテナを都度立ち上げて処理を実行する必要がありました。

デプロイパイプラインの構築

今回は、Herokuからの環境移行(リフトアンドシフト)にあたり、新たにデプロイパイプラインを構築する必要がありました。移行後の環境では、Gitの操作をトリガーにデプロイが自動で行われる仕組みを構築したいと考え、GitHub Actions を採用しました。パイプライン全体の流れは以下の通りです。

  • 対象ブランチへのマージをトリガー
  • GitHub Actions が起動
  • Docker イメージをビルド
  • Amazon ECR(Elastic Container Registry)にイメージをプッシュ
  • AWS Fargate 上のタスクを更新してアプリケーションをデプロイ

コンテナイメージのビルド

もともとHeroku上で動作していたアプリケーションのため、クラウド環境(Fargateなど)での運用を前提としたDockerfileは存在していませんでした。今回のリフト&シフトにあたり、環境をコンテナベースに移行する必要がありましたが、アプリ本体とフロントエンドビルドを分離する構成となっていたため、2段階ビルド(multi-stage build)を採用しました。

  • 最初のステージでは、Node.js 環境でフロントエンドの依存解決およびビルド(yarn build)を実施。
  • 次のステージでは、Ruby環境でRailsの依存をインストールし、先ほどのビルド成果物を取り込んで本番環境用のイメージを作成。

この構成により、不要なビルドツールや開発用の依存を含まない、軽量かつ安全な本番イメージを作成することができました。最終的に、このイメージを用いてFargate上での実行にも成功しました。

ボトルネックとなった箇所

ボトルネックとなった箇所はコンテナイメージの2段階ビルドの設計です。クラウド環境(Fargate)での運用を前提とした Dockerfile が存在していなかったため、Dockerベースの構成を一から設計し直す必要がありました。特に時間を要したのは、アプリ本体とフロントエンドのビルド工程が分かれていたことです。これにより単純なイメージビルドでは対応できず、2段階ビルドの導入が必須となりました。環境の切り分けやアセットの受け渡し、依存の関係の整理など、調整作業に多くの時間を要しましたが、最終的には本番イメージの作成に成功し、Fargate 上で安定して動作させることができました。

成果

今回のリフトアンドシフトにより、Herokuに依存した運用から脱却し、AWS上での柔軟かつ拡張性のある運用基盤へと移行することができました。

  • Herokuのビルドパック依存から脱却し、Dockerベースの標準化された環境構築に成功
  • GitHub Actions による自動デプロイパイプラインを整備し、CI/CD の効率化を実現
  • 2段階ビルドによる軽量で本番向けのコンテナイメージ作成により、セキュリティと保守性を向上
  • Fargate + RDS という構成により、サーバーレスかつスケーラブルな実行基盤を構築

アプリケーション自体はモノリシックな構成のままですが、インフラと運用面をモダナイズすることで、今後のスケールやマイクロサービス化にも対応しやすい土台を整えることができました。