Amazon Web Services ブログ

AWS App Runner がモノレポをサポート

この記事は、AWS App Runner adds support for monorepos を翻訳したものです。

はじめに

AWS App Runner は、インフラストラクチャやコンテナに関する経験がなくても、コンテナ化されたウェブアプリケーションや API サービスを構築、デプロイ、実行できる、フルマネージド型のコンテナアプリケーションサービスです。本日より、AWS App Runner はモノレポ構造を取っているソースコードリポジトリからのサービスのデプロイをサポートします。これにより、複数のサービスのソースコードをホストするモノレポにおいて、デプロイするソースディレクトリを AWS App Runner に伝えることができます。
モノレポは、複数の異なるプロジェクトのコードを、明確に定義された関係を持つ単一のリポジトリに保存するソフトウェア開発戦略です。クラウドコンピューティングを利用するお客様は、コラボレーションを強化し、コードの重複を避け、可視性を向上させるために、モノレポ開発戦略を採用している場合があります。これは、マイクロサービスベースのアーキテクチャに従った最新のアプリケーションを開発する場合に特に有益です。
お客様は、AWS App Runner の「ソースからのビルド」機能を使用してソースコードから直接サービスをデプロイすることで、ビルドとデプロイのワークフロー管理を AWS App Runner に任せることができます。以前は、AWS App Runner は、ビルドコマンドと起動コマンドを実行する際にリポジトリのルートディレクトリのみをサポートしていました。本日より、アプリケーションのビルドパイプラインとデプロイパイプラインを個別に管理する必要がなくなります。アプリケーションのビルドとデプロイに使用する AWS App Runner サービス設定でソースディレクトリを定義できます。
サービスの自動デプロイを有効にすることもできます。自動デプロイを有効にすると、AWS App Runner はソースディレクトリまたはサービスの依存関係に更新があったときにサービスを再ビルドしてデプロイします。ソースディレクトリ以外の他のアプリケーションやフォルダが更新されても、AWS App Runner はサービスを不必要に再構築してデプロイしません。
つまり、お客様は AWS App Runner のソースからのビルド機能を利用して、モノレポ構造に従うソースコードリポジトリから直接サービスをデプロイできます。お客様は、ソースコードベースのサービスの標準ビルド料金を支払います。お客様は、モノレポアプリケーションの柔軟性と、AWS App Runner でのシンプルな実行のメリットを享受できます。

ソリューション概要

AWS App Runner でモノレポをサポートする機能を紹介するために、ウォークスルーを行います。1 つのソースコードリポジトリから 2 つのサービス (1 つはフロントエンド、もう 1 つはバックエンド) を含むサンプルアプリケーションをデプロイします。
サンプルアプリケーションは、架空のホテルのウェブサイトを運営する 2 つのマイクロサービスのモノレポによって支えられています。フロントエンドとバックエンドはどちらも、Express ウェブフレームワークが提供する Node.js アプリケーションです。AWS App Runner を使用して、アプリケーションをホストする 2 つのサービスを作成します。
アプリケーションをサポートするインフラストラクチャの一部として、Amazon Relational Database Service (Amazon RDS) データベースと、2 つのサービスとデータベース間の通信に必要な Amazon Virtual Private Cloud (Amazon VPC) ネットワークコンポーネントを作成します。パブリックのインターネットトラフィックがフロントエンドサービスに到達し、ホテルの部屋の管理をリクエストできるようになります。
これらのリクエストは、VPC ConnectorVPCIngressConnection を経由して、Amazon RDS データベースにクエリを実行してリクエストを処理するバックエンドサービスに安全に送信されます。最後に、リクエストが処理され、リクエスタがレスポンスを受け取ります。AWS App Runner によるプライベートネットワーキングの詳細については、VPC ネットワーキングプライベートサービスに関する詳細な投稿を参照してください。
AWS App Runner サービスを作成する際には、各ソースディレクトリに対して行われたコミットがそれぞれのサービスにのみデプロイされるように、自動デプロイを有効にしてそれぞれのソースディレクトリを設定します。

図 1: AWS クラウド内で接続された 2 つの モノレポ AWS App Runner サービスを示すアーキテクチャ図

図 1: AWS クラウド内で接続された 2 つの モノレポ AWS App Runner サービスを示すアーキテクチャ図

前提条件

ウォークスルーを完了するには、次のツールのセットアップが必要です。

ウォークスルー

AWS App Runner の Connection

