こんにちは、つくぼし(tsukuboshi0755)です!
最近案件でCDK(TypeScript)を使うため、色んなAWSリソースをCDKで書けるように絶賛勉強中の身です。
今回は何番煎じになるか分からないですが、CDKv2を使って、ECSとECRのコンテナ構成を実装してみます!
前提条件
今回は以下の通り、CDKv2を使ってコードを書いていきます。
$ cdk version
2.83.1 (build 006b542)
またDockerクライアントとしては、Rancher Desktopを使用します。
$ rdctl version
rdctl client version: 1.1.0, targeting server version: v1
全体構成
今回はVPC+ALB+ECS(Fargate)+ECRの構成を作成し、nginxコンテナをパブリック公開します。
なおDockerfileを用いてビルドしたイメージをECRにプッシュし、そのイメージを用いてECSタスクを起動する処理についても、全てCDKコードで自動化します。
リポジトリ
コード全体については、以下のリポジトリに格納していますのでご参照ください。
tsukuboshi/cdk-microservices-template
コード解説
CDKコードの中核となるlib/cdk-microservices-template-stack.ts
について説明します。
AWSアカウントID/リージョン/リソース名の設定
lib/cdk-microservices-template-stack.ts
// Get AWS Account ID and Region
const { accountId, region } = new ScopedAws(this);
// Set Resource Name
const resourceName = "test";
ScopedAws
を用いて、ECRリポジトリURLの設定に必要なAWSアカウントIDとリージョンを取得します。
またAWSリソース全体で使用するリソース名についても、ここで任意の名前を設定します。
ECR作成/イメージプッシュ実施
lib/cdk-microservices-template-stack.ts
// Create ECR Repository
const ecrRepository = new ecr.Repository(this, "EcrRepo", {
repositoryName: `${resourceName}-ecr-repo`,
removalPolicy: RemovalPolicy.DESTROY,
autoDeleteImages: true,
});
// Create Docker Image Asset
const dockerImageAsset = new DockerImageAsset(this, "DockerImageAsset", {
directory: path.join(__dirname, "..", "app"),
platform: Platform.LINUX_AMD64,
});
// Deploy Docker Image to ECR Repository
new ecrdeploy.ECRDeployment(this, "DeployDockerImage", {
src: new ecrdeploy.DockerImageName(dockerImageAsset.imageUri),
dest: new ecrdeploy.DockerImageName(
`${accountId}.dkr.ecr.${region}.amazonaws.com/${ecrRepository.repositoryName}:latest`
),
});
今回のECRリポジトリは検証用のため、autoDeleteImages
をtrueで設定し、リポジトリにイメージが存在する場合でも削除できるようにしています。
またDockerImageAsset
を用いて、appディレクトリ内に存在するDockerfileを元に、イメージをビルドします。
さらにcdk-ecr-deployment
を用いて、ビルドしたイメージにlatastタグを付与してECRリポジトリにプッシュします。
なおcdk-ecr-deployment
はcdklabs
モジュールでの提供になるため、別途npmでインストールする必要がありますのでご注意ください。
- class DockerImageAsset (construct) · AWS CDK
- cdklabs/cdk-ecr-deployment: A CDK construct to deploy docker image to Amazon ECR
VPC作成
lib/cdk-microservices-template-stack.ts
// Create VPC and Subnet
const vpc = new ec2.Vpc(this, "Vpc", {
vpcName: `${resourceName}-vpc`,
maxAzs: 2,
ipAddresses: ec2.IpAddresses.cidr("10.0.0.0/20"),
subnetConfiguration: [
{
cidrMask: 24,
name: `${resourceName}-public`,
subnetType: ec2.SubnetType.PUBLIC,
},
],
});
ECSクラスターにVPCの指定が必要になるため、事前にVPCを作成します。
今回はVPC内にパブリックサブネットを2つ作成し、コンテナを作成したサブネット内で稼働させます。
ALB/ECS作成
lib/cdk-microservices-template-stack.ts
// Create ECS Cluster
const cluster = new ecs.Cluster(this, "EcsCluster", {
clusterName: `${resourceName}-cluster`,
vpc: vpc,
});
// Create CloudWatch Log Group
const logGroup = new logs.LogGroup(this, "LogGroup", {
logGroupName: `/aws/ecs/${resourceName}`,
removalPolicy: RemovalPolicy.DESTROY,
});
// Create ALB and ECS Fargate Service
const service = new ecs_patterns.ApplicationLoadBalancedFargateService(
this,
"FargateService",
{
loadBalancerName: `${resourceName}-lb`,
publicLoadBalancer: true,
cluster: cluster,
serviceName: `${resourceName}-service`,
cpu: 256,
desiredCount: 2,
memoryLimitMiB: 512,
assignPublicIp: true,
taskSubnets: { subnetType: ec2.SubnetType.PUBLIC },
taskImageOptions: {
family: `${resourceName}-taskdef`,
containerName: `${resourceName}-container`,
image: ecs.ContainerImage.fromEcrRepository(ecrRepository, "latest"),
logDriver: new ecs.AwsLogDriver({
streamPrefix: `container`,
logGroup: logGroup,
}),
},
}
);
事前にECSクラスターとロググループを作成した後、ecs_patterns.ApplicationLoadBalancedFargateService
モジュールを用いて、ALB/ECSサービス及び関連リソースをまとめて作成します。
今回はパブリックサブネット内にALB及びECSサービスを作成するため、publicLoadBalancer
とassignPublicIp
はtrueで設定します。
またtaskImageOption
のimage
にて、ECRリポジトリにプッシュしたlatestタグを持つイメージを指定し、タスクを起動させます。
さらにtaskImageOptions
のlogDriver
にて、タスクのログを事前に作成したロググループへ出力するように設定します。
動作確認
CDKコードのデプロイを行う事で、ECSタスクが正常に起動するか確認します。
初めに、必要なnpmパッケージのインストールとCDK実行環境の整備を事前に実施しておきます。
$ git clone https://github.com/tsukuboshi/cdk-microservices-template
$ cd cdk-microservices-template
$ npm install
$ cdk bootstrap
またイメージビルドに必要なDockerクライアントを事前に起動させてください。
Rancher Desktopを使用している場合は、以下のコマンドで起動できます。
$ rdctl start
準備が整いましたら、以下の通りCDKコードをデプロイします。
変更セットを確認し、問題がなければyを入力します。
正常にデプロイが完了すると、アウトプットにALBのDNS名とECSサービスのURLが表示されます。
$ cdk deploy
(中略)
Do you wish to deploy these changes (y/n)? y
(中略)
Outputs:
CdkMicroservicesTemplateStack.FargateServiceLoadBalancerDNSXXXXXXXX = <ALBのDNS名>
CdkMicroservicesTemplateStack.FargateServiceServiceURLXXXXXXXX = <ECSサービスURL>
curlコマンドでALBのDNS名にアクセスすると、以下の通りnginxのデフォルトページが表示されました!
$ curl <ALBのDNS名>
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
最後に
今回はCDKv2を使って、ECSとECRのコンテナ構成を実装してみました。
DockerImageAsset
モジュールとcdk-ecr-deployment
モジュールを組み合わせる事で、CDKコード内でイメージのビルドとECRへのプッシュが自動化できるのは非常に便利ですね。
またL2のaws_ecs_patterns
モジュールを使用する事で、ECSに必要なリソースを作成するためのコード記述量を減らせるので、ECS構成をCDKで書く場合は積極的に利用すると良さそうです。
ぜひCDKでECS構成を実装する際に、参考にしてみてください。
以上、つくぼし(tsukuboshi0755)でした!