🎉

AppRunnerでexpressのデプロイ

2024/03/07に公開

AppRunnerとは

インフラやAWSの経験がなくてもコンテナ化されたWebアプリやAPIを簡単にAWS上で動かせるサービスです。構築済みのインフラが早く欲しいとき、とりあえずアプリの開発をしたい時、アプリ開発に集中したい時などに便利なサービスです。
利用者からはロードバランサーなどのインフラは見えないようになっており、見えるのはコンテナインスタンスのみです。これによりアプリの開発と運用に集中することができます。
AppRunner
AppRunner

AppRunnerでできること

  • ソースコードやソースイメージの変更を検知し、自動でデプロイしてくれます。このデプロイはブルーグリーンデプロイでダウンタイムなく行われます。よってCodePipelineなど不要でCI/CD環境の構築ができます。

  • しきい値、コンテナの最小最大数を決めるだけでトラフィックサイズに応じてオートスケールします。

  • ログやメトリクスは自動で収集されAppRunnerのコンソール画面から確認することができます。

  • デプロイするとデフォルトドメインが自動生成されます。このドメインはhttpsとなりTLSを自動で設定します。(任意のカスタムドメインも設定可能)

  • VPCコネクタを設定することで自身のVPC内にあるRDSなどのサービスに接続可能です。

  • アプリケーション設定値を環境変数で管理できます。

その他のAWSサービスとの比較

Webアプリを作成した際にどのサービスを利用して公開するか考えると思います。その際のAppRunnerと比較したときのメリットデメリットです。

  1. EC2
  • メリット・・・自分で制御が可能で柔軟性が高い
  • デメリット・・・自分でメンテナンスをしなければならない、インフラの知識が必要
  1. ECS Fargate
  • メリット・・・コンテナの特性を活かしつつ運用を自動化できる。
  • デメリット・・・コンテナに関する知識経験が必要
  1. Lambda
  • メリット・・・フルマネージドな環境で実行でき流。またリクエスト数と実行時間による従量課金でコスト最適化もできる。
  • デメリット・・・アプリケーションアーキテクチャを大きく作り変える必要がある。

動かしてみる

expressとTypeScriptのプロジェクトをGithub, ECR,CDKを使用して3パターンでデプロイしてみます。

1. Githubからデプロイする

簡単なプロジェクトをセットアップします。
express-ts-from-githubフォルダを作成して、その中で以下のコマンドを実行します。

npm init -y
npm install express
npm install typescript ts-node @types/node @types/express --save-dev
npx tsc --init

srcフォルダを作成し、index.tsファイルを作成します。index.tsに以下のコードを追加します。

import express from 'express'

const app: express.Express = express()

const router: express.Router = express.Router()
router.get('/', (req: express.Request, res: express.Response) => {
  res.send("Hello World")
})
app.use(router)

app.listen(3000, () => { console.log('Example app listening on port 3000!') })

package.jsonのscriptsに"start": "ts-node src/index.ts"を追加します。
npm run startをしてHello Worldが確認できればOKです。
githubにリポジトリを新たに作成し、作成したファイルをpushしたら準備完了です。
github

AppRunnerのサービスの作成から新たにサービスを作成します。
リポジトリタイプにソースコードリポジトリを選択し、プロバイダーにGitHubを選択します。
GitHubの接続ができていない場合は、新規追加から設定します。
その後、作成したリポジトリとブランチを選択します。
AppRunner
デプロイの設定は自動にしておくことでGitHubにプッシュした際に自動で再デプロイしてくれます。

ランタイム、構築コマンド、開始コマンド、ポートを以下の画像のように入力し次へを押します。
なお、apprunner.yamlにここの設定を記述してデプロイすることも可能です。

サービス名は自由に設定し他の設定はデフォルトのままで次へを押します。
確認画面に移りますが、間違いがないか確認して作成とデプロイをクリックします。

しばらく待つとデプロイが完了して、デフォルトドメインからHello Worldが表示されることが確認できます。

2. ECRからデプロイする

ECRからデプロイする方法です。
まずECRにプライベートリポジトリを作成します。

先ほどのプロジェクトに、以下のDockerfileを追加します。

FROM node:14-slim
WORKDIR /usr/src/app
COPY . .
RUN npm install
EXPOSE 3000

CMD ["npm", "start"]

ECRのリポジトリからプッシュコマンドを確認できるので4つの各コマンドを実行します。
macのM1/M2を使用している場合はイメージのbuild時に--platform linux/amd64を指定する必要があります。
プッシュが完了したら以下の設定でサービスを作成します。画像以外の設定はGitHubでデプロイした際の設定と同じになります。

設定が完了したら作成とデプロイをクリックしてデプロイを待ちます。
しばらくしてステータスがRunningになり、デフォルトドメインからHello Worldが確認できれば完了です。

3. CDKを使ってデプロイする

次はAWS CDKを使用してデプロイする方法です。
cdk init app --language=typescriptでプロジェクトを作成します。

ECRのリポジトリを作成します。

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { aws_ecr as ecr, RemovalPolicy }from 'aws-cdk-lib';

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

    // ecr
    const repository = new ecr.Repository(this, 'AppRunnerDemoRepository', {
      removalPolicy: RemovalPolicy.DESTROY
    })
    
  }
}

先にイメージをプッシュしておかないといけないのでこの時点で一度デプロイします。初めてデプロイする際はcdk bootstrapの実行が必要です。

cdk bootstrap
cdk deploy

デプロイが完了したらECRリポジトリにDockerイメージをプッシュします。プッシュの仕方は前述と同じです。

次にIAMの作成をします。

const ecrAccessRole = new iam.Role(this, 'AppRunnerECRAccessRole', {
  assumedBy: new iam.ServicePrincipal('build.apprunner.amazonaws.com'),
  managedPolicies: [
    iam.ManagedPolicy.fromAwsManagedPolicyName(
    'service-role/AWSAppRunnerServicePolicyForECRAccess',
    ),
  ]
})

次にapprunnerを作成します。sourceConfigurationのみ必須の項目となります。

new apprunner.CfnService(this, 'AppRunnerService', {
      serviceName: 'apprunner-cdk-demo',
      healthCheckConfiguration: {
        protocol: 'HTTP',
        path: '/',
      },
      sourceConfiguration: {
        authenticationConfiguration: {
          accessRoleArn: ecrAccessRole.roleArn,
        },
        imageRepository: {
          imageRepositoryType: 'ECR',
          imageIdentifier: repository.repositoryUriForTag('latest'),
          imageConfiguration: {
            port: '3000',
          },
        },
        autoDeploymentsEnabled: true,
      },
      instanceConfiguration: {
        cpu: '1024',
        memory: '2048',
      },
})

ここまでできたらもう一度cdk dpeloyを実行します。
デフォルトドメインからHello Worldが実行できたら完了です!

まとめ

3パターンでデプロイしましたが、簡単ですばやく環境を構築することができました。早く公開したい時やインフラにそこまで詳しくない方が使用するにはとても便利なサービスだと思います。

Discussion