AWS LambdaからECS Fargateへの移行

こんにちは、久保田(@kubotak_public)です

今回は弊社で運用しているサービスであるM&Aクラウドのフロントエンドの実行環境をAWS LambdaからECS Fargateへ移行した話です。 まずは弊社のサービスが動いている環境は次のようになっていました。

f:id:kubotak:20210609141326p:plain

フロントエンドにNuxt.js(JavaScript)、バックエンドにLaravel(PHP)を利用しています。 LaravelはAWS ElasticBeanstalkで作成されたEC2インスタンス上で動作しています。

そしてフロントエンドのNuxt.jsはAWS Lambda上で動作し、API Gatewayを利用してHTTPによりアクセスできるようになっていました。 もともとはLaravelの環境だけでしたが、Nuxt.jsでフロントエンドをリプレースしたページが混在しているのが現状です。 これらはCloudFrontによって各環境にリクエストがルーティングされています。

Nuxt.jsへの移行に関しては別途シリーズでお届けしていますのでよろしければご参照ください。

tech.macloud.jp

なぜ移行するのか

このNuxt.jsの実行環境であるAWS Lambdaでは次のようなメリットがありました。

  • 運用が楽
  • 費用が安い

弊社の運用においてはLambdaのコールドスタートに関しても特に問題ではありませんでした。 一年程はLambdaによる運用にも特に問題にはなっていませんでした。

しかし、Nuxt.jsのアプリの肥大化により状況が変わってしまいました。 AWS Lambdaではデプロイできるサイズがzipの状態で50MB、展開した状態で250MBの制限があり、Nuxt.jsとTypeScriptのバージョンアップを行った際にこの制限に引っかかる様になってしまいました。

そこで、容量の制限に余裕がある環境への移行が求められ、Amazon ECSのFargateが候補にあがりました。

Amazon ECSとは

Amazon Elastic Container Service(Amazon ECS)は完全マネージド型コンテナオーケストレーションサービスであり、コンテナ化されたアプリケーションを簡単にデプロイ、管理、スケールするのに役立ちます。

AWS Fargateとは

FargateはECSやEKS上で動作するコンテナ向けサーバーレスコンピューティングエンジンです。 Amazon EC2 インスタンスでサーバーまたはクラスターを管理する必要なくコンテナを実行することができます。

サーバー管理にコストを掛けずに運用したいという考えからAWS Lambdaを利用していましたので、同じようにサーバー管理をマネージドしてくれるECS Fargateを採用しました。

AWS LambdaへのデプロイからAWS Fargateへのデプロイに切り替え

まずはFargateで動作させるためにDockerコンテナ化と、そのコンテナをホスティングする必要があります。 Dockerfileは非常にシンプルです。

FROM node:12.14.0-alpine

RUN mkdir -p /var/www/workspace
WORKDIR /var/www/workspace
COPY ./.nuxt /var/www/workspace/.nuxt
COPY ./dist /var/www/workspace/dist
COPY ./node_modules /var/www/workspace/node_modules
COPY ./.env /var/www/workspace/.env

EXPOSE 80

ENTRYPOINT ["node", "dist/server/server.js"]

※一部改変しています。

ベースとなるnode.jsのDockerイメージを利用してビルドしたアプリケーションがコンテナ内に配置されるだけです。

Dockerイメージのpush

DockerイメージはAmazon Elastic Container Registry(Amazon ECR)にホスティングします。 弊社ではアプリケーションのデプロイのCI/CDとしてCircleCIを利用しています。 CircleCIでECRへpush(Dockerイメージをホスティング)するには次のプラグインを利用しています。

version: 2.1
orbs:
  aws-ecr: circleci/aws-ecr@7.0.0

定義は次のようになります。

- aws-ecr/build-and-push-image:
          account-url: AWS_ECR_ACCOUNT_URL
          aws-access-key-id: AWS_ACCESS_KEY_ID
          aws-secret-access-key: AWS_SECRET_ACCESS_KEY
          create-repo: true
          region: AWS_REGION
          repo: repository-name
          skip-when-tags-exist: true
          tag: "${CIRCLE_SHA1}"

これでDockerfileを利用してDockerイメージの作成とECRへのpushをCI上で行ってくれます。

Fargateへのデプロイ

AWS LambdaへのデプロイはServerless Frameworkを利用していたので、同じ仕組みでFargeteへのデプロイを行いました。

Serverless Pluginを追加してFargateへのデプロイを追加します。 検索すると次のプラグインが上位に出ます。

GitHub - honerlaw/serverless-fargate-plugin: Serverless plugin to deploy fargate tasks to an ECS cluster.

しかし、こちらのプラグインは保守がされてなく、弊社の環境ではデプロイも失敗しました。 そこでこのプラグインをForkしているものを使っています。

github.com

※一部subnetに関する拡張を行いたかったのでコントリビュートしました
※このプラグインの作者が別途新規でプラグインを作成中ですのでそちらもチェックされると良いかもしれません。

デプロイして新環境で動いていることが確認できたらAWS Lambdaで動いている環境から切り替えます。 この切り替えは前段であるCloudFrontからの向き先を変更するだけです。

f:id:kubotak:20210609141837p:plain

移行してみて

AWS Lambdaと異なり、Fargateでは稼働している台数のハンドリングや利用しているCPUやメモリのリソースの把握が重要になってくるかと思います。 しかし、Fargateのオートスケールの設定などはわかりやすく、スケーリング自体もスムーズな印象です。 API Gatewayが不要になり、代わりにロードバランサーが追加された点などもあり、アーキテクチャとしては複雑になったという印象があります。 デプロイ時間に関してはECRへのイメージpushやECSがローリングデプロイをする関係上遅くはなってしまいましたが、現時点では運用して間もないので過不足は特に感じていません。 引き続き監視を行い、アプリケーションの成長に合わせてインフラもスケールするように改善していきたいと思います。

最後に

弊社のフロントエンドエンジニアは今回のようにAWS環境を利用した改善・保守も行います。
アプリケーションコードにとどまらず、インフラ環境も扱いたいフロントエンドエンジニアの方は一緒にアプリケーションを成長させていきましょう。

www.wantedly.com