Amazon Web Services ブログ

AWS App Runner でセマンティックバージョニングに基づいた継続的デプロイメントの実現

イントロダクション

今日のモダンなクラウド時代では、アプリケーションは、一日に何回も自動的にビルドされ、テストされ、デプロイされます。このソフトウェア開発のサイクルにおける一般的なシナリオは、機能やバグフィックスやその他アップデートをエンドユーザに対して素早く提供します。継続的なデプロイメントの重要な側面の一つにセマンティックバージョニングがあります。これは、ソフトウェアのリリースにバージョンナンバーを割り当てる仕組みです。セマンティックバージョニングでは、リリースにおける変更度合いを伝える標準的なフォーマットを利用します。それにより、開発者やユーザはアップデートの潜在的なインパクトを理解することができます。セマンティックバージョニングなしでは、次のバージョンへの移行を妨げる破壊的変更を追跡するのは困難です。

このブログでは、AWS App Runner が提供する継続的インテグレーション (CI) / 継続的デプロイメント (CD) 機能とセマンティックバージョニングを組み合わせて使用し、新しいバージョンのアプリケーションを自動的にデプロイする方法を説明します。

セマンティックバージョニング

セマンティックバージョニングは、ソフトウェアリリースにバージョン番号を付与する方法です。これは、変更度合いを表す指標であり、開発者やユーザはアップデートの潜在的なインパクトを理解することができます。セマンティックバージョンナンバーの基本的なフォーマットは、MAJOR.MiNOR.PATCHで、それぞれの数字は正の整数です。

ここでは、セマンティックバージョニングの一般的なルールを記載します。

  • 当該リリースが後方互換を含まない場合 (API コンストラクトの破棄など)、MAJOR バージョンをインクリメントします。
  • 当該リリースが後方互換を含む場合、MINOR バージョンをインクリメントします。
  • 当該リリースがバグフィックスのみ含まれる場合、PATCH バージョンをインクリメントします。

セマンティックバージョニングを用いることで、開発者はユーザにリリースのインパクトを伝えることができます。ユーザは新しいバージョンを利用する際のリスクやメリットを容易に理解することができます。

問題提起

AWS App Runner は、ソースコードリポジトリからコンテナ化されたアプリケーションを迅速かつ簡単にデプロイできるフルマネージドなコンテナサービスです。AWS App Runner は、コンテナ化されたアプリケーションの構築、実行、スケーリングするための完全に制御された環境を提供します。また、新しいバージョンのアプリケーションを自動的に構築、デプロイするためのフルマネージドな CI/CD パイプラインも提供します。AWS App Runnerを活用することで、Amazon Elastic Container Registry ( Amazon ECR ) リポジトリにプッシュされる特定のタグ ( 例:LATEST ) が付いたイメージを継続的に監視し、AWS App Runner にアプリケーションを自動的にデプロイできます。

しかしこのアプローチでは、新しいバージョンのアプリケーションの監視やデプロイが、セマンティックバージョニングに基づいてできません。顧客が、>= MAJOR1.MINOR2.PATCH3 のようなパターンに基づいて、新しいバージョンのアプリケーションを AWS App Runner に対して自動的にデプロイしたい場合、既存のAWS App Runner の機能では難しいです。

ソリューションの概要

このソリューションでは、以下の AWS サービスを利用します。

  • AWS App Runner – ソースコードリポジトリからコンテナ化されたアプリケーションを素早くデプロイすることが容易な、フルマネージドなコンテナアプリケーションサービス
  • AWS Lambda – サーバを構築/運用することなくコードを実行することが可能なサーバレスコンピュートサービス
  • Amazon Elastic Container Registry (Amazon ECR) – 開発者が容易に Docker コンテナイメージを保存、管理、デプロイできるフルマネージドな Docker コンテナレジストリ
  • Amazon EventBridge – 自社のアプリケーションや Software-as-a-Service (SaaS) アプリケーション、AWSサービスのデータをアプリケーション間で容易にやり取りするフルマネージドなイベントバス
  • Amazon Simple Storage Service (Amazon S3) – 業界をリードするスケーラビリティやデータの可用性、セキュリティ、パフォーマンスを提供するフルマネージドなオブジェクトストレージサービス
  • Amazon Simple Queue Service (Amazon SQS) – マイクロサービスや分散システム、サーバレスアプリケーションのためのフルマネージドなメッセージキュー

以下の図に、ソリューションの全体像を示します。

