AWS CDK と ecspresso の組合せで AutoScaling までうまいことやる

2023/08/11に公開

CDK大好きちゃちいです。こんにちは。
これまではECS(Fargate)のデプロイもCDKで行っていたんですが、依存やら時間やらが気になるため、ecspressoを組み合わせてみたのですが、AutoScalingで躓いたのでその解決までの記録です

もし、もっといいやり方があるぞ!!という場合はお知らせください…

2023/08/10 時点

構成

  • Fargate クラスタはCDK、サービスとタスクはecspressoでデプロイしたい
  • AutoScalingはややこしいのでCDKで定義したい
  • インフラ構築リポジトリ (AWS CDK)
    • CloudFront, Fargateクラスタ, ECR その他諸々を構築
    • ecspressoでデプロイするために SSM Parameter Store に必要な情報を出力する
  • アプリケーションリポジトリ
    • Dockerコンテナで実行
    • こちらからインフラ構築リポジトリで用意したECRへPushしecspressoでサービスとタスクをデプロイしたい
    • ecspressoのSSMプラグインを使い、Paramter Storeから読み込んでテンプレートファイルで呼び出す

躓いたところ

CDKのみで構築するのであれば、FargateService(サービス) の autoScaleTaskCount() メソッドから ScalableTaskCount を作り、scaleOnSchedule()scaleOnMetric()を使います。

今回は、サービスはecspressoでデプロイするため、FargateService.fromFargateServiceArn()を使えばええやろ、と思っていました。

IFargateService には AutoScale のメソッドは無い

つまり、以下のようなことはできません。

// ecspresso でデプロイされるサービスを取得
const serviceArn = `arn:aws:ecs:ap-northeast-1:${props.account}:service/${cluster.clusterName}/hoge-service`;
const service = FargateService.fromFargateServiceArn(this, 'FargateService', serviceArn);

// オートスケールの設定(をしたかった)
const autoScale = service.autoScaleTaskCount({
	maxCapacity: 4,
	minCapacity: 2,
});
// ...略 autoScale.scaleOnSchedule() など

ではどうするか?

autoScaleTaskCount() がどこにどう実装されているか見てみる

基底クラスの BaseService に実装されていました

https://github.com/aws/aws-cdk/blob/916f94dd6427516833c5796ed06aa3fa021972c8/packages/aws-cdk-lib/aws-ecs/lib/base/base-service.ts#L1088-L1100

どうも ScalableTaskCount を new してあげれば良さそうで、パラメータも特別なものは無さそうです

コピーしてペー

makeAutoScalingRole() によってロールも作成しているので、該当の実装も持ってきてしまいます

https://github.com/aws/aws-cdk/blob/916f94dd6427516833c5796ed06aa3fa021972c8/packages/aws-cdk-lib/aws-ecs/lib/base/base-service.ts#L1325-L1333

// ecspresso でデプロイされるサービスを取得
const serviceArn = `arn:aws:ecs:ap-northeast-1:${props.account}:service/${cluster.clusterName}/v2web`;
const service = FargateService.fromFargateServiceArn(this, 'FargateService', serviceArn);

const autoScale = new ScalableTaskCount(this, 'AutoScaling', {
	maxCapacity: 4,
	minCapacity: 2,
	serviceNamespace: ServiceNamespace.ECS,
	dimension: 'ecs:service:DesiredCount',
	resourceId: `service/${cluster.clusterName}/hoge-service`,
	role: iam.Role.fromRoleArn(this, 'ScalingRole', Stack.of(this).formatArn({
		region: '',
		service: 'iam',
		resource: 'role/aws-service-role/ecs.application-autoscaling.amazonaws.com',
		resourceName: 'AWSServiceRoleForApplicationAutoScaling_ECSService',
	})),
})
// ...略 autoScale.scaleOnSchedule() など

これで ScalableTaskCount の作成ができました

なお、お察しかと思いますが、サービスが作成されていない場合には 「そんな resourceId ないぞ」というエラーでデプロイできないため、ecspressoで1回はサービスを作る必要があります
「初回実行かどうか(サービスが無いものとみなす)」オプション(--context)などを用意すると良いでしょう

メトリックによるオートスケール

scaleOnMetric() というメトリックに応じたオートスケールを定義できるメソッドがあります
props に metric があり、CDKでサービスを定義した場合、以下のように指定できます

const service = new FargateService(/*...略...*/);
const autoScale = service.autoScaleTaskCount(/*...略...*/);

// CPU使用率を見てスケール
autoScale.scaleOnMetric('ScaleOnMetricByCpuUtilization', {
	metric: serivce.metricCpuUtilization({
		statistic: 'Maximum',
                // 注: 名前空間が AWS/ から始まるメトリクスは1分以上でないといけない
                period: cdk.Duration.seconds(60),
	}),
	// ... 略
});

CPU使用率の metricMemoryUtilization() とメモリ使用率の metricMemoryUtilization() が定義されていますが、前述のオートスケール周りと同様に、IFargateServiceには実装されていません

metricCpuUtilization() がどこにどう実装されているか見てみる

こちらも、BaseServiceに実装されています
https://github.com/aws/aws-cdk/blob/916f94dd6427516833c5796ed06aa3fa021972c8/packages/aws-cdk-lib/aws-ecs/lib/base/base-service.ts#L1195-L1220

metric() メソッドを呼び出しているだけで、この実装をコピペでいけそうです

コピーしてペー

// CPU使用率を見てスケール
autoScale.scaleOnMetric('ScaleOnMetricByCpuUtilization', {
	metric: new aws_cloudwatch.Metric({
                namespace: 'AWS/ECS',
                metricName: 'CPUUtilization',
                dimensionsMap: { 
			ClusterName: cluster.clusterName,
			ServiceName: 'hoge-service'
		},
                statistic: 'Maximum',
                // 注: 名前空間が AWS/ から始まるメトリクスは1分以上でないといけない
                period: Duration.seconds(60),
            }).attachTo(this),
	// ... 略
});

これで、CDKだけで構築したときと同等になったはずです

まとめ

  • もうちょっと整理して実装もできると思うのですが、動くもの(デプロイできるもの)としてはこれくらいでいいかなと
  • AWS CDKはいいぞ
  • ecspressoはいいぞ

Discussion