コードベースのサービスの場合、AWS App Runner はリポジトリからコードをデプロイするための Connection を必要とします。このチュートリアルでは、フォークできるホテルアプリケーションのモノレポを GitHub 上に作成しました。
まず、この GitHub リポジトリにアクセスし、個人の GitHub アカウントにモノレポブランチをフォークします。フォークする際には、Copy the main branch only のチェックを外し、main 以外のブランチもコピーするようにしてください。フォークしたのち、リポジトリの URL を環境変数として保存します。

REPOSITORY_URL=https://<<YOUR_FORKED_REPOSITORY_URL>>

次に、us-east-2 の AWS App Runner コンソールにアクセスして、個人用 GitHub アプリケーションに接続する GitHubConnection という名前の AWS App Runner の Connection を作成します。認証されると、GitHub リポジトリから AWS App Runner サービスを作成できるようになります。この認証ハンドシェイクの仕組みの詳細については、開発者ガイドをご覧ください。
ConnectionArn をこの後使用できるようにローカルに保存します。

export MRO_AWS_REGION=us-east-2
LIST_CONNECTIONS_RESPONSE=$(aws apprunner --region $MRO_AWS_REGION \
    list-connections \
    --connection-name "GitHubConnection")

APP_RUNNER_CONNECTION_ARN=$(echo ${LIST_CONNECTIONS_RESPONSE} | jq -r '.ConnectionSummaryList[0].ConnectionArn')

ネットワーク及びデータベースインフラストラクチャの構築

サンプルのリポジトリ (もしくは、新しくフォークしたレポジトリ) をクローンします。

export MRO_STACK_NAME=apprunner-monorepo
git clone --branch monorepo https://github.com/aws-samples/apprunner-hotel-app.git
cd ./apprunner-hotel-app/

それでは以下のコマンドを実行し、AWS CloudFormation のテンプレートである infrastructure/base-infra.yaml を利用して、VPC、VPC エンドポイント、VPC Connector、Amazon RDS インスタンス、データベースの認証情報のための AWS Secrets Manager の Secret をデプロイしましょう。

aws cloudformation deploy \
    --region ${MRO_AWS_REGION} \
    --template-file ./infrastructure/base-infra.yaml \
    --stack-name ${MRO_STACK_NAME} \
    --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM

上記の AWS CloudFormation スタックの実行が完了するまで待ちます。この際、スタック作成に時間がかかる場合はコマンドから抜けてプロンプトに戻る場合があります。必要に応じてスタックの実行状況を確認し、スタックの実行が継続していれば問題ありません。スタックの実行が完了したら、以下のコマンドを実行してスタックから出力値を取得します。

#Hotel Name
HOTEL_NAME=$(aws cloudformation describe-stacks \
    --region ${MRO_AWS_REGION} \
    --stack-name ${MRO_STACK_NAME} \
    --query 'Stacks[0].Outputs[?OutputKey==`HotelName`].OutputValue' \
    --output text)

#RDS Secret
SECRET_ARN=$(aws cloudformation describe-stacks \
    --region ${MRO_AWS_REGION} \
    --stack-name ${MRO_STACK_NAME} \
    --query 'Stacks[0].Outputs[?OutputKey==`DBSecret`].OutputValue' \
    --output text)

#VPC ID
VPC_ID=$(aws cloudformation describe-stacks \
    --region ${MRO_AWS_REGION} \
    --stack-name ${MRO_STACK_NAME} \
    --query 'Stacks[0].Outputs[?OutputKey==`VPCID`].OutputValue' \
    --output text)

#App Runner VPC Endpoint
VPC_ENDPOINT=$(aws cloudformation describe-stacks \
    --region ${MRO_AWS_REGION} \
    --stack-name ${MRO_STACK_NAME} \
    --query 'Stacks[0].Outputs[?OutputKey==`AppRunnerVPCEndpoint`].OutputValue' \
    --output text)
    
#Backend App Runner VPC Connector
VPC_BE_CONNECTOR_ARN=$(aws cloudformation describe-stacks \
    --region ${MRO_AWS_REGION} \
    --stack-name ${MRO_STACK_NAME} \
    --query 'Stacks[0].Outputs[?OutputKey==`AppRunnerBEVPCConnector`].OutputValue' \
    --output text)
    
#Frontend App Runner VPC Connector
VPC_FE_CONNECTOR_ARN=$(aws cloudformation describe-stacks \
    --region ${MRO_AWS_REGION} \
    --stack-name ${MRO_STACK_NAME} \
    --query 'Stacks[0].Outputs[?OutputKey==`AppRunnerFEVPCConnector`].OutputValue' \
    --output text)

#App Runner IAM Instance Role
INSTANCE_ROLE_ARN=$(aws cloudformation describe-stacks \
    --region ${MRO_AWS_REGION} \
    --stack-name ${MRO_STACK_NAME} \
    --query 'Stacks[0].Outputs[?OutputKey==`AppRunnerInstanceRole`].OutputValue' \
    --output text)

