2022/04/30

aws-cdkでNext.jsを動かすためのECS環境を構築したメモ

Yuta Takahashi


AWS なんもわからん状態はまずいなと思い、最近話題の aws-cdk を使って Next.js のコンテナを AWS ECS(on Fargate)上にデプロイしてみた。 備忘録として過程をメモっておく。

aws-cdk とは

  • AWS のクラウドインフラストラクチャをコードとして定義できる
  • 定義したコードをもとに AWS CloudFormation を通じてインフラをデプロイ(リソース作成)することができる

やったことログ

Dockerfile の作成と ECR への push

公式に Docker Image のサンプルがあるのでそのまま使った。

experimental の outputStandalone を有効にすると、production 環境に必要なファイルのみをコピーした .next/standalone が作成される。

  • Deployment | Next.js
  • Output File Tracing | Next.js

CloudFormation

【CloudFormation 入門】5 分と 6 行で始める AWS CloudFormation テンプレートによるインフラ構築 | DevelopersIO を読んで CloudFormation の概要をを頭に入れて用語を整理した。

  • テンプレート:yaml or json で記述するインフラ定義のファイル
  • スタック:テンプレートから作成/破棄をまとめて実行するリソースの集合
    • 1 つのスタック内に VPC, EC2, S3, RDS などのプロダクトを 1 つ動かすために必要なリソースをまとめて定義するkとができる

aws-cdk インフラを構築

セットアップ

$ npm i -g aws-cdk cdk init app --language=typescript

CdkStack を定義する

ECS を動かすための最小限の Stack を定義した。

import { Stack, StackProps } from 'aws-cdk-lib'; import { Construct } from 'constructs'; import { Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam'; import { TaskDefinition, Compatibility, ContainerImage, AwsLogDriver, Protocol, Cluster } from 'aws-cdk-lib/aws-ecs'; import { Vpc } from 'aws-cdk-lib/aws-ec2'; import { Repository } from 'aws-cdk-lib/aws-ecr'; import { ApplicationLoadBalancedFargateService } from 'aws-cdk-lib/aws-ecs-patterns'; export const defaultProps: StackProps = { env: { region: 'ap-northeast-1', account: process.env.AWS_ACCOUNT, }, }; export class CdkStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); const taskRole = new Role(this, 'ecs-task-role', { assumedBy: new ServicePrincipal('ecs-tasks.amazonaws.com'), }); const taskDefinition = new TaskDefinition(this, 'ecs-task-definition', { taskRole, executionRole: taskRole, compatibility: Compatibility.FARGATE, cpu: '512', memoryMiB: '1024', }); const repository = Repository.fromRepositoryArn(this, 'repo', process.env.ECR_ARN); const container = taskDefinition.addContainer('ecs-task-container', { image: ContainerImage.fromEcrRepository(repository), logging: new AwsLogDriver({ streamPrefix: 'ecs-task-log-prefix', }), }); container.addPortMappings({ containerPort: 3000, hostPort: 3000, protocol: Protocol.TCP, }); const vpc = new Vpc(this, 'ecs-task-vpc', { maxAzs: 2, natGateways: 1, }); const cluster = new Cluster(this, 'ecs-task-cluster', { vpc }); new ApplicationLoadBalancedFargateService(this, 'FargateService', { cluster, cpu: 512, desiredCount: 2, taskDefinition, memoryLimitMiB: 512, publicLoadBalancer: true, }); } }

デプロイ

以下を実行するとインフラが構築できた。

$ yarn build $ yarn cdk bootstrap $ yarn cdk deploy

感想

  • CloudFormation だとクソ長 yaml を書くのがしんどそうだったが、cdk だと TypeScript で数十行でかけたので開発体験が良かった
  • AWS コンソールでポチポチ設定したことがあるが、それと比較すると、どのリソースをどのように使うのかを cdk だと明確に書けるので AWS への理解が深まった。コードなのであとから見返すこともできて良い。

参考