✂️

ECS(Next.js)の本番リリース時間を半分以下にしてみた

2024/02/28に公開

前談

本番リリースの待ち時間ってドキドキして、他の作業も手につきませんよね?
そんなモヤモヤタイムを減らすべく今回Next.jsアプリケーションの本番リリース時間を半分以下に出来たので何が効果的だったのかの共有記事になります。

概要

Next.jsの本番リリースフローを見直して、本番リリース時間の短縮を目指しました。
今回の記事では、細かい実装内容については省略している部分がありますので、どんなことをやれば時間短縮につながるのか?といった部分の確認にいただければと思います。

基礎技術

  • Amazon ECS
  • Amazon ECR
  • Nextjs
  • Yarn

修正を加えた技術

  • AWS CodePipeline
    • GitHub Actionsへ移行
  • AWS CodeBuild
    • GitHub Actionsへ移行

成果

  • 本番へのリリースフロー全体の待ち時間
    • 40分→18分に短縮

内訳

  • ステージング環境から本番への待ち時間
    • 20分→5分に短縮
  • 開発環境からステージング環境への待ち時間
    • 20分→13分に短縮

おまけ

  • 月額料金10$程度の削減
    • Github Actionsの無料枠が余っていたためCodePipeline/CodeBuildの料金削減になりました。

対応内容

効果のあった内容から順に記載しております。

Dockerイメージの軽量化

Dockerイメージの軽量化を行いました。
これは、以下3箇所に影響する認識で、効果が大きいです。

  • Dockerイメージの書き出し時間
  • ECRへのPush時間
  • ECRからのPull時間

今回対応した内容は、Yarnのcache cleanだけで、約2GBのイメージ削減で、0.8GB程度になりました。
(元々が大きすぎましたね。。。。。)

RUN \
  yarn install --frozen-lockfile --no-progress &&\
  yarn cache clean # これの追加

また、.dockerignoreで不要なファイルをDockerイメージに含ませないことでの軽量化も効果があります。

node_modules
.git
.vscode
.next
.github
yarn
Dockerfile
.dockerignore

上記以外にも将来的には、Next.jsのstandaloneの設定を行い、マルチステージビルドなどを取り入れて、更なる削減を行いたいと考えております。
https://nextjs.org/docs/pages/api-reference/next-config-js/output#automatically-copying-traced-files

Github Actionsを利用したDockerレイヤーキャッシュ

Dockerレイヤーキャッシュを利用することで、変更点のない部分のビルド時間の短縮に成功しました。
Github ActionsでDockerレイヤーキャッシュを設定するには以下記事を参考にさせていただきました。
https://dev.classmethod.jp/articles/github-actions-docker-layer-cache-enable/

デプロイ環境をCodePipeline/CodeBuildからGithub Actionsへ移行しました。
CodePipeline/CodeBuildでもDockerレイヤーキャッシュの設定は可能ですが、ビルドの頻度が低い場合キャッシュが効かなくなる様でして、全然キャッシュがヒットしませんでした。

対して、Github Actionsに関しては、7日間キャッシュを保管してくれるので基本的にキャッシュが効くようになりました。

また、GithubActions上のキャッシュ保存期間内でも、Dockerfileの書き方がDockerレイヤーキャッシュが効かない状態であれば、キャッシュは効かないためDockerレイヤーキャッシュを効きやすくするには、Dockerfileのベストプラクティスに則る必要があるので以下をご参考にください。
https://docs.docker.jp/develop/develop-images/dockerfile_best-practices.html

私自身がハマったポイントも記事にしておりますのでこちらもご参考にいただければと思います。
https://zenn.dev/spacemarket/articles/6ff335bf3db3d7

本番Dockerイメージの事前ビルド

本番のDockerイメージを事前にビルドする仕組みづくりをしました。
概要としては、以下の図になりますが、ステージング環境にデプロイを行う時に並列で本番のDockerイメージのビルドを行っております。

