🚀

ECSへのデプロイをGitHub Actionsで完結させるか、CodeDeployを挟むべきか比較してみた。

2024/03/11に公開

概要

こんにちは🙌
今回は、GitHub Actionsを使って、ECSにデプロイする2つの方法を比較しながら試していきたいと思います。

  1. GitHub Actionsのみでのデプロイ
  2. GitHubActions + CodeDeployを使ったデプロイ

ゴール

それぞれの方法によって、どんなメリット・デメリットが生まれるのかを理解すること としたいと思います。

TL;DR

ダウンタイムが許容される一括デプロイ(インプレースデプロイ)で良いならGitHub Actionsで完結
ダウンタイムが許容されない、デプロイをより厳密に制御したい場合にはGitHub Actions + CodeDeployを使う

GitHub Actions概要

GitHub Actionsは、ビルド、テスト、デプロイメントの自動化を実現するCI/CDプラットフォームです。
利用者は、リポジトリ内で直接ワークフローを定義し、プルリクエストやイシュー作成などのイベントに応じて自動的にジョブを実行できます。
ワークフローはYAMLファイルで定義され、Linux、Windows、macOSの仮想マシンをランナーとして選択することが出来ます。
プランに関しては、GitHub ActionsはGitHubのすべての現行ユーザーごとのプランで利用可能であり、公開リポジトリでは無料で使用できます。しかし、プライベートリポジトリではプランに応じて無料で利用できる分数、ストレージ、データ転送量に制限があります。
とはいえ、Freeプランであっても月あたり2,000分(2023/3時点)まで実行出来ますので、非常に使いやすいですね。

デプロイ方式についておさらい

アプリケーションのデプロイ戦略についてもおさえておきます。ここでは、AWSのホワイトペーパーAWS での継続的インテグレーションと継続的デリバリーの実践を参考にしています。

デプロイ時の考慮事項

デプロイ方法に入る前にアプリケーションに求められるアップデート時の考慮事項を整理する必要があります。

  • アプリケーションのダウンタイムを許容すること出来るか
  • デプロイの所要時間
  • パフォーマンスの問題
  • デプロイに失敗した場合の影響と効果的な処理方法
  • ヒューマンエラーのリスクを軽減するための仕組み

といったポイントをあげることができるかと思います。
これらは各組織やアプリケーションによって求められる基準が異なってくるものであるかと思います。デプロイ方式を決める前に決めておく必要があります。

デプロイ方式

方法 所要時間 ダウンタイム ロールバックプロセス
一括デプロイ(インプレースデプロイ) あり 再デプロイ
ローリングデプロイ なし 更新箇所を戻す
ブルー/グリーンデプロイ なし 環境切り替え
  • 一括デプロイ(インプレースデプロイ)
    既存のアプリケーションコードを1つのデプロイアクションで全て置き換える方式となります。そのためダウンタイムが必要となります。
    ロールバックプロセスは古いコードを再デプロイをする方法です。
  • ローリングデプロイ
    既存環境の一部に新しいアプリケーションコードをデプロイします。このタイミングで本番環境でどのように動作をするのか少数のサーバで確認することが出来ます。問題があった場合には、更新された箇所を古いバージョンに戻すことでロールバックすることが出来ます。
  • ブルー/グリーンデプロイ
    新しいバージョンのアプリケーションを使用した別の環境を作成します。そこでテスト等を行い、問題ないことが確認をした後にトラフィックを移行していくことになります。ロールバックは古い環境(ブルー)にトラフィックが戻るように切り替えてあげることになります。
    この構成は一時的に環境が2つ存在することになるため、その分コストも掛かる構成となります。

デプロイ方式を振り返ったところで、今回2つのデプロイ方法を見ていきたいと思います。

GitHub Actionsでのデプロイ

まずは、GitHub Actionsを使ったデプロイ方式から確認していきます。
GitHub Docs Amazon Elastic Container Serviceへのデプロイを参考にしています。

実際にサンプルコードを見てみます。(デプロイに関係ない部分は省略しています。)

name: Deploy to Amazon ECS

on:
  push:
    branches:
      - main

env:
  AWS_REGION: MY_AWS_REGION                   # set this to your preferred AWS region, e.g. us-west-1
  ECR_REPOSITORY: MY_ECR_REPOSITORY           # set this to your Amazon ECR repository name
  ECS_SERVICE: MY_ECS_SERVICE                 # set this to your Amazon ECS service name
  ECS_CLUSTER: MY_ECS_CLUSTER                 # set this to your Amazon ECS cluster name
  ECS_TASK_DEFINITION: MY_ECS_TASK_DEFINITION # set this to the path to your Amazon ECS task definition
                                               # file, e.g. .aws/task-definition.json
  CONTAINER_NAME: MY_CONTAINER_NAME           # set this to the name of the container in the
                                               # containerDefinitions section of your task definition

jobs:
  deploy:
    name: Deploy
    runs-on: ubuntu-latest
    environment: production
