[PYTHON] [AWS] Build an ECR with AWS CDK

Do you use AWS CDK? I was using CFn, but writing in YAML has become a lot harder, so I recently switched to AWS CDK.

So, this time, I would like to write how to build an ECR using AWS CDK.

environment

languagepython3.8pipenv

Premise

The following commands are installed

Directory structure

.
├── Pipfile
├── Pipfile.lock
├── README.md
├── app.py #CDK main script for Python
├── cdk.json #CDK config
├── cdk.out #CFn template etc. output from CDK is stored
├── mypy.ini
├── src
│   ├── __init__.py
│   ├── entity #Stores the entity of the resource
│   ├── props #Resource parameter definition class
│   └── stack #Store CFn stack
└── tox.ini

Directory and script relationships

props/ -> entity/ -> stack/ -> app.py

By maintaining the above relationships, you will be more resistant to changes, and you can also manage by changing the parameters for each environment.

Installation

Install the required aws_cdk library

pipenv install aws_cdk.core aws_cdk.aws_ecr

Implementation

props/ecr.py

The aws_cdk library for Python also has a Props class for each resource, but I personally feel that it is not easy to use, so I have defined it myself. Only the necessary parameters are set here, and the default value is changed from the official one depending on the application.

In the case of CFn, the life cycle rule must be written in JSON text, but in the case of CDK, it is quite easy because it only passes the value to the propadi.

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

I will write the definition of the resource I want to create This time it is defined in a class, but I think you can instantiate it. I just do that because the class looks better personally

from typing import List, Optional

from src.entity.base import Base
from src.props.ecr import LifecycleRule, Repository


class EcrBase(Base):
    """ECR basis 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

We will define the stack for ECR The only resource definition you pass to the stack class should basically be ʻentity` After that, if necessary, let's pass the tag etc. that you want to set in common in app.py as an argument

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

Let's write in app.py to deploy the stack written in stack / ecr.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 to set for all resources
tags = {'CreatedBy': 'iscream'}

EcrStack(app, entity=SampleEcr, tags=tags)

app.synth(skip_validation=False)

Deploy

There are two deployment methods:

pipenv run cdk deploy SampleEcr

OR

pipenv shell
cdk deploy SampleEcr

One thing to keep in mind when deploying is to run the cdk command on the virtual environment with pipenv. If you forget it, you will get the following error

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

in conclusion

In the case of CDK, resources can be defined in a programming language, so resources can be managed flexibly. On the other hand, if the code is not designed properly, it will become spaghetti, and it will be inflexible and difficult to operate. I don't know if the method introduced this time is the best practice, but I think that the operation will be improved just by managing directories and files separately for each function.

CDK is a pretty good product, so please try it !!

Reference

Recommended Posts

[AWS] Build an ECR with AWS CDK
[AWS] Let's build an ECS Cluster with CDK
Building an AWS Fargate Service with AWS CDK
AWS CDK with Python
Easily build network infrastructure and EC2 with AWS CDK Python
Build a cheap summarization system with AWS components
Easily build HPC on AWS with genuine AWS Cfn Cluster
[Linux] WSL2 Build an environment for laravel7 with Ubuntu 20.04
I tried AWS CDK!
CodeCommit + CodePipeline with CDK
Build python3.x with pyenv
Build AWS EC2 and RDS with Terraform Terraform 3 minutes cooking
Create a Todo app with Django ① Build an environment with Docker
Create an environment with virtualenv
Create an API with Django
Build an Anaconda virtual environment
Build python3 environment with ubuntu 16.04
Build python environment with direnv
Linux fastest learning with AWS
Easily build CNN with Keras
AWS Lambda with PyTorch [Lambda import]
Let's build git-cat with Python
Use AWS interpreter with Pycharm
Create an alias for Route53 to CloudFront with the AWS API
Prepare an environment to use OpenCV and Pillow with AWS Lambda
When doing sam build with AWS Lambda Layers, the file hierarchy changes and an import error occurs.