AWS なんもわからん状態はまずいなと思い、最近話題の aws-cdk を使って Next.js のコンテナを AWS ECS(on Fargate)上にデプロイしてみた。 備忘録として過程をメモっておく。
aws-cdk とは
- AWS のクラウドインフラストラクチャをコードとして定義できる
- 定義したコードをもとに AWS CloudFormation を通じてインフラをデプロイ(リソース作成)することができる
やったことログ
Dockerfile の作成と ECR への push
公式に Docker Image のサンプルがあるのでそのまま使った。
experimental の outputStandalone
を有効にすると、production 環境に必要なファイルのみをコピーした .next/standalone
が作成される。
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 への理解が深まった。コードなのであとから見返すこともできて良い。