このソリューションは、Amazon ECR の PUSH イベントをキャッチするために EventBridge ルールを用います。図に示す通り、PUSH イベントをキャッチすると、Amazon SQS キューを経由して AWS Lambda 関数によって処理されます。AWS Lambda 関数は AWS App Runner (Application Programming interface) の API を利用して、現在デプロイされているアプリケーションのイメージタグを取得し、Amazon ECR リポジトリにプッシュされたイメージのタグと比較します。これらがマッチした場合、AWS Lambda 関数は 新しいバージョンのアプリケーションをデプロイするために、AWS App Runner サービスをアップデートします。ユーザは、Amazon S3 バケット内にJSONファイル(以下、サンプル)の形式で格納することで、AWS Lambda 関数のインプットパラメータとしてマッチパターンを提供できます。

[
   {
     "repository": "hello-World-AppRunner-Repo",
      "semVersion": ">1.2.3",
      "serviceArn": "arn:aws:apprunner:us-east-1:123456789123:service/Hello-World-Service/2d0032a93cbb4cbdaef0966607052336"
   }
]

当該ソリューションは、Node Package Manager(NPM) 形式のバージョニングチェックをサポートします。以下に、サポートするマッチパターンの例を記載します。

  • >1.2.3 – 1.2.3 以上の どのバージョンにもマッチ
  • 1.1 || 1.2.3 – 2.0.0 – バージョン1.1.1 または 1.2.3 から2.0.0の どのバージョンにもマッチ
  • 1.* – 1.1以上の どのバージョンにもマッチ
  • ~1.2.1 – 1.2.1 以上 1.3.0 未満の どのバージョンにもマッチ
  • ^1.2.1 – 1.2.1 以上 2.0.0未満 の どのバージョンにもマッチ

以下の環境変数を AWS Lambda 関数に設定する必要があります。

  • QUEUE_NAME – Amazon ECR の PUSH イベントを受け取り、AWS Lambda 関数をトリガーする Amazon SQS キュー名
  • CONFIG_BUCKET – マッチパターンが記載された JSON ファイルを含む Amazon S3 bucket 名
  • CONFIG_FILE – マッチパターンを含む JSON ファイル名 ( config フォルダの下にサンプルが提供されています )

以下のシーケンス図は、新しいバージョンのアプリケーションが Amazon ECR リポジトリにプッシュされた際に、ソリューションで利用するコンポーネント同士のやり取りを示しています。

当該ソリューションは、リトライロジックが備わっています。同一のAmazon ECR リポジトリへ複数回 PUSH イベントがされた場合、ターゲットの AWS App Runner サービスがアップデート進行中であれば、AWS Lambda 関数は待機します。AWS Lambda 関数は、10分間に最大3回リトライします。

前提条件

当該ソリューションを導入するために、以下の前提条件を満たす必要があります。

ウォークスルー

Amazon ECR リポジトリと AWS App Runner サービスのセットアップ

AWS App Runner のドキュメントのサンプルアプリケーションを使って、ソリューションのデモンストレーションをしましょう。

環境変数にAWS アカウント番号をセットします。

AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text)
AWS_REGION=us-east-1 # Change this to region of your choice

もし、リージョンが設定されていない場合、aws configure を用いて設定してください。

  1. GitHub からサンプルアプリケーションをチェックアウトしてください。
git clone https://github.com/aws-containers/hello-app-runner.git
cd hello-app-runner
  1. 新しい Amazon ECR リポジトリを作成します。
aws ecr create-repository --repository-name hello-world-apprunner-repo

リポジトリを作成すると、下記のようなテキストが出力されます。

{
    "repository": {
        "repositoryArn": "arn:aws:ecr:us-east-1:<<account>>:repository/hello-world-apprunner-repo",
        "registryId": "<<account>>",
        "repositoryName": "hello-world-apprunner-repo",
        "repositoryUri": "<<account>>.dkr.ecr.us-east-1.amazonaws.com/hello-world-apprunner-repo",
        "createdAt": "2023-01-02T15:20:14-08:00",
        "imageTagMutability": "MUTABLE",
        "imageScanningConfiguration": {
            "scanOnPush": false
        },
        "encryptionConfiguration": {
            "encryptionType": "AES256"
        }
    }
}
  1. Amazon ECR リポジトリにログインします。
aws ecr get-login-password --region $AWS_REGION | \
  docker login --username AWS --password-stdin \
  $AWS_ACCOUNT_ID.dkr.ecr.${AWS_REGION}.amazonaws.com
  1. コンテナイメージをビルドし、Amazon ECR リポジトリにプッシュします。
docker build --platform linux/amd64 -t hello-world-apprunner-repo .
docker tag hello-world-apprunner-repo:latest ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/hello-world-apprunner-repo:1.2.3
docker push ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/hello-world-apprunner-repo:1.2.3

