先輩がノリで作ったAWSをちゃんとコード化してみた4 (ECS clusterとserviceのコード化)

## コード化中
1. [AWS CDKの導入](https://qiita.com/emi_yoshi/items/d775cd80e44095d468f1)
1. [CodePipelineのコード化](https://qiita.com/emi_yoshi/items/9e178c975cece3a5ad1b)
1. [EventBridge Schedulerのコード化](https://qiita.com/emi_yoshi/items/f19044eb0ac843e6cfca)
1. ECS clusterとserviceのコード化(この記事)
1. [ElastiCacheのコード化、本番環境構築](https://qiita.com/emi_yoshi/items/93913cca44658067cd98)

## ECS service
サービス作成箇所です
Fargateを使用しています
```Kotlin:rescources/api/Ecs.kt
fun createService() {
        val securityGroupId = context["apiSecurityGroup"].toString()
        val securityGroup = SecurityGroup.fromLookupById(scope, "developmentMyappApiCdkSecurityGroup", securityGroupId)

        val subnet1 = context["apiSubnet1"].toString()
        val subnet2 = context["apiSubnet2"].toString()
        val routeTableId = context["apiRouteTable"].toString()
        val vpcSubnets = SubnetSelection.builder()
            .subnets(listOf(
                Subnet.fromSubnetAttributes(
                    scope,
                    "developmentMyappApiCdkSubnet1",
                    SubnetAttributes.builder()
                        .subnetId(subnet1)
                        .routeTableId(routeTableId)
                        .build()),
                Subnet.fromSubnetAttributes(
                    scope,
                    "developmentMyappApiCdkSubnet2",
                    SubnetAttributes.builder()
                        .subnetId(subnet2)
                        .routeTableId(routeTableId)
                        .build())
            ))
            .build()

        service = FargateService.Builder
            .create(scope, "developmentMyappApiCdkEcsService")
            .serviceName("api")
            .cluster(ecsCluster)
            .taskDefinition(taskDefinition)
            .desiredCount(1)
            .minHealthyPercent(100)
            .maxHealthyPercent(200)
            .securityGroups(listOf(securityGroup))
            .vpcSubnets(vpcSubnets)
            .healthCheckGracePeriod(Duration.minutes(0))
            .enableEcsManagedTags(true)
            .assignPublicIp(true)
            .build()

        val albTargetGroupArn = context["apiAlbTargetGroupArn"].toString()
        val albTargetGroup = ApplicationTargetGroup.fromTargetGroupAttributes(
            scope,
            "developmentMyappApiCdkAlbTargetGroup",
            TargetGroupAttributes.builder()
                .targetGroupArn(albTargetGroupArn)
                .build()
        )

        service.attachToApplicationTargetGroup(albTargetGroup)
    }
```

## ECS cluster
service作成時にclusterが必要になるので全体に共通するリソース用のstackを追加してclusterを作成します
vpcはすでに作成してあるリソースを取得するようにします
```Kotlin:InfraStack.kt
class InfraStack(
    parent: Construct, name: String, stackProps: StackProps
): Stack(parent, name, stackProps) {
    var ecsCluster: Cluster

    init {
        val context = this.getNode().tryGetContext("development") as Map<String, Any>

        val vpc = Vpc(this, context)
        vpc.getVpc()

        val ecs = Ecs(this, context)
        ecs.createCluster(vpc.vpc)
        ecsCluster = ecs.cluster
    }
}
```

```Kotlin:recources/infra/Vpc.kt
class Vpc(val scope: Construct, val context: Map<String, Any>) {
    lateinit var vpc: IVpc

    fun getVpc() {
        val vpcId = context["vpcId"].toString()
        vpc = Vpc.fromLookup(
            scope,
            "developmentMyappAdminCdkVpc",
            VpcLookupOptions.builder()
                .vpcId(vpcId)
                .build()
        )
    }
}
```

```Kotlin:recources/infra/Ecs.kt
class Ecs(val scope: Construct, val context: Map<String, Any>) {
    lateinit var cluster: Cluster

    fun createCluster(vpc: IVpc) {
        cluster = Cluster.Builder
            .create(scope, "developmentMyappInfraCdkCluster")
            .clusterName("developmentMyappInfraCdkCluster")
            .containerInsights(true)
            .vpc(vpc)
            .enableFargateCapacityProviders(true)
            .build()
    }
}
```

vpc取得時のLookUpを実行するために `cdk ls` などを実行した際に作成される `cdk.context.json` をコミットします

## 管理画面用のリポジトリ
API用とほぼ同じなのでcdk.jsonの定義追加してあとはほぼコピペです

## 依存関係
全体に共通するリソース用のStackを作成したので、stack間に依存関係ができました
依存関係が守られるように `DeployStage.kt` で `addDependency` を設定します
```Kotlin:DeployStage.kt
val infraStack = InfraStack(this, "developmentMyappInfraCdkInfraDeployStack", stackProps)

AdminStack(this, "developmentMyappInfraCdkAdminDeployStack",
    stackProps,
    infraStack.ecsCluster)
.addDependency(infraStack)

ApiStack(this, "developmentMyappInfraCdkApiDeployStack",
    stackProps,
    infraStack.ecsCluster)
.addDependency(infraStack)

BatchStack(this, "developmentMyappInfraCdkBatchDeployStack",
    stackProps,
    infraStack.ecsCluster.getClusterArn())
.addDependency(infraStack)
```

これをデプロイすると、InfraStackが先に実行され、ApiStack、AdminStack、BatchStackはInfraStackが完了してから並列で実行されるようになります

![スクリーンショット 2023-06-12 18.16.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457018/aca77d91-bbbe-f0f9-eeb7-4fd3786db00e.png)

## 次回
ElastiCacheのコード化と本番環境の構築です