モノレポサービスの作成

これで、バックエンドとフロントエンドの AWS App Runner サービスをサポートするために必要なすべてのインフラストラクチャが揃いました。

バックエンドサービスの作成

それでは、バックエンドサービスを作成しましょう。まず、以下のコマンドを実行して、サービスの構成を表すローカルファイル create_backend_service.json を作成します。SourceDirectory は Git リポジトリ内の backend ディレクトリ が指定されていることに注意してください。

cat > create_backend_service.json << EOF
{
    "ServiceName": "hotel-backend",
    "SourceConfiguration": {
       "CodeRepository": {
          "RepositoryUrl": "${REPOSITORY_URL}",
          "SourceCodeVersion": {
             "Type": "BRANCH",
             "Value": "monorepo"
          },
          "CodeConfiguration": {
             "ConfigurationSource": "API",
             "CodeConfigurationValues": {
               "BuildCommand": "npm install",
               "Port": "8080",
               "Runtime": "NODEJS_16",
               "RuntimeEnvironmentSecrets": {
                  "HOTEL_NAME" : "${HOTEL_NAME}",
                  "MYSQL_SECRET": "${SECRET_ARN}"
               },
               "StartCommand": "npm start"
             }
          },
          "SourceDirectory": "backend"
       },
       "AutoDeploymentsEnabled": true,
       "AuthenticationConfiguration": {
          "ConnectionArn": "${APP_RUNNER_CONNECTION_ARN}"
       }
    },
    "InstanceConfiguration": {
        "Cpu": "1 vCPU",
        "Memory": "3 GB",
        "InstanceRoleArn": "${INSTANCE_ROLE_ARN}"
    },
    "NetworkConfiguration": {
        "EgressConfiguration": {
            "EgressType": "VPC",
            "VpcConnectorArn": "${VPC_BE_CONNECTOR_ARN}"
        },
        "IngressConfiguration": {
            "IsPubliclyAccessible": false
        }
    }
}
EOF

バックエンドサービスを作成します。