注:ハードウェアやネットワーク構成によっては、Docker build が完了まで数分かかる可能性があります。

  1. AWS App Runner へのアクセスロールを作成し、Amazon ECR アクセスポリシーをロールにアタッチします。
export TP_FILE=$(mktemp)
export ROLE_NAME=AppRunnerSemVarAccessRole
cat <<EOF | tee $TP_FILE
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "build.apprunner.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF
aws iam create-role --role-name $ROLE_NAME --assume-role-policy-document file://$TP_FILE
rm $TP_FILE
aws iam attach-role-policy --role-name $ROLE_NAME --policy-arn arn:aws:iam::aws:policy/service-role/AWSAppRunnerServicePolicyForECRAccess
  1. 先ほどの手順で作成した Amazon ECR リポジトリを使用して、新しい AWS App Runner サービスを作成します。
    1. AWS App Runner サービスの構成ファイルを作成します。
cat > input.json << EOF
{
    "ServiceName": "hello-world-service",
    "SourceConfiguration": {
        "AuthenticationConfiguration": {
            "AccessRoleArn": "arn:aws:iam::${AWS_ACCOUNT_ID}:role/${ROLE_NAME}"
        },
        "ImageRepository": {
            "ImageIdentifier": "${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/hello-world-apprunner-repo:1.2.3",
            "ImageConfiguration": {
                "Port": "8000"
            },
            "ImageRepositoryType": "ECR"
        }
    },
    "InstanceConfiguration": {
        "Cpu": "1 vCPU",
        "Memory": "3 GB"
    }
}
EOF
SERVICE_ARN=$(aws apprunner create-service --cli-input-json file://input.json \
 --output text \
 --query 'Service.ServiceArn')
 
SERVICE_ID=$(aws apprunner describe-service --service-arn ${SERVICE_ARN} | jq -r '.Service.ServiceId') 
SERVICE_URL=$(aws apprunner describe-service --service-arn ${SERVICE_ARN} | jq -r '.Service.ServiceUrl') 

echo $SERVICE_URL

## Run describe service command to check the status
aws apprunner describe-service --service-arn \
arn:aws:apprunner:${AWS_REGION}:${AWS_ACCOUNT_ID}:service/hello-world-service/${SERVICE_ID} | jq -r '.Service.Status'
  1. AWS App Runner サービスが作成された後、$SERVICE_URL にブラウザでアクセスしてください。サンプルアプリケーションが表示されます。以下は、そのスクリーンショットです。

ソリューションのデプロイ

  1. GitHub からソリューションをチェックアウトします。
git clone https://github.com/aws-samples/containers-blog-maelstrom.git
cd containers-blog-maelstrom/sem-var-ecr-watcher-app-runner
  1. config/ ディレクトリに ServiceArn を含む 以下のような config ファイルを作成します。
cat > config/config.json<< EOF
[
    {
      "repository": "hello-world-apprunner-repo",
      "semVersion": ">1.2.2",
      "serviceArn": "arn:aws:apprunner:${AWS_REGION}:${AWS_ACCOUNT_ID}:service/hello-world-service/${SERVICE_ID}"
    }
  ]
EOF
  1. もし 初めて AWS CDK を実行する場合は、AWS CDK 環境をブートストラップします (AWS アカウント ID と AWS リージョンを指定します)。
npm i
cdk bootstrap \
    --template bootstrap-template.yaml \
    aws://${AWS_ACCOUNT_ID}/${AWS_REGION}

注:ブートストラップは、1 回だけ必要です ( 既に実施している場合、このステップをスキップしてください )。

  1. コードをデプロイするために、以下のコマンドを実行してください。
npm i
cdk deploy --requires-approval

テスト

ソリューションのテストをするために、新しいバージョンのアプリケーションを Amazon ECR へプッシュしてください。最新のバージョンは、Amazon S3 バケット内の config.json ファイルで指定した、セマンティックバージョニングパターン (>1.2.3) に一致する必要があります。

  1. templates\index.html を開き、“And we’re live!“ の行を ”And we’re live, one more time! v1.2.4” と書き換え、hello world アプリケーションをアップデートします。
  2. docker イメージをビルドし、 Amazon ECR リポジトリにプッシュします。
cd ..
VERSION=1.2.4
docker build --platform linux/amd64 -t hello-world-apprunner-repo .
docker tag hello-world-apprunner-repo:latest ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/hello-world-apprunner-repo:${VERSION}
docker push ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/hello-world-apprunner-repo:${VERSION}

上記のアクションにより Amazon ECR のイベントがトリガーされ、AWS Lambda 関数が当該イベントをキャッチします。そして AWS Lambda 関数は、数秒以内に AWS App Runner サービスを新しいコンテナイメージでアップデートします。下記のコマンドで動作を確認することができます。