~~~~~~~~~~
      - name: Fill in the new image ID in the Amazon ECS task definition
        id: task-def
        uses: aws-actions/amazon-ecs-render-task-definition@c804dfbdd57f713b6c079302a4c01db7017a36fc
        with:
          task-definition: ${{ env.ECS_TASK_DEFINITION }}
          container-name: ${{ env.CONTAINER_NAME }}
          image: ${{ steps.build-image.outputs.image }}

      - name: Deploy Amazon ECS task definition
        uses: aws-actions/amazon-ecs-deploy-task-definition@df9643053eda01f169e64a0e60233aacca83799a
        with:
          task-definition: ${{ steps.task-def.outputs.task-definition }}
          service: ${{ env.ECS_SERVICE }}
          cluster: ${{ env.ECS_CLUSTER }}
          wait-for-service-stability: true

(AWSの認証情報設定、ECRへのログイン、ビルドとプッシュは省略しています。)
このサンプルコードでは、aws-actions/amazon-ecs-render-task-definitionを使ってタスク定義の更新、aws-actions/amazon-ecs-deploy-task-definitionで更新したタスク定義を使ってECSサービスをデプロイしています。
先ほどまでのデプロイ方式で言うと 一括デプロイ(インプレースデプロイ) となります。

ただ、ECSでサービス設定をすれば、ローリングアップデートとすることは可能です。(最小ヘルス率、最大率を設定します。)

  • メリット
    • GitHub Actionsワークフローのみで管理することが出来る
    • 実行上限時間を超えない、ランナーを選ばなければお金が掛からない
  • デメリット
    • 一括デプロイ(インプレースデプロイ)以外のデプロイ方式は困難
    • ロールバックのためにワークフローの再実行が必要となる。

GitHub Actions + CodeDeployでのデプロイ

続いて、GitHubActions + CodeDeployを使ったデプロイ方法を見ていきます。
先ほどまでと同様にaws-actions/amazon-ecs-deploy-task-definitionを使いますが、GitHub Actionsワークフローファイルの中でデプロイの部分が変わってきます。
AWS CodeDeploy Support

    - name: Deploy to Amazon ECS
      uses: aws-actions/amazon-ecs-deploy-task-definition@v1
      with:
        task-definition: task-definition.json
        service: my-service
        cluster: my-cluster
        wait-for-service-stability: true
        codedeploy-appspec: appspec.json
        codedeploy-application: my-codedeploy-application
        codedeploy-deployment-group: my-codedeploy-deployment-group

CodeDeployは既に利用環境で作られていることが前提となりますが、デプロイに利用するためのCodeDeployアプリケーションを使うように書かれていることが見て取れます。ここで使われるファイルの例についてもいくつか見ていきます。
AppSpec ファイルの例
サービスをデプロイするための情報が書かれていることが分かります。また、hooksセクションを利用することによってデプロイメントライフサイクルの中でLambdaを起動させることも可能です。

AppSpecファイルの例
version: 0.0
Resources:
  - TargetService:
      Type: AWS::ECS::Service
      Properties:
        TaskDefinition: "arn:aws:ecs:us-east-1:111222333444:task-definition/my-task-definition-family-name:1"
        LoadBalancerInfo:
          ContainerName: "SampleApplicationName"
          ContainerPort: 80
# Optional properties
        PlatformVersion: "LATEST"
        NetworkConfiguration:
          AwsvpcConfiguration:
            Subnets: ["subnet-1234abcd","subnet-5678abcd"]
            SecurityGroups: ["sg-12345678"]
            AssignPublicIp: "ENABLED"
        CapacityProviderStrategy:
          - Base: 1
            CapacityProvider: "FARGATE_SPOT"
            Weight: 2
          - Base: 0
            CapacityProvider: "FARGATE"
            Weight: 1
Hooks:
  - BeforeInstall: "LambdaFunctionToValidateBeforeInstall"
  - AfterInstall: "LambdaFunctionToValidateAfterInstall"
  - AfterAllowTestTraffic: "LambdaFunctionToValidateAfterTestTrafficStarts"
  - BeforeAllowTraffic: "LambdaFunctionToValidateBeforeAllowingProductionTraffic"
  - AfterAllowTraffic: "LambdaFunctionToValidateAfterAllowingProductionTraffic"

Amazon ECS デプロイ用のデプロイグループを作成する
デプロイグループとは、アプリケーションを含むECSサービスやロードランサー設定等を定義したファイルとなります。このファイルによってデプロイ方式の細かな制御を可能とすることが出来ます。
イメージとしては、同一クラスタにサービスがもう一つ出来上がります。ロードバランサは本稼働リスナーとテスト稼働リスナーを持ち、開発者はテスト稼働リスナーから新規アプリケーションの動作を試すことが出来ます。
そこで問題がなければ、ロードバランサを更新して新しいアプリケーションが本稼働することとなります。

  • メリット
    • 一括デプロイ(インプレースアップデート)、ローリングアップデート、ブルーグリーンデプロイメントまでデプロイ方式に対応することが出来る。
    • ロールバックが簡単&自動
      デプロイに失敗した場合に、自動的にロールバックを行うように設定することが出来ます。それだけであれば、ECS側でも制御ができますが、閾値を設けて、そこに達した場合にロールバックといった設定も可能です。
  • デメリット

おわりに

ECSへのCI/CDを考えるにあたっての選択肢としての2つの方法を比較してみました。

  1. GitHub Actionsのみでのデプロイ
  2. GitHubActions + CodeDeployを使ったデプロイ

どちらを使ってもデプロイをすることは可能ではありますが、各アプリケーションでのリリースにおける要件を考慮した上で、選択すべきデプロイ方式を決めていく必要があるようでした。
ありがとうございました🙌

Discussion