BACKEND_CREATE_SERVICE_RESPONSE=$(aws apprunner --region $MRO_AWS_REGION \
    create-service \
    --cli-input-json file://create_backend_service.json) 

バックエンドサービスの ServiceArn を保存します。

BACKEND_SERVICE_ARN=$(echo ${BACKEND_CREATE_SERVICE_RESPONSE} | jq -r '.Service.ServiceArn')

VpcIngressConnection の作成

デフォルトでは、AWS App Runner サービスはインターネット経由でパブリックにアクセスできます。ただし、バックエンドサービスは公開されることを意図したものではありません。VPC 内でのみアクセスできるようにする必要があります。
バックエンドサービスへのネットワークアクセスを制限するために、AWS App Runner VPC Ingress Connection リソースを作成します。VPC Ingress Connection は VPC インターフェイスエンドポイントと AWS App Runner サービス間の接続を確立し、Amazon VPC 内からのみ App Runner サービスにアクセスできるようにします

バックエンドサービスへのプライベートアクセスを許可する VpcIngressConnection を作成します。

INGRESS_CREATE_RESPONSE=$(aws apprunner --region ${MRO_AWS_REGION} \
    create-vpc-ingress-connection \
    --service-arn ${BACKEND_SERVICE_ARN} \
    --vpc-ingress-connection-name "Private-Connection-To-Backend" \
    --ingress-vpc-configuration VpcId=${VPC_ID},VpcEndpointId=${VPC_ENDPOINT})

VpcIngressConnectionArn と、バックエンドサービスURL の出力値を保存します。

INGRESS_ARN=$(echo ${INGRESS_CREATE_RESPONSE} | jq -r '.VpcIngressConnection.VpcIngressConnectionArn')
BACKEND_URL="https://$(echo ${INGRESS_CREATE_RESPONSE} | jq -r '.VpcIngressConnection.DomainName')/"

フロントエンドサービスの作成

いよいよフロントエンドサービスをデプロイします。以下のコマンドを実行して、サービスの構成を表すローカルファイル create_frontend_service.json を作成します。SourceDirectory は Git リポジトリ内の frontend ディレクトリ が指定されていることに注意してください。

cat > create_frontend_service.json << EOF
{
    "ServiceName": "hotel-frontend",
    "SourceConfiguration": {
       "CodeRepository": {
          "RepositoryUrl": "${REPOSITORY_URL}",
          "SourceCodeVersion": {
             "Type": "BRANCH",
             "Value": "monorepo"
          },
          "CodeConfiguration": {
             "ConfigurationSource": "API",
             "CodeConfigurationValues": {
               "BuildCommand": "npm install",
               "Port": "8080",
               "Runtime": "NODEJS_16",
               "RuntimeEnvironmentSecrets": {
                  "HOTEL_NAME" : "${HOTEL_NAME}"
               },
               "RuntimeEnvironmentVariables": {
                  "BACKEND_URL": "${BACKEND_URL}"
               },
               "StartCommand": "npm start"
             }
          },
          "SourceDirectory": "frontend"
       },
       "AutoDeploymentsEnabled": true,
       "AuthenticationConfiguration": {
          "ConnectionArn": "${APP_RUNNER_CONNECTION_ARN}"
       }
    },
    "InstanceConfiguration": {
        "Cpu": "1 vCPU",
        "Memory": "3 GB",
        "InstanceRoleArn": "${INSTANCE_ROLE_ARN}"
    },
    "NetworkConfiguration": {
        "EgressConfiguration": {
            "EgressType": "VPC",
            "VpcConnectorArn": "${VPC_FE_CONNECTOR_ARN}"
        },
        "IngressConfiguration": {
            "IsPubliclyAccessible": true
        }
    }
}
EOF

フロントエンドサービスを作成します。

FRONTEND_CREATE_SERVICE_RESPONSE=$(aws apprunner --region $MRO_AWS_REGION \
    create-service \
    --cli-input-json file://create_frontend_service.json)

フロントエンドサービスの ServiceArn と、ServiceUrl の出力値を保存します。

FRONTEND_SERVICE_ARN=$(echo ${FRONTEND_CREATE_SERVICE_RESPONSE} | jq -r '.Service.ServiceArn')
FRONTEND_URL="https://$(echo ${FRONTEND_CREATE_SERVICE_RESPONSE} | jq -r '.Service.ServiceUrl')"

作成が完了するまでの間、適宜ステータスを確認します。

aws apprunner --region $MRO_AWS_REGION \
    describe-service \
    --service-arn ${FRONTEND_SERVICE_ARN}

サービスが利用可能になったら、FRONTEND_URL を介してアプリケーションにアクセスします。

echo ${FRONTEND_URL}

図2: モノレポのフロントエンドとバックエンドサービスで構築された実行中のホテルアプリケーション

図2: モノレポのフロントエンドとバックエンドサービスで構築された実行中のホテルアプリケーション

このアプリケーションは、Create ボタンを押下してデータベーススキーマを作成し、ホテルの部屋を追加して表示することができます。自動デプロイを有効にしている場合、GitHub リポジトリの frontend ディレクトリに変更がコミットされると、フロントエンドサービスへのデプロイが開始されますが、バックエンドサービスへのデプロイは行われません。本記事では、自動デプロイを有効にしてサービスを作成しています。ぜひ試してみてください!

後片付け

このウォークスルー中に作成された AWS リソースにはコストがかかります。ウォークスルーが終了したら、作成したインフラストラクチャを必ず削除してください。

まず、AWS App Runner VpcIngressConnection を削除します。

INGRESS_DELETE_RESPONSE=$(aws apprunner --region ${MRO_AWS_REGION} \
    delete-vpc-ingress-connection \
    --vpc-ingress-connection-arn ${INGRESS_ARN})

次に、フロントエンド、バックエンドサービスを削除します。

FRONTEND_DELETE_SERVICE_RESPONSE=$(aws apprunner --region $MRO_AWS_REGION \
    delete-service \
    --service-arn ${FRONTEND_SERVICE_ARN})

BACKEND_DELETE_SERVICE_RESPONSE=$(aws apprunner --region $MRO_AWS_REGION \
    delete-service \
    --service-arn ${BACKEND_SERVICE_ARN})

最後に、AWS CloudFormation スタックを削除します。

DELETE_STACK_RESPONSE=$(aws cloudformation --region $MRO_AWS_REGION \
    delete-stack \
    --stack-name ${MRO_STACK_NAME})

まとめ

本記事では、AWS App Runner を使用してモノレポ構成のリポジトリからサービスをデプロイする方法を紹介しました。App Runner のモノレポをサポートする機能を使用して、単一のソースコードリポジトリにフロントエンド層とバックエンド層の両方を持つサンプルアプリケーションをデプロイしました。AWS App Runner を使用して、本番環境のウェブアプリケーションでこの新機能を試すことをお勧めします。AWS App Runner の詳細については、ドキュメント開発者ガイドをご覧ください。

翻訳はパートナーソリューションアーキテクトの髙橋達矢が担当しました。原文はこちらです。