aws apprunner describe-service --service-arn \
  arn:aws:apprunner:${AWS_REGION}:${AWS_ACCOUNT_ID}:service/hello-world-service/${SERVICE_ID} | jq -r '.Service.Status'

出力

OPERATION_IN_PROGRESS

AWS App Runner サービスのイベントログを見ると、ソリューションによってアップデートがトリガーされていることが確認できます。

12-30-2022 01:55:48 PM [CI/CD] Semantic version >1.2.2 matched with the recent 
ECR push 1.2.4, so updating the service to the deploy from the latest version

アップデートが成功したら、AWS App Runner サービスへ適用された変更を含む以下のアウトプットが表示されます。

メリット

以下に、当該ソリューションを利用した際のメリットの一部を記載します。

  • セマンティックバージョニングは、リリースの影響をユーザに伝えます。そのため、ユーザは新しいバージョンへアップデートする際のリスクとメリットを容易に理解することができます。
  • AWS App Runner は、セマンティックバージョニングに基づき、新しいバージョンのアプリケーションを自動的にデプロイします。
  • アプリケーションのそれぞれのバージョンに (build ID, git commit hashに基づいた) ユニークなタグを利用できます。それゆえ、アプリケーションのバージョンの追跡や管理が簡単になります。
  • このアプローチにより、ソフトウェアのバージョン管理とリリースのベストプラクティスに従うことができます。さらに、インフラを心配することなくエンドユーザへソフトウェアの変更をロールアウトするために、AWS App Runner を活用することができます。
  • 概説した当該ソリューションは、スケーラブルであり、複数のアプリケーションをデプロイ可能です。

考慮事項

このソリューションを利用する上で考慮すべき必須項目をいくつか以下に記載します。

  • 当該ソリューションは、新しいバージョンのアプリケーションをデプロイする際に AWS App Runner APIs を call します。つまり、ソリューション自体はフルマネージドではありません。AWS CDK スタックや AWS Lambda 関数を管理する必要があります。
  • 当該ソリューションは、latest タグの追跡をサポートしません。latest タグや任意のタグを利用したい場合、AWS App Runner ネイティブの CI/CD 機能を使用することをお勧めします。
  • 当該ソリューションは、セマンティックバージョンパターンを追跡するために、さまざまな AWS サービス (Amazon Eventbridge, Amazon SQS, and AWS Lambda 等) を利用します。そのため、複数のAWS App Runner サービスや Amazon ECR リポジトリを追跡すると複数のイベント、 Amazon SQSのメッセージング、関数呼び出しが発生するため、コストが高くなる可能性があります。
  • 当該ソリューションは、現状のまま提供され、プロダクションレディではありません。利用する前に、検証環境でソリューションをテストしてください。
  • 当該ソリューションは、同一リポジトリを用いた複数の AWS App Runner サービスの追跡をサポートしていません。もし、複数の AWS App Runner で同一リポジトリを利用したい場合は、ソリューション コードを更新する必要があります。

クリーンアップ

AWS内に作成したインフラを削除するまでコストがかかり続けます。以下のコマンドを用いて、リソースを削除しましょう。

cdk destroy
aws ecr delete-repository --repository-name hello-world-apprunner-repo --force
aws iam detach-role-policy --role-name ${ROLE_NAME} --policy-arn arn:aws:iam::aws:policy/service-role/AWSAppRunnerServicePolicyForECRAccess
aws iam delete-role --role-name ${ROLE_NAME}
aws apprunner delete-service --service-arn ${SERVICE_ARN}
rm  -rf ../../../hello-app-runner

さいごに

このブログでは、セマンティックバージョニングに基づいてリリースパイプラインを強化する方法 及び 新しいバージョンのアプリケーションを完全に自動化してエンドユーザに配信する方法について記載しました。AWS App Runner は、AWS Secrets Manager や AWS Systems Manager Parameter Store などの多くの AWS サービスと統合し、安全にアプリケーションをデプロイ/実行するために設定データを参照します。AWS App Runner VPC support を利用すれば、アプリケーション周りのネットワークセキュリティを強化することもできます。そして、Amazon Virtual Private Cloud (Amazon VPC) 内にホストされた DB や他のアプリケーションに閉域で通信が可能になります。AWS App Runner のサービスを利用して AWS 上で適切に設計されたソリューションを構築する方法の詳細については、AWS App Runner の投稿を参照してください。

詳細は、以下を参照してください。

Build a Continuous Delivery Pipeline for Your Container Images with Amazon ECR as Source

翻訳はソリューションアーキテクト祖父江が担当しました。原文はこちらです。