🐾
AWS CDK で ECS Fargate を プライベートサブネット に構築する
はじめに
AWS CDK (TypeScript) を使用して、VPC、ALB、ECS (Fargate)、ECR の簡単な Webアプリの構成を作成しました。なお、ECS は プライベートサブネット に配置しますが、NATゲートウェイは使用せず、外部アクセスを VPCエンドポイント経由 で行うようにしました。
構成イメージは下記になります。
前提
- CDK 実行環境が整備されている
- Dockerfile が作成済み
事前準備:コンテナレジストリ へ イメージをプッシュ
CDK 実行前に、ECRリポジトリの作成/イメージのビルドとプッシュをしておきます。
手順は下記の通りです。
1. ECRリポジトリの作成
- ECR (Elastic Container Registry) に移動します。
- 「リポジトリの作成」をクリックし、リポジトリ名を入力します。
- 必要に応じて設定を調整し、「リポジトリの作成」をクリックします。
2. Dockerイメージのビルドとプッシュ
- Dockerfileを作成し、アプリケーションのDockerイメージをビルドします。
docker build -t <リポジトリ名>:<タグ> .
- ECRにログインします。
aws ecr get-login-password --region <リージョン> | docker login --username AWS --password-stdin <アカウントID>.dkr.ecr.<リージョン>.amazonaws.com
- イメージをECRにプッシュします。
docker tag <リポジトリ名>:<タグ> <アカウントID>.dkr.ecr.<リージョン>.amazonaws.com/<リポジトリ名>:<タグ> docker push <アカウントID>.dkr.ecr.<リージョン>.amazonaws.com/<リポジトリ名>:<タグ>
CDK スタックのデプロイ
ckd deploy
コマンドでCDK スタックをデプロイします。
CDKコードの中核となる\lib\cdk-study-stack.ts
は下記になります🖊️
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as ecs from 'aws-cdk-lib/aws-ecs';
import * as ecr from 'aws-cdk-lib/aws-ecr';
import * as logs from 'aws-cdk-lib/aws-logs';
import * as ecsp from 'aws-cdk-lib/aws-ecs-patterns';
import { RemovalPolicy } from 'aws-cdk-lib';
export class CdkStudyStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// 変数定義
const projectName = "sample";
const envName = "dev";
const cidr = '10.0.0.0/16';
const repoName = "myapp";
const imageTag = "v1";
// VPCの作成
const vpc = new ec2.Vpc(this, `${projectName}-${envName}-vpc`, {
cidr: cidr,
maxAzs: 2,
subnetConfiguration: [
{
cidrMask: 24,
name: `${projectName}-${envName}-public`,
subnetType: ec2.SubnetType.PUBLIC,
},
{
cidrMask: 24,
name: `${projectName}-${envName}-private`,
subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
},
]
});
// VPCエンドポイントの追加
vpc.addInterfaceEndpoint(`${projectName}-${envName}-ecr-api-vpce`, {
service: ec2.InterfaceVpcEndpointAwsService.ECR
});
vpc.addInterfaceEndpoint(`${projectName}-${envName}-ecr-dkr-vpce`, {
service: ec2.InterfaceVpcEndpointAwsService.ECR_DOCKER
});
vpc.addInterfaceEndpoint(`${projectName}-${envName}-logs-vpce`, {
service: ec2.InterfaceVpcEndpointAwsService.CLOUDWATCH_LOGS
});
vpc.addGatewayEndpoint(`${projectName}-${envName}-s3-vpce`, {
service: ec2.GatewayVpcEndpointAwsService.S3,
subnets: [
{
subnets: vpc.isolatedSubnets
}
]
});
// CloudWatch Logsのロググループを作成
const logGroup = new logs.LogGroup(this, `${projectName}-${envName}-log-gp`, {
logGroupName: `/aws/ecs/${projectName}-${envName}`,
removalPolicy: RemovalPolicy.DESTROY,
});
// ECRリポジトリの取得
const repo = ecr.Repository.fromRepositoryName(this, `${projectName}-${envName}-repo`, repoName);
// ECSクラスターの作成
const cluster = new ecs.Cluster(this, `${projectName}-${envName}-cluster`, { vpc });
// ALB + Fargateサービスの作成
new ecsp.ApplicationLoadBalancedFargateService(this, `${projectName}-${envName}-service`, {
cluster,
memoryLimitMiB: 512,
desiredCount: 2,
cpu: 256,
assignPublicIp: true,
loadBalancerName: `${projectName}-${envName}-lb`,
publicLoadBalancer: true,
taskImageOptions: {
family: `${projectName}-${envName}-taskdef`,
image: ecs.ContainerImage.fromEcrRepository(repo, imageTag),
containerPort: 8080,
logDriver: new ecs.AwsLogDriver({
streamPrefix: `container`,
logGroup: logGroup,
})
},
});
}
}
確認
デプロイすると、無事クラスター内でタスクが実行されていることを確認できました🙆
-
ECS に必要な VPCエンドポイント が作成されている
-
CloudWatch logs へログが出力されている
-
ALB の DNS名へアクセスするとアプリケーションが表示される
-
タスク定義に適切なタスク実行ロールが付与されている
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"ecr:BatchCheckLayerAvailability",
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer"
],
"Resource": "arn:aws:ecr:<リージョン>:<アカウントID>:repository/${repoName}",
"Effect": "Allow"
},
{
"Action": "ecr:GetAuthorizationToken",
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:<リージョン>:<アカウントID>:log-group:/aws/ecs/${projectName}-${envName}:*",
"Effect": "Allow"
}
]
}
以上、どなたかの参考になれば幸いです。
えみり〜でした|ωΦ)ฅ
Discussion