みなさんAWS CDK使っていますか?
私はCFnを使用していたのですがYAMLで書くのがいろいろ辛くなってきたので、最近AWS CDKに乗り換えました
という訳で今回は、AWS CDKを使ったECRの構築方法を書いていきたいと思います
環境
前提
以下のコマンドがインストール済み
- pipenv
- aws-cdk
ディレクトリ構成
.
├── Pipfile
├── Pipfile.lock
├── README.md
├── app.py # CDKのPython用メインスクリプト
├── cdk.json # CDKのコンフィグ
├── cdk.out # CDKから出力されたCFn templateなどが格納される
├── mypy.ini
├── src
│ ├── __init__.py
│ ├── entity # リソースの実体を格納
│ ├── props # リソースのパラメータ定義クラス
│ └── stack # CFn stackを格納
└── tox.ini
ディレクトリやスクリプトの関係性
props/ -> entity/ -> stack/ -> app.py
上記のような関係性を保つことで変更に対して強くなりますし、環境ごとにパラメータを変えて管理したい場合にも対応します
インストール
必要なaws_cdkのライブラリをインストールしましょう
pipenv install aws_cdk.core aws_cdk.aws_ecr
実装
props/ecr.py
Python用のaws_cdkライブラリにも各リソースのPropsクラスがあるのですが、個人的に使い勝手がよくない印象なので、独自に定義しています
ここには必要なパラメータしか設定せずに、用途によってデフォルト値も公式のものから変更しています
CFnの場合、ライフサイクルルールをJSONテキストで書かなければなりませんが、CDKだとプロパディに値を渡すだけなので、かなり楽です
from dataclasses import dataclass
from typing import Optional
from aws_cdk.aws_ecr import TagStatus
from aws_cdk.core import RemovalPolicy
from src.props.base import Base
@dataclass(frozen=True)
class LifecycleRule(Base):
description: Optional[str] = None
max_image_count: int = 5
rule_priority: int = 1
tag_status: TagStatus = TagStatus.ANY
@dataclass(frozen=True)
class Repository(Base):
id: str
repository_name: str
image_scan_on_push: Optional[bool] = None
removal_policy: RemovalPolicy = RemovalPolicy.RETAIN
entity/ecr.py
作成したいリソースを定義を書いていきます
今回はクラスで定義していますが、インスタンス化させても良いと思います
クラスの方が個人的に体裁がよく見えるのでそうしているだけです
from typing import List, Optional
from src.entity.base import Base
from src.props.ecr import LifecycleRule, Repository
class EcrBase(Base):
"""ECR基底class"""
repository: Repository
lifecyle_rules: Optional[List[LifecycleRule]] = None
class SampleEcr(EcrBase):
"""Sample"""
id = 'SampleEcr'
repository = Repository(
id='SampleEcrRepo',
repository_name='sample'
)
lifecyle_rules = [
LifecycleRule(
description='Delete more than 10 images',
max_image_count=10
)
]
stack/ecr.py
ECR用のスタックを定義していきます
スタックのクラスに渡すリソース定義は基本的にentity
だけになるはずです
あとは必要に応じてapp.pyで全体で共通して設定したいtagなどを引数に渡してあげましょう
from typing import Any, Type
from aws_cdk import core
from aws_cdk.aws_ecr import Repository
from src.entity.ecr import EcrBase
class EcrStack(core.Stack):
def __init__(
self,
scope: core.Construct,
entity: Type[EcrBase],
**kwargs: Any) -> None:
super().__init__(scope, entity.id, **kwargs)
repo = Repository(self, **entity.repository.to_dict())
if entity.lifecyle_rules:
for rule in entity.lifecyle_rules:
repo.add_lifecycle_rule(**rule.to_dict())
app.py
stack/ecr.pyに書いたスタックをデプロイするためにapp.pyに書いていきましょう
#!/usr/bin/env python3
from aws_cdk import core
from src.entity.ecr import SampleEcr
from src.stack.ecr import EcrStack
app = core.App()
# 全てのリソースに設定するタグ
tags = {'CreatedBy': 'iscream'}
EcrStack(app, entity=SampleEcr, tags=tags)
app.synth(skip_validation=False)
デプロイ
デプロイ方法は以下の2つです
pipenv run cdk deploy SampleEcr
OR
pipenv shell
cdk deploy SampleEcr
デプロイする際に注意して欲しいのはpipenvで仮想環境上でcdkコマンドを実行する点です
それを忘れると以下のようなエラーになります
Traceback (most recent call last):
File "app.py", line 3, in <module>
from aws_cdk import core
ModuleNotFoundError: No module named 'aws_cdk'
Subprocess exited with error 1
おわりに
CDKの場合はプログラミング言語でリソースを定義できるので、柔軟にリソースを管理することができます。
その反面、コードの設計をしっかりしないとスパゲッティになり、柔軟性がなくなり運用がつらくなります。
今回紹介した方法がベストプラクティスかはわかりませんが、ちゃんと機能ごとにディレクトリやファイルを分けて管理するだけでも運用はよくなると思います。
CDKはかなり良いプロダクトなので、ぜひ使ってみてください!!