先輩がノリで作ったAWSをちゃんとコード化してみた5 (ElastiCacheのコード化、本番環境構築)

## コード化中
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のコード化](https://qiita.com/emi_yoshi/items/62311276cb84ca7e4db6)
1. ElastiCacheのコード化、本番環境構築(この記事)

## ElastiCache
L2がないのでL1のCfnReplicationGroupを使用します
```Kotlin
fun createElastiCache(vpc: IVpc) {
    val subnets: List<ISubnet> = vpc.getPublicSubnets() + vpc.getPrivateSubnets() + vpc.getIsolatedSubnets()
    var subnetIds: List<String> = listOf()
    subnets.forEach {
        subnetIds = subnetIds.plus(it.getSubnetId())
    }

    val subnetGroup: CfnSubnetGroup = CfnSubnetGroup.Builder
        .create(scope, "developmentMyappInfraCdkElastiCacheSubnetGroup")
        .description("developmentMyappInfraCdkElastiCacheSubnetGroup")
        .subnetIds(subnetIds.toList())
        .cacheSubnetGroupName("developmentMyappInfraCdkElastiCacheSubnetGroup")
        .build()

    val parameterGroup: CfnParameterGroup = CfnParameterGroup.Builder
        .create(scope, "developmentMyappInfraCdkElastiCacheParameterGroup")
        .cacheParameterGroupFamily("redis7")
        .description("developmentMyappInfraCdkElastiCacheParameterGroup")
        .build()

    val userGroupId = context["elastiCacheUserGroupId"].toString()
    val securityGroupIds = context["elastiCacheSecurityGroupIds"] as List<String>

    val redis: CfnReplicationGroup = CfnReplicationGroup.Builder
        .create(scope, "developmentMyappInfraCdkElastiCache")
        .replicationGroupDescription("developmentMyappInfraCdkElastiCache")
        .atRestEncryptionEnabled(true)
        .automaticFailoverEnabled(true)
        .autoMinorVersionUpgrade(true)
        .cacheNodeType("cache.t2.micro")
        .cacheParameterGroupName(parameterGroup.getRef())
        .cacheSubnetGroupName(subnetGroup.getCacheSubnetGroupName())
        .engine("redis")
        .engineVersion("7.0")
        .ipDiscovery("ipv4")
        .multiAzEnabled(true)
        .networkType("ipv4")
        .nodeGroupConfiguration(listOf(
            CfnReplicationGroup.NodeGroupConfigurationProperty.builder()
                .nodeGroupId("1")
                .primaryAvailabilityZone("ap-northeast-1a")
                .replicaAvailabilityZones(listOf("ap-northeast-1c"))
                .replicaCount(1)
                .build()
        ))
        .numNodeGroups(1)
        .port(6379)
        .preferredMaintenanceWindow ("sat:16:00-sat:17:00")
        .securityGroupIds(securityGroupIds)
        .transitEncryptionEnabled(true)
        .userGroupIds(listOf(userGroupId))
        .build()
    redis.applyRemovalPolicy(RemovalPolicy.RETAIN)
}
```
userについてはtypeプロパティをsynthでType(大文字)に変換されない状況のためuser groupとともにコード化断念しました。[こちら](https://github.com/aws/aws-cdk/issues/23709)と同じ現象です

ElastiCacheに変更が入ると再作成になり、アプリケーション側で接続URLの変更が必要になります
そのため削除ポリシーを `RETAIN` に変更し、アプリケーション側での変更が完了してから手動で古い方を削除する運用にしています

## 環境ごとにリソース名を変更する
リソース名やIDに `development` と入っている箇所は環境ごとに変更したいので、環境名を引数で渡すように変更しました

そして `App.kt` に1行追加します
```Kotlin:App.kt
fun main(args: Array<String>): Unit {
    val app = App()
    val awsAccountId = app.getNode().tryGetContext("awsAccountId").toString()
    val awsDefaultRegion = app.getNode().tryGetContext("awsDefaultRegion").toString()

    val stackProps: StackProps = StackProps.Builder()
            .env(Environment.builder()
                .region(awsDefaultRegion)
                .account(awsAccountId)
                .build())
            .build()
    InfraPipelineStack(app, "developmentMyappInfraCdkPipelineStack", stackProps, "development")
    InfraPipelineStack(app, "productionMyappInfraCdkPipelineStack", stackProps, "production") // この行を追加
    app.synth()
}
```

## 本番環境の構築
コード化できていないリソースを作成し、cdk.jsonとcdk.context.jsonを更新します

### cdk deploy
最後に `cdk deploy` を実行で完了の予定でしたが、
API用と管理画面用のstackの作成がいつまで経っても完了しませんでした

ECS serviceの作成で、docker image取得先のECRが作成したばっかりでimageがpushされていないため失敗していました
CDKのコードをアプリケーションのリポジトリでまとめて管理することで解消できそうですが、今回はリポジトリを分けているので強行突破します

`cdk deploy` を実行してECR作成されたあとに、ビルドしておいたdocker imageをECRにpushすることで回避！発生するのは初回だけ！

## 完了
これでコード化と本番環境の構築が完了しました！
vpc周りは、subnetやRouteTableなどの知識がなくコード化を断念したため、その周辺の知識をつけたいと思いました
L3のApplicationLoadBalancedFargateServiceを使用すると楽に作成できそうです