仕組みとしては、本番リリース用PRが作成/更新されたタイミングでビルドを実行し、
本番リリース用PRがマージされたタイミングでビルド済みのDockerイメージをECSへデプロイします。
この時、ビルドとデプロイのGithubActionsが分断されるため、Dockerイメージタグをどうやって指定するか試行錯誤しましたが、PR番号を指定することで橋渡しに成功しました。

ビルド

    - name: Login to Amazon ECR
      id: login-ecr
      uses: aws-actions/amazon-ecr-login@v2
    - name: Docker meta
      id: meta
      uses: docker/metadata-action@v5
      with:
        images: |
          ${{ steps.login-ecr.outputs.registry }}/リポジトリ名
        tags: |
          ${{ github.event.pull_request.number }}

    - name: Build and push
      uses: docker/build-push-action@v5
      with:
        context: .
        tags: ${{ steps.meta.outputs.tags }}
     push: true

デプロイ

    - name: Docker meta
      id: meta
      uses: docker/metadata-action@v5
      with:
        images: |
          ${{ steps.login-ecr.outputs.registry }}/リポジトリ名
        tags: |
          ${{ github.event.pull_request.number }}
    - name: Fill in the new image ID in the Amazon ECS task definition
      id: task-def
      uses: aws-actions/amazon-ecs-render-task-definition@v1
      with:
        task-definition: task-definition.json
        container-name: コンテナ名
        image: ${{ steps.meta.outputs.tags }}

    - name: Deploy Amazon ECS task definition
      uses: aws-actions/amazon-ecs-deploy-task-definition@v1
      with:
        cluster: クラスタ名
        service: サービス名
        task-definition: ${{ steps.task-def.outputs.task-definition }}
        wait-for-service-stability: true

ECSデプロイ時間の短縮

どれだけビルド時間を短縮しても、ECSへのデプロイ時間が10分とか15分とか結構かかったりします。
主な要因としては、ALBからの切り離しの部分だと思われます。
この部分は、以下記事で綺麗にまとめられていますので、それぞれチューニングいただければと思います。

ただ、注意いただきたいポイントとしましては、コネクションドレインやminimumHealthyPercentの値は、下手をするとデプロイの度にサービスの安定稼働に影響が出る部分ですので、本番設定に関しましては、慎重に行っていただく必要があると思われます。
開発環境であれば、比較的容易に設定を変更し時間短縮に繋がると思われます。

https://toris.io/2021/04/speeding-up-amazon-ecs-container-deployments/
https://dev.classmethod.jp/articles/tsnote-i-want-to-shorten-the-deployment-time-for-rolling-updates-of-ecs-is-there-any-way-to-shorten-the-time/

多分効果が無いSOCI

実践したわけではありませんが、恐らくリリース時間短縮には繋がらないと思うので、リリース時間短縮という目的であれば優先度は低いと思われます。
また、SOCIインデックスの作成時間もかかるため、リリース時間だけに着目した場合は悪影響を及ぼす可能性があります。

ただ、オートスケーリング時のスケールアウト時間短縮には繋がるはずなので、スパイクアクセスが多いサービスの場合などは導入の効果は高いと思われます。
https://aws.amazon.com/jp/blogs/aws/aws-fargate-enables-faster-container-startup-using-seekable-oci/

https://pages.awscloud.com/rs/112-TZM-766/images/20230928_34th_ISV_DiveDeepSeminar_sobukos_Container.pdf

感想

今回、リリース時間を短縮するといった目的で、あれこれ記事を読み漁り、あれこれ試してみて良い経験ができました。
私が調べた中で、ポイント、ポイントで記事はあるのですが、何が効果あって何が効果なかったのかと言った記事が少ないと思いますので、この記事が参考になれば幸いです。

宣伝

スペースマーケットエンジニア募集しております!
こういったDX改善もどんどんできる環境ですので、ぜひご興味ある方は是非以下をご覧ください。

https://www.wantedly.com/projects/1113570

弊社は人、文化が良いなと個人的には感じておりますのでこちらも併せて読んでいただけますと嬉しいです。
https://spacemarket.co.jp/recruit/engineer/

スペースマーケット Engineer Blog

Discussion