【AWS】CDKでECS Clusterを構築しよう

[【AWS】CDKでECRを構築しよう](https://qiita.com/homines22/items/71e5b596e3edab46f062)のECS Clusterバージョンです
前提などは省きますので[前回の記事]((https://qiita.com/homines22/items/71e5b596e3edab46f062))を参照してください :pray:

# 環境

![language](https://img.shields.io/badge/language-python-blue.svg) ![python3.8](https://img.shields.io/badge/python-^3.8-blue.svg) ![pipenv](https://img.shields.io/badge/package_manager-pipenv-blue.svg)

# インストール

```bash
pipenv install aws_cdk.aws_ec2 aws_cdk.aws_ecs
```

# 実装

## props/ecs_cluster.py

ECS Clusterを作成する時に既存のVPCを利用したい場合は、VPC IDをもとにルックアップして既存VPCをStack内で利用できるようにする必要があります。そのためVpcForEcsClusterというクラスでパラメータ管理をします。

あとはAPI Referenceを参照しながら、設定可能なパラメータを定義しています。Container Insightsは有効化しておいた方が監視で役に立つので、デフォルトでTrueにしてあります。

```python:props/ecs_cluster.py
from dataclasses import dataclass
from typing import Optional

from aws_cdk.aws_ecs import CloudMapNamespaceOptions

from src.props.base import Base


@dataclass(frozen=True)
class VpcForEcsCluster(Base):
    id: str
    vpc_id: str


@dataclass(frozen=True)
class EcsCluster(Base):
    id: str
    cluster_name: str
    container_insights: bool = True
    default_cloud_map_namespace: Optional[CloudMapNamespaceOptions] = None
```

## entity/ecs_cluster.py

ECS Clusterの場合は環境によってパラメータが違う、具体的にいうとVPC IDが異なるので環境ごとのentityを作成します。今回はステージングだけを書いていますが、本番の場合は同じファイルにProdEcsClusterクラスを定義すればいいでしょう。

```python:entity/ecs_cluster.py
from src.entity.base import Base
from src.props.ecs_cluster import EcsCluster, VpcForEcsCluster


class EcsClusterBase(Base):
    vpc: VpcForEcsCluster
    cluster: EcsCluster


class StgEcsCluster(EcsClusterBase):
    id = 'StgEcsCluster'

    vpc = VpcForEcsCluster(
        id='StgVpc',
        vpc_id='vpc-xxxxxxxx'
    )

    cluster = EcsCluster(
        id='StgEcsCluster',
        cluster_name='sample'
    )
```

## stack/ecs_cluster.py

stackはどの環境でも使いまわせるようになっています。

Clusterでは、既存VPCを利用したい場合は引数に`vpc`の指定が必要なのでVpc.from_lookupというクラスメソッドを利用して、VPC IDをもとに取得しています。

```python:stack/ecs_cluster.py
from typing import Any, Type

from aws_cdk.aws_ec2 import Vpc
from aws_cdk.aws_ecs import Cluster
from aws_cdk.core import Construct, Stack

from src.entity.ecs_cluster import EcsClusterBase


class EcsClusterStack(Stack):
    def __init__(
            self,
            scope: Construct,
            entity: Type[EcsClusterBase],
            **kwargs: Any) -> None:
        super().__init__(scope, entity.id, **kwargs)

        vpc = Vpc.from_lookup(self, **entity.vpc.to_dict())
        Cluster(self, **entity.cluster.to_dict(), vpc=vpc)
```

## app.py

それではapp.pyに構築したいリソースのスタックを追加しましょう。
既存VPCを利用してECS Clusterを構築したい場合は、region/accountの環境設定も`EcsClusterStack`に渡してあげます。CDKが自動でContextを作成して、デプロイ時に変更されるとまずいデータ(今回は既存VPC)を保持してくれます。キャッシュ的な意味もあります。

```python:app.py
#!/usr/bin/env python3

from aws_cdk import core

from src.entity.ecr import SampleEcr
from src.entity.ecs_cluster import StgEcsCluster
from src.stack.ecr import EcrStack
from src.stack.ecs_cluster import EcsClusterStack

app = core.App()

# 全てのリソースに設定するタグ
tags = {'CreatedBy': 'iscream'}

# 環境設定 (既存VPCを利用するために必須)
env_stg = {'region': 'ap-northeast-1', 'account': 'xxxxxxxx'}

# 前回作成したECR
EcrStack(app, entity=SampleEcr, tags=tags)

# ECS Cluster
EcsClusterStack(app, StgEcsCluster, tags=tags, env=env_stg)

app.synth(skip_validation=False)
```

# デプロイ

いざ、デプロイ!!!

```bash
pipenv run cdk deploy StgEcsCluster
```

# Reference

- https://docs.aws.amazon.com/cdk/api/latest/python/aws_cdk.aws_ecs/Cluster.html
- https://docs.aws.amazon.com/cdk/api/latest/python/aws_cdk.aws_ec2/Vpc.html