ついにAWS App RunnerでVPCリソースにアクセスできるようになったので、CloudFormationで作ってみました。
概要
先日ようやく対応した、VPCリソースにアクセスできるAWS App Runnerを、CloudFormationで構築します。
前提
マネージドランタイム(GitHub)ではなく、ECR連携での構築を行います。
(マネージドランタイムの場合でも土台は変わりません。)
ちなみにCDKでも作っているので(こちらはマネージドランタイム版)、良ければご覧下さい。
方法
テンプレートのyamlファイルとシェルスクリプトによって構築します。
補足
別記事にまとめたのですが、App RunnerとECS Fargateの違いなどを以下に記載していますのでよかったらぜひご覧下さい。
また、以下のようなVPCコネクタにおけるルールやエラーもあるため、構築・変更には注意が必要です。
コード
※GitHubにもあります。
yaml
- apprunner.yaml
Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: "ServiceName" Parameters: - ServiceName - Label: default: "VpcConnectorName" Parameters: - VpcConnectorName - Label: default: "SecurityGroupName" Parameters: - SecurityGroupName - Label: default: "SubnetID1" Parameters: - SubnetID1 - Label: default: "SubnetID2" Parameters: - SubnetID2 - Label: default: "VpcID" Parameters: - VpcID - Label: default: "CPU" Parameters: - CPU - Label: default: "Memory" Parameters: - Memory - Label: default: "AutoScalingConfigurationArn" Parameters: - AutoScalingConfigurationArn - Label: default: "ImageIdentifier" Parameters: - ImageIdentifier Parameters: ServiceName: Type: String VpcConnectorName: Type: String SecurityGroupName: Type: String SubnetID1: Type: String SubnetID2: Type: String VpcID: Type: String CPU: Type: String AllowedPattern: "1024|2048|(1|2) vCPU" Default: "1 vCPU" Memory: Type: String AllowedPattern: "2048|3072|4096|(2|3|4) GB" Default: "2 GB" AutoScalingConfigurationArn: Type: String ImageIdentifier: Type: String Resources: AppRunnerAccessRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: - build.apprunner.amazonaws.com Action: sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSAppRunnerServicePolicyForECRAccess AppRunnerInstanceRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Effect: Allow Principal: Service: tasks.apprunner.amazonaws.com Action: sts:AssumeRole AppRunnerSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: VpcId: !Ref VpcID GroupName: !Ref SecurityGroupName GroupDescription: "for App Runner" Tags: - Key: "Name" Value: !Ref SecurityGroupName VpcConnector: Type: AWS::AppRunner::VpcConnector Properties: VpcConnectorName: !Ref VpcConnectorName Subnets: - !Ref SubnetID1 - !Ref SubnetID2 SecurityGroups: - !Ref AppRunnerSecurityGroup AppRunnerService: Type: AWS::AppRunner::Service Properties: AutoScalingConfigurationArn: !Ref AutoScalingConfigurationArn # EncryptionConfiguration: # by default, App Runner uses an AWS managed key. HealthCheckConfiguration: Path: "/" Protocol: "HTTP" Interval: 5 Timeout: 5 HealthyThreshold: 1 UnhealthyThreshold: 2 InstanceConfiguration: Cpu: !Ref CPU Memory: !Ref Memory InstanceRoleArn: !GetAtt AppRunnerInstanceRole.Arn NetworkConfiguration: EgressConfiguration: EgressType: VPC VpcConnectorArn: !GetAtt VpcConnector.VpcConnectorArn ServiceName: !Ref ServiceName SourceConfiguration: AuthenticationConfiguration: AccessRoleArn: !GetAtt AppRunnerAccessRole.Arn # ConnectionArn: # required for GitHub code repositories. AutoDeploymentsEnabled: true # CodeRepository: # required for GitHub code repositories. ImageRepository: ImageRepositoryType: ECR ImageIdentifier: !Ref ImageIdentifier ImageConfiguration: Port: 80 # RuntimeEnvironmentVariables: # StartCommand:
シェル
- apprunner.sh
#/bin/bash set -eu StackName="test-app-runner" TemplateFile="apprunner.yaml" ServiceName="${StackName}" VpcConnectorName="${StackName}" SecurityGroupName="${StackName}" VpcID="vpc-*****************" SubnetID1="subnet-*****************" SubnetID2="subnet-*****************" ImageIdentifier="123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/test-app-runner:latest" CPU="1 vCPU" # "1024|2048|(1|2) vCPU" Memory="2 GB" # "2048|3072|4096|(2|3|4) GB" MaxSize=3 MinSize=2 MaxConcurrency=100 AutoScalingConfiguration=$(cat <<EOF { "AutoScalingConfigurationName": "${StackName}", "MinSize": ${MinSize}, "MaxSize": ${MaxSize}, "MaxConcurrency": ${MaxConcurrency} } EOF ) AutoScalingConfigurationArn=$( \ aws apprunner \ create-auto-scaling-configuration \ --cli-input-json "${AutoScalingConfiguration}" \ | jq -r '.AutoScalingConfiguration.AutoScalingConfigurationArn' \ ) aws cloudformation deploy \ --stack-name ${StackName} \ --region ap-northeast-1 \ --template-file ${TemplateFile} \ --capabilities CAPABILITY_IAM \ --no-fail-on-empty-changeset \ --parameter-overrides \ ServiceName="${ServiceName}" \ VpcConnectorName="${VpcConnectorName}" \ SecurityGroupName="${SecurityGroupName}" \ VpcID="${VpcID}" \ SubnetID1="${SubnetID1}" \ SubnetID2="${SubnetID2}" \ CPU="${CPU}" \ Memory="${Memory}" \ AutoScalingConfigurationArn="${AutoScalingConfigurationArn}" \ ImageIdentifier="${ImageIdentifier}"
解説
yaml
AppRunnerAccessRole
AppRunnerAccessRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: - build.apprunner.amazonaws.com Action: sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSAppRunnerServicePolicyForECRAccess
こちらはアクセスロールと呼ばれるもので、App RunnerがECRからイメージをプルするときに使用されるIAMロールです。
PrincipalにはServiceとしてbuild.apprunner.amazonaws.com
を指定します。
またManagedPolicyArnsとしてAWSAppRunnerServicePolicyForECRAccess
がAWSから提供されているので、こちらを指定します。
アクセスロールは、Amazon ECR パブリックを使用する場合は必須ではありません。
AppRunnerInstanceRole
AppRunnerInstanceRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Effect: Allow Principal: Service: tasks.apprunner.amazonaws.com Action: sts:AssumeRole
こちらはインスタンスロールというもので、App Runnerで実行されるアプリケーションがAWSサービスにアクセスするために必要な権限を指定するものです。
今回特にポリシーは付けていませんが、アプリケーションの実装に合わせて適宜必要な権限を付与してください。
大事なのが、PrincipalにはServiceとしてtasks.apprunner.amazonaws.com
を指定する点です。
AppRunnerSecurityGroup
AppRunnerSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: VpcId: !Ref VpcID GroupName: !Ref SecurityGroupName GroupDescription: "for App Runner" Tags: - Key: "Name" Value: !Ref SecurityGroupName
これは、VpcConnectorにアタッチするセキュリティグループです。
インバウンドルールは必要ない(効かない)ので、特に何も指定せずに作成します。すると、インバウンドルールが全て拒否・アウトバンドルールが全て許可のセキュリティグループになります。
自前でセキュリティグループを作成しない場合は、VpcConnectorではデフォルトのセキュリティグループ(上記と同じルール)がアタッチされます。
VpcConnector
VpcConnector: Type: AWS::AppRunner::VpcConnector Properties: VpcConnectorName: !Ref VpcConnectorName Subnets: - !Ref SubnetID1 - !Ref SubnetID2 SecurityGroups: - !Ref AppRunnerSecurityGroup
こちらが先日対応された、App RunnerからVPCリソースにアクセスするためのリソース「VpcConnector」になります。
特に複雑な設定はなく、名前、サブネット、セキュリティグループを指定します。
指定したサブネットにネットワークインターフェースが作成されます。アクセスしたいリソースがあるサブネット、またはそのサブネットへの疎通が可能なサブネットを指定してください。
複数サブネットを指定した場合は、それぞれのサブネットにネットワークインターフェースが作成されます。
またセキュリティグループは、上記で少しご説明しましたが、指定しない場合はデフォルトのものが使用され、全アウトバウンドを許可するものになります。もしアクセスしたいリソースが、特定のセキュリティグループからのアクセスのみ受け付けるように環境を構築している場合などは、そのセキュリティグループをアタッチしてください。
AppRunnerService
AppRunnerService: Type: AWS::AppRunner::Service Properties: AutoScalingConfigurationArn: !Ref AutoScalingConfigurationArn # EncryptionConfiguration: # by default, App Runner uses an AWS managed key. HealthCheckConfiguration: Path: "/" Protocol: "HTTP" Interval: 5 Timeout: 5 HealthyThreshold: 1 UnhealthyThreshold: 2 InstanceConfiguration: Cpu: !Ref CPU Memory: !Ref Memory InstanceRoleArn: !GetAtt AppRunnerInstanceRole.Arn NetworkConfiguration: EgressConfiguration: EgressType: VPC VpcConnectorArn: !GetAtt VpcConnector.VpcConnectorArn ServiceName: !Ref ServiceName SourceConfiguration: AuthenticationConfiguration: AccessRoleArn: !GetAtt AppRunnerAccessRole.Arn # ConnectionArn: # required for GitHub code repositories. AutoDeploymentsEnabled: true # CodeRepository: # required for GitHub code repositories. ImageRepository: ImageRepositoryType: ECR ImageIdentifier: !Ref ImageIdentifier ImageConfiguration: Port: 80 # RuntimeEnvironmentVariables: # StartCommand:
こちらはApp Runnerのサービス自体を定義するAWS::AppRunner::Service
です。
AutoScalingConfigurationArnには、次項目のシェルでの説明で後述するAutoScalingConfigurationのARNを指定します。AutoScalingのスケーリングポリシーのようなものです。
AutoScalingConfigurationArn: !Ref AutoScalingConfigurationArn
HealthCheckConfigurationはヘルスチェックの設定(値はテキトーです)、InstanceConfigurationはインスタンスのスペックとインスタンスロールの設定になります。
HealthCheckConfiguration: Path: "/" Protocol: "HTTP" Interval: 5 Timeout: 5 HealthyThreshold: 1 UnhealthyThreshold: 2 InstanceConfiguration: Cpu: !Ref CPU Memory: !Ref Memory InstanceRoleArn: !GetAtt AppRunnerInstanceRole.Arn
ちなみにApp Runnerで指定できるスペックは以下の組み合わせになります。ECS Fargateと比べると少し少ないです。
CPU | メモリ |
---|---|
1 vCPU | 2 GB |
1 vCPU | 3 GB |
1 vCPU | 4 GB |
2 vCPU | 4 GB |
また、VPCへのアクセスは、以下の部分にて可能になります。
EgressType: VPC
にして、先ほど作成したVpcConnectorのARNを指定します。
NetworkConfiguration: EgressConfiguration: EgressType: VPC VpcConnectorArn: !GetAtt VpcConnector.VpcConnectorArn
最後にアプリケーションのソースに関する設定です。GitHubを使用する際には必須のパラメータなどもありますが、今回はコメントアウトしています。
SourceConfiguration: AuthenticationConfiguration: AccessRoleArn: !GetAtt AppRunnerAccessRole.Arn # ConnectionArn: # required for GitHub code repositories. AutoDeploymentsEnabled: true # CodeRepository: # required for GitHub code repositories. ImageRepository: ImageRepositoryType: ECR ImageIdentifier: !Ref ImageIdentifier ImageConfiguration: Port: 80 # RuntimeEnvironmentVariables: # StartCommand:
AccessRoleArnには、先ほど定義したアクセスロールを指定します。
AuthenticationConfiguration: AccessRoleArn: !GetAtt AppRunnerAccessRole.Arn
AutoDeploymentsEnabled
はプッシュ時に自動デプロイを走らせるかの設定です。
AutoDeploymentsEnabled: true
ImageRepositoryTypeでは、ソースのリポジトリをGitHubにするのかECRにするのかを決めたり、ImageIdentifierにはイメージのURIを指定します。
またImageConfigurationで、コンテナ自体の開放するポートや環境変数なども設定します。(指定しなくてもOKです)
ImageRepository: ImageRepositoryType: ECR ImageIdentifier: !Ref ImageIdentifier ImageConfiguration: Port: 80 # RuntimeEnvironmentVariables: # StartCommand:
ちなみにImageIdentifierはイメージのURIを指定すると書きましたが、具体的には以下のように「(ECRリポジトリのURI):(イメージタグ)」という形になります。
AutoDeploymentsEnabledがtrueの場合、このタグのイメージがプッシュされたら自動でデプロイが走ります。
(latestタグで運用する必要があるということでしょうかね。今回は深く触れません。)
123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/test-app-runner:latest
シェル
全体的にテキトーに書いているので実際に使われる方はうまくアレンジしてください・・・!
ここで説明するのは、AutoScalingConfigurationArnです。
MaxSize=3 MinSize=2 MaxConcurrency=100 AutoScalingConfiguration=$(cat <<EOF { "AutoScalingConfigurationName": "${StackName}", "MinSize": ${MinSize}, "MaxSize": ${MaxSize}, "MaxConcurrency": ${MaxConcurrency} } EOF ) AutoScalingConfigurationArn=$( \ aws apprunner \ create-auto-scaling-configuration \ --cli-input-json "${AutoScalingConfiguration}" \ | jq -r '.AutoScalingConfiguration.AutoScalingConfigurationArn' \ )
AutoScalingConfiguration
AutoScalingConfigurationArnはAWS::AppRunner::Serviceで指定するのですが、その名の通りAutoScalingConfigurationのARNになります。
この指定をわざわざCLIでの実行を通してyamlテンプレート側に渡しているのは、2022年2月14日現在、AutoScalingConfigurationの作成がCloudFormationに対応していないからです。
そのため、CLIでAutoScalingConfigurationを作成し、レスポンスからjqコマンドを通してARNを抽出しています。
JSONで記述するのですが、上記のようにヒアドキュメントでやるのも良し、別JSONファイルに格納してファイルとして読ませるのも良しです。
そもそもAutoScalingConfigurationとは、App RunnerでデプロイするアプリケーションのAutoScalingのための設定です。
以下4つが主な設定項目になります。
項目 | 内容 | デフォルト値 |
---|---|---|
AutoScalingConfigurationName | 設定名 | - |
MinSize | 最低台数 | 1 |
MaxSize | 最大台数 | 25 |
MaxConcurrency | 最大同時リクエスト数 これを超えたときにスケールアウトする |
100 |
最後に
ようやくApp Runnerで既存のVPCリソースへのアクセスが可能になったため、とりあえずCloudFormationで構築してみました。
App Runner自体の良し悪し・気になる点もあるので今度まとめてみたいなと思っています。