AWS CDKを使用してECS Fargate構築する
公開日
2025年1月19日
更新日
2025年1月19日
17
分で読める
AWS
この記事では、AWS Cloud Development Kit(CDK)を使用してECS Fargateベースのサービスを構築する方法を詳しく解説します。VPC構成、アプリケーションロードバランサー(ALB)の作成、タスク定義、自動スケーリング設定などを含みます。
プロジェクト背景
目標は、AWS ECS Fargateを基盤とするコンテナ化されたサービスをデプロイし、アプリケーションロードバランサー(ALB)を通じて外部に公開することです。本サービスには以下の特徴があります:
- プライベートVPCとセキュリティグループでネットワークセキュリティを保護する。
- ECRエンドポイントを設定し、プライベートサブネットから安全にコンテナイメージをプルする。
- CPUおよびメモリ負荷に基づいて動的にサービスインスタンスを調整する自動スケーリングを設定する。
前提条件
本サンプルコードを用いてECS Fargateを構築するには、以下の条件が満たされている必要があります:
- VPC(publicサブネット、privateサブネット、privateセキュリティグループを含む)を既に作成済み。
- ECSTaskExecutionRoleとECSTaskRoleを既に作成済み。
- ECRリポジトリを既に作成済み。
- サーバーコードをDockerイメージにパッケージ化し、ECRにプッシュ済み。
CDKコード
以下はCDKスタックのコード例です。
import * as cdk from 'aws-cdk-lib';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import { Cluster, ContainerImage, LogDriver } from 'aws-cdk-lib/aws-ecs';
import { ApplicationLoadBalancedFargateService } from 'aws-cdk-lib/aws-ecs-patterns';
import { ApplicationLoadBalancer, ApplicationProtocol } from 'aws-cdk-lib/aws-elasticloadbalancingv2';
import { Role } from 'aws-cdk-lib/aws-iam';
import { LogGroup, RetentionDays } from 'aws-cdk-lib/aws-logs';
import { Construct } from 'constructs';
export class ServerStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// VPC ID
const vpcId = 'xxx';
// プライベートセキュリティグループID
const privateSecurityGroupId = 'xxx';
// ECSタスク実行ロールARN
const ecsTaskExecutionRoleArn = 'xxx';
// ECSタスクロールARN
const ecsTaskRoleArn = 'xxx';
// ECRリポジトリURI
const ecrRepositoryURI = 'xxxxxx';
// ECRイメージタグ
const ecrImageTag = 'xxxxxx';
// -------------------------------------------
// VPCを取得する
const vpc = ec2.Vpc.fromLookup(this, 'VPC', { vpcId: vpcId });
// Private Security Groupを取得する
const privateSecurityGroup = ec2.SecurityGroup.fromLookupById(this, 'PrivateSecurityGroup', privateSecurityGroupId);
// ECR DKRエンドポイントを作成する(プライベートサブネット内でECRから安全にイメージをプル)
new ec2.InterfaceVpcEndpoint(this, `EcrDkrEndpoint`, {
vpc: vpc,
service: ec2.InterfaceVpcEndpointAwsService.ECR_DOCKER,
subnets: { subnets: vpc.privateSubnets },
securityGroups: [privateSecurityGroup],
});
// ECR API Interface Endpointを作成する(プライベートサブネット内でECR APIに安全にアクセス)
new ec2.InterfaceVpcEndpoint(this, `EcrApiEndpoint`, {
vpc: vpc,
service: ec2.InterfaceVpcEndpointAwsService.ECR,
subnets: { subnets: vpc.privateSubnets },
securityGroups: [privateSecurityGroup],
});
// ECR API Interface Endpointを作成する(プライベートサブネットからCloudWatchにログを吐き出す)
new ec2.InterfaceVpcEndpoint(this, `EcrLog`, {
vpc: vpc,
service: ec2.InterfaceVpcEndpointAwsService.CLOUDWATCH_LOGS,
subnets: { subnets: vpc.privateSubnets },
securityGroups: [privateSecurityGroup],
});
// ECS Task Execution Roleを取得する
const ecsTaskExecutionRole = Role.fromRoleArn(this, `EcsTaskExecutionRole`, ecsTaskExecutionRoleArn, {
mutable: false,
});
// ECS Task Roleを取得する
const ecsTaskRole = Role.fromRoleArn(this, `EcsTaskRole`, ecsTaskRoleArn, {
mutable: false,
});
// ALBを作成する
const alb = new ApplicationLoadBalancer(this, `ALB`, {
vpc: vpc,
vpcSubnets: { subnets: vpc.publicSubnets },
internetFacing: true,
loadBalancerName: `<ALB_NAME>`,
securityGroup: privateSecurityGroup,
idleTimeout: cdk.Duration.seconds(60),
});
// Log Groupを作成する
const ecsLogGroup = new LogGroup(this, `LogGroup`, {
logGroupName: '<LOG_GROUP_NAME>',
retention: RetentionDays.ONE_MONTH,
});
// ECS Clusterを作成する
const cluster = new Cluster(this, `ECSCluster`, {
vpc: vpc,
clusterName: `<CLUSTER_NAME>`,
});
// ECS Serviceを作成する
const service = new ApplicationLoadBalancedFargateService(this, `ECSService`, {
cluster: cluster,
protocol: ApplicationProtocol.HTTP,
targetProtocol: ApplicationProtocol.HTTP,
securityGroups: [privateSecurityGroup],
listenerPort: 80,
loadBalancer: alb,
openListener: false,
publicLoadBalancer: false,
taskSubnets: { subnets: vpc.privateSubnets },
memoryLimitMiB: 1024,
cpu: 512,
// デフォルトで起動するタスクの数
desiredCount: 2,
// サービスの更新やスケーリング中に、健康なタスクの最小割合を維持します。
// たとえば、desiredCount が 2 で minHealthyPercent が 50% の場合、更新中に少なくとも 1 つの健康なタスクが実行され続ける必要があります。
minHealthyPercent: 50,
// サービスの更新やスケーリング中に、実行可能な健康タスクの最大割合を設定します。
// たとえば、desiredCount が 2 で maxHealthyPercent が 200% の場合、更新中に最大 4 タスク(現在の 2 タスク + 新規 2 タスク)が同時に実行される可能性があります。
maxHealthyPercent: 200,
healthCheckGracePeriod: cdk.Duration.seconds(500),
taskImageOptions: {
image: ContainerImage.fromRegistry(`${ecrRepositoryURI}:${ecrImageTag}`),
logDriver: LogDriver.awsLogs({
logGroup: ecsLogGroup,
streamPrefix: 'api',
}),
executionRole: ecsTaskExecutionRole,
taskRole: ecsTaskRole,
// serverポート番号
containerPort: 3000,
// 環境変数
environment: {
XX: 'XX',
},
},
serviceName: `<SERVICE_NAME>`,
});
// Health Checkを設定する
service.targetGroup.configureHealthCheck({
path: '<health check path in your server>', // サーバーヘルスチェック用のAPIパス
});
// Auto Scalingの設定
const autoScaling = service.service.autoScaleTaskCount({
minCapacity: 1, // ECSサービス内のタスクインスタンスの最小数
maxCapacity: 10, // ECSサービス内のタスクインスタンスの最大数
});
// scaleOnCpuUtilization と scaleOnMemoryUtilization:
// - CPU使用率とメモリ使用率に基づいて、自動スケーリングのルールを設定します。
// - 使用率が目標値(targetUtilizationPercent: 60)を超えると、タスクが自動的にスケールアウトされます。
// - 使用率が目標値を下回ると、タスクが自動的にスケールインされます。
//
// scaleInCooldown と scaleOutCooldown:
// - スケールイン・スケールアウトのクールダウン時間を設定し、頻繁なスケーリングによる揺らぎを回避します。
// - 60秒に設定すると、直近のスケール操作から最低60秒間は次のスケーリングが行われません。
autoScaling.scaleOnCpuUtilization(`ScalingOnCPU`, {
targetUtilizationPercent: 60,
scaleInCooldown: cdk.Duration.seconds(60),
scaleOutCooldown: cdk.Duration.seconds(60),
});
autoScaling.scaleOnMemoryUtilization(`ScalingOnMemory`, {
targetUtilizationPercent: 60,
scaleInCooldown: cdk.Duration.seconds(60),
scaleOutCooldown: cdk.Duration.seconds(60),
});
}
}
まとめ
このコードは、AWS CDKを使用して、ECS Fargateサービスのネットワーク構成、セキュリティ、ロードバランシング、ログ記録、自動スケーリングを包括的に設定する方法を示しています。このようなInfrastructure as Code(IaC)のアプローチは、デプロイ効率と一貫性を大幅に向上させます。
この記事が、AWS CDKを使用して独自のコンテナサービスを構築する際の参考になれば幸いです。コード実装やデプロイプロセスに関して質問がありましたら、ぜひコメントでお知らせください!