ECR上のImage を AWS App Runner に CDK V2 でデプロイする

目次

AWS App Runner を使うと、Web Server を Docker コンテナにしておけば簡単にデプロイできます。 しかも、

  • ECR の Image を更新したら自動で再デプロイしてくれる(自動にしないことももちろん可能)
  • AutoScale や 独自ドメインの設定も可能
  • アクセス数、Latency、Instance 数などのメトリクスも設定無しで見れる
  • Application ログも自動で見られるようにしてくれる

といたれりつくせりです。ちょっとしたサービスをデプロイしたいときにはかなり重宝しそうです。

一度慣れれば手動設定でもそこまで面倒ではないですが、 今後何度もお世話になりそうなので、CDK V2 での書き方をまとめておきます。

Version

  • node: 16.13.1
  • aws-cdk: 2.8.0 (build 8a5eb49)
  • tsc: 4.5.4

書き方

import { Stack, StackProps } from "aws-cdk-lib";
import { Construct } from "constructs";

import { CfnService } from "aws-cdk-lib/aws-apprunner";
import { Role, ServicePrincipal } from "aws-cdk-lib/aws-iam";
import { StringParameter } from "aws-cdk-lib/aws-ssm";

// Deploy する Image の URI
const IMAGE_URI =
  "99999999999.dkr.ecr.ap-northeast-1.amazonaws.com/my-web-app:release";

export class MyWebAppStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    let buildRole = this.createECRRole();
    let taskRole = this.createTaskRole();

    // AWS::AppRunner::Service
    let appService = new CfnService(this, "MyWebApp", {
      serviceName: "MyWebApp",
      sourceConfiguration: {
        authenticationConfiguration: {
          accessRoleArn: buildRole.roleArn, // ECRにアクセスするときのRole?
        },
        imageRepository: {
          imageIdentifier: IMAGE_URI,
          imageRepositoryType: "ECR",
          imageConfiguration: {
            port: "8080", // Listen Port
            runtimeEnvironmentVariables: [
              // 実行時の環境変数
              { name: "PRODUCTION", value: "1" },
            ],
            startCommand: "web", // コンテナ実行時のコマンド
          },
        },
        autoDeploymentsEnabled: true, // 自動デプロイするか?
      },
      instanceConfiguration: {
        cpu: "1 vCPU",
        memory: "2 GB",
        instanceRoleArn: taskRole.roleArn, // コンテナ実行時の Role
      },
    });
  }

  createECRRole(): Role {
    let role = new Role(this, "MyWebAppECRRole", {
      assumedBy: new ServicePrincipal("build.apprunner.amazonaws.com"),
    });
    role.addManagedPolicy({
      managedPolicyArn:
        "arn:aws:iam::aws:policy/service-role/AWSAppRunnerServicePolicyForECRAccess",
    });
    return role;
  }

  // 実行時のRoleを作成する。必要にあわせて定義する。
  createTaskRole(): Role {
    // AppRunner Task用のRoleを作成
    let taskRole = new Role(this, "MyWebAppTaskRole", {
      assumedBy: new ServicePrincipal("tasks.apprunner.amazonaws.com"),
    });

    // SSM の SecureString へのアクセス権
    let googleJson = StringParameter.fromSecureStringParameterAttributes(
      this,
      "google-json",
      {
        parameterName: "/shared/google.json",
        version: 1,
      }
    );

    // Grant する
    googleJson.grantRead(taskRole);

    return taskRole;
  }
}

Reference