AWS Cloud Development Kit (AWS CDK) дозволяє описувати інфрастуктуру використовуючи мови програмування TypeScript, JavaScript, Python, Java, C# або Go.
“Під капотом” створює CloudFormation стек, в якому створються ресурси, описані в вашому коді.
Відповідь на питання “Нашо CDK, коли є Terraform?” можна знайти ось тут – 4 ultimate reasons to prefer AWS CDK over Terraform.
Але так як я поки CDK не користувався, то про переваги та недоліки казати нічого не буду.
Єдине, на що відразу можна звернути увагу, це те, ще по-перше – нема state-файлів, як у Terraform, які, звістно, корисні, але додають трохи болю при менеджменті, по-друге – сам CloudFortaion, який має свої недоліки та “це не баг, а фіча”, зато маємо можливість у веб-інтерфейсі AWS Console побачити всі ресурси.
Взагалі, до AWS CDK прийшов тому, що на новому проекті він вже використовується, тож перш ніж тягнути в проект Terraform, треба розібратись з тим, що тут вже є.
UPD: Ну, нє, таки скажу. Виглядає наче й непогано і цікаво, бо “нарешті Python!”, але:
- всеж HCL-код виглядає куди більш лаконічно та зрозуміло
- прикладів та й банально результатів пошуку у Гуглі по Terraform набагто більше, а це значить і швидкість виконання задачі більша
- ну й… дойшов до того, що треба було створити SES домен, і… І нічого. Дуже неочікувано, але в стандартних Construtcs нічого толком не зайшов, а єдиний більш-менш конструкт з Construct Hub мав приклади тільки для TypeScript, навіть у PyDoc. Таке собі задоволення, чесно кажучи
Окрім CDK для самого AWS, є також cdk8s для Kubernetes та CDKTF для Terraform.
Окей, давайте знайомитись з AWS CDK.
Зміст
Key concepts
Почати можна з документації AWS Getting started with the AWS CDK або переглянути непоганий відео-туторіал на 20 хвилин Getting Started with AWS CDK and Python.
Отже, основні поняття, якими будемо оперувати при роботі з AWS CDK:
- App: App являє собою такий собі “контейнер”, в якому ми описуємо наш застосунок, і може мати в собі один або більше Stacks (які потім будут сформовані у CloudFormation Stacks). Див. Apps.
- Stack: зі Stack буде формуватись CloudFormation Stack або change set. У самому Stack на рівні коду ми описуємо те, які саме ресурси в цьому стеку будуть створені, а ресурси описуємо, використовуючи Constructs. Див. Stacks.
- Construct: основні “будівельні блоки” у CDK, в яких описуються компоненти, которі необхідно створити в AWS. Див. Construct.
На Construct зупинимось трохи детальніше, бо вони розподіляють на три основних групи:
- AWS CloudFormation-only або L1 (“layer 1”): тут маємо ресурси, які описані і підтримуються самим CloudFormation, і всі такі ресурси мають имена з префіксом Cfn, наприклад, для AWS S3 бакетів це
CfnBucket
. Всі ці ресурси знаходяться у модуліaws-cdk-lib
. - Curated або L2: ці конструкти розроблені командою AWS CDK для спрощення управління інфрастуктурою. Як правило, вони включать в себе ресурси з L1 з деякими дефолтними значеннями та політиками безпеки. Ресурси у
aws-cdk-lib
готові до використання у production, а якщо ресурс являє собою окремий модуль – то він ще або у стані розробки, або є experimental. - Patterns або L3: Patterns включають в себе декілька ресурсів, які дозволяють побудувати всю архітектуру під конкретний use case. Так само як і з L2 ресурсами, готові до продакшену модулі включені в модуль
aws-cdk-lib
, а ті, що знаходяться у стані розробки – являть собою окремі модулі.
Окрім AWS Construct Library є ще й Construct Hub, в якому можна знайти модулі від партнерів AWS.
Встановлення AWS CDK
Для роботи з AWS CDK, навіть якщо ви будете писати на Python, потрібна Node.js, так як всі мови програмування на CDK будуть працювати через бекенд на Node.js.
Для роботи з CDK маємо CLI, через яку можемо створювати нові App, генерувати CloudFormation темплейти, виконувати diff між нашим кодом і існуючими CloudFormation стеками та багато іншого.
Встановлюємо сам CDK – бекенд та CLI:
[simterm]
$ npm install -g aws-cdk added 1 package, and audited 2 packages in 1s
[/simterm]
Перевіряємо CLI:
[simterm]
$ cdk --help Usage: cdk -a <cdk-app> COMMAND Commands: cdk list [STACKS..] Lists all stacks in the app [aliases: ls] cdk synthesize [STACKS..] Synthesizes and prints the CloudFormation template for this stack [aliases: synth] cdk bootstrap [ENVIRONMENTS..] Deploys the CDK toolkit stack into an AWS environment cdk deploy [STACKS..] Deploys the stack(s) named STACKS into your AWS account ...
[/simterm]
Цікавості заради – куди веде сам файл cdk
:
[simterm]
$ which cdk /home/setevoy/.nvm/versions/node/v16.18.0/bin/cdk
[/simterm]
Ага, Node.js.
Authentication with AWS
Документація – Authentication and access:
- AWS SSO: використовууючи SSO-сессію через AWS CLI конфіг (
~/aws/config
), див. IAM Identity Center authentication - AWS EC2 Instance IAM Role: якщо код CDK запускається всередені AWS, наприклад з EC2, то до інстансу можна підключити IAM roles for Amazon EC2
- AWS IAM User Access/Secret keys: ну і звичні ключі доступу для AWS CLI з профілем у файлах
~/aws/config
та~/.aws/credentials
Створення проекту
cdk init
За допомогою cdk init
можемо згенерувати шаблон файлів та директорій.
Щоб отримати допомогу по конкретній команді, наприклад для init
– додайте --help
після неї, тобто cdk init --help
:
Тут з цікавих опцій можуть бути наступні:
--verbose
: більш детальний аутпут--debug
: ще більш детальний--role-arn
: використовувати IAM Role (див. AWS: IAM AssumeRole – описание, примеры)--language
: мова програмування, яка буде використовуватись при створенні проекту--list
: отримати список доступних шаблонів
Спробуємо list
:
[simterm]
$ cdk init --list Available templates: * app: Template for a CDK Application └─ cdk init app --language=[csharp|fsharp|go|java|javascript|python|typescript] * lib: Template for a CDK Construct Library └─ cdk init lib --language=typescript * sample-app: Example CDK Application with some constructs └─ cdk init sample-app --language=[csharp|fsharp|go|java|javascript|python|typescript]
[/simterm]
Тут можемо створити шаблон для App, бібліотеки для Construct Library, або створити sample-app
, тобто приклад App, в якому вже будуть додані якісь Constructcs.
Створюємо директорію нашого проекту:
[simterm]
$ mkdir cdk-example && cd cdk-example
[/simterm]
Запускаємо init sample-app
:
[simterm]
$ cdk init sample-app --language=python Applying project template sample-app for python ... Initializing a new git repository... hint: Using 'master' as the name for the initial branch. This default branch name hint: is subject to change. To configure the initial branch name to use in all hint: of your new repositories, which will suppress this warning, call: hint: hint: git config --global init.defaultBranch <name> hint: hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and hint: 'development'. The just-created branch can be renamed via this command: hint: hint: git branch -m <name> Please run 'python3 -m venv .venv'! Executing Creating virtualenv... ✅ All done!
[/simterm]
Глянемо структуру файлів та директорій проекту:
[simterm]
$ tree . . |-- README.md |-- app.py |-- cdk.json |-- cdk_example | |-- __init__.py | `-- cdk_example_stack.py |-- requirements-dev.txt |-- requirements.txt |-- source.bat `-- tests |-- __init__.py `-- unit |-- __init__.py `-- test_cdk_example_stack.py 4 directories, 11 files
[/simterm]
Python virtualenv та встановлення модулів AWS CDK
source.bat
– скрипт для Windows для створення Python virtualenv, який має викликати файл .venv\Scripts\activate.bat
:
[simterm]
$ tail -1 source.bat .venv\Scripts\activate.bat
[/simterm]
Але так як я це роблю на Linux, то каталогу .venv\Scripts
намє взагалі, натомість маємо набор скриптів у .venv/bin/
(ну теж якось виглядає… AWS CDK начебто серйозний проект, але таку дрібницю, як скрипти, зроблено через якось… недбало?):
[simterm]
$ ls -1a .venv/bin/ . .. Activate.ps1 activate activate.csh activate.fish pip pip3 pip3.11 python python3 python3.11
[/simterm]
Для Linux використовуємо .venv/bin/activate
, який являею собой shell-команди для створення та налаштування змінних оточення:
[simterm]
$ . .venv/bin/activate (.venv)
[/simterm]
Щоб перевірити, що ми справді у virtualenv можна перевірити значення змінної $VIRTUAL_ENV
, яка має шлях до каталогу поточного віртуального оточення, в якому будуть необхідні бібліотеки:
[simterm]
$ echo $VIRTUAL_ENV /home/setevoy/Scripts/AWS_CDK/.venv
[/simterm]
Далі, встановлюємо залежності – модулі aws-cdk-lib
та constructs
:
[simterm]
$ pip install -r requirements.txt ... Collecting aws-cdk-lib==2.78.0 Using cached aws_cdk_lib-2.78.0-py3-none-any.whl (41.0 MB) Collecting constructs<11.0.0,>=10.0.0 Using cached constructs-10.2.18-py3-none-any.whl (58 kB) ...
[/simterm]
Файл app.py
Перевіримо зміст app.py
, який являє собою основу нашого проекту, бо саме з нього CDK почне свою роботу:
#!/usr/bin/env python3 import aws_cdk as cdk from cdk_example.cdk_example_stack import CdkExampleStack app = cdk.App() CdkExampleStack(app, "cdk-example") app.synth()
Першим виконуємо import aws_cdk as cdk
– імпортуємо модуль aws_cdk dir
, який знаходиться у каталозі .venv
:
[simterm]
$ find . -name aws_cdk ./.venv/lib/python3.11/site-packages/aws_cdk
[/simterm]
Далі, from cdk_example.cdk_example_stack import CdkExampleStack
– імпортуємо клас CdkExampleStack(Stack)
з модулю cdk_example_stack.py
:
from constructs import Construct from aws_cdk import ( Duration, Stack, aws_iam as iam, aws_sqs as sqs, aws_sns as sns, aws_sns_subscriptions as subs, ) class CdkExampleStack(Stack): def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) queue = sqs.Queue( self, "CdkExampleQueue", visibility_timeout=Duration.seconds(300), ) topic = sns.Topic( self, "CdkExampleTopic" ) topic.add_subscription(subs.SqsSubscription(queue))
А в ньому вже бачимо самі ресурси, які будуть створюватись – SQS та SNS з Subscription.
Можна переглянути документацію по ресурсах:
[simterm]
>>> import aws_cdk as cdk >>> help (cdk.App())
[/simterm]
Де буде описаний клас App
:
[simterm]
Help on App in module aws_cdk object: class App(Stage) | App(*args: Any, **kwargs) -> Any | | A construct which represents an entire CDK app. This construct is normally the root of the construct tree. | | You would normally define an ``App`` instance in your program's entrypoint, | then define constructs where the app is used as the parent scope. ...
[/simterm]
Добре, йдемо далі.
cdk list
(або ls
) поверне нам список ресурсів в поточному каталозі/проекті:
[simterm]
$ cdk ls cdk-example
[/simterm]
Далі, можемо спробувати cdk synth
, який сгенерує нам шаблон CloudFormation, которий буде використано при деплої ресурсів:
[simterm]
$ cdk synth Resources: CdkExampleQueue7618E31B: Type: AWS::SQS::Queue Properties: VisibilityTimeout: 300 UpdateReplacePolicy: Delete DeletionPolicy: Delete Metadata: aws:cdk:path: cdk-example/CdkExampleQueue/Resource CdkExampleQueuePolicy839151B5: Type: AWS::SQS::QueuePolicy ...
[/simterm]
Робота з AWS CDK
AWS Account CDK Bootstrap
Перед тим, як вже деплоїти проект, нам потрібно налаштувати наш AWS аккаунт (або регіон) для роботи AWS CDK. Для цього використовуємо cdk bootstrap
, який створить CloudFormation стек з необхідними для роботи CDK ресурсами – S3-корзиною, ролі та полісі в IAM, ECR репозиторій, та записи у AWS Systems Manager Parameter Store. Див. bootstrapping.
Запускаємо:
[simterm]
$ cdk -v bootstrap ... ⏳ Bootstrapping environment aws://264***286/eu-central-1... ... CDKToolkit | 0/12 | 1:43:06 PM | REVIEW_IN_PROGRESS | AWS::CloudFormation::Stack | CDKToolkit User Initiated CDKToolkit | 0/12 | 1:43:11 PM | CREATE_IN_PROGRESS | AWS::CloudFormation::Stack | CDKToolkit User Initiated CDKToolkit | 0/12 | 1:43:16 PM | CREATE_IN_PROGRESS | AWS::IAM::Role | FilePublishingRole CDKToolkit | 0/12 | 1:43:16 PM | CREATE_IN_PROGRESS | AWS::IAM::Role | LookupRole CDKToolkit | 0/12 | 1:43:16 PM | CREATE_IN_PROGRESS | AWS::SSM::Parameter | CdkBootstrapVersion CDKToolkit | 0/12 | 1:43:16 PM | CREATE_IN_PROGRESS | AWS::IAM::Role | CloudFormationExecutionRole CDKToolkit | 0/12 | 1:43:16 PM | CREATE_IN_PROGRESS | AWS::ECR::Repository | ContainerAssetsRepository CDKToolkit | 0/12 | 1:43:16 PM | CREATE_IN_PROGRESS | AWS::IAM::Role | ImagePublishingRole CDKToolkit | 0/12 | 1:43:16 PM | CREATE_IN_PROGRESS | AWS::S3::Bucket | StagingBucket ... CDKToolkit | 11/12 | 1:44:02 PM | CREATE_COMPLETE | AWS::IAM::Role | DeploymentActionRole CDKToolkit | 12/12 | 1:44:04 PM | CREATE_COMPLETE | AWS::CloudFormation::Stack | CDKToolkit [13:44:09] Stack CDKToolkit has completed updating ✅ Environment aws://264***286/eu-central-1 bootstrapped.
[/simterm]
Перевіряємо S3 бакет:
[simterm]
$ aws s3 ls 2023-05-10 13:43:42 cdk-hnb659fds-assets-264***286-eu-central-1
[/simterm]
cdk deploy
І тепер можемо виконати cdk deploy
, який створить CloudFormation Stack з нашими SQS/SNS/Subscription:
[simterm]
$ cdk deploy ... cdk-example: assets built This deployment will make potentially sensitive changes according to your current security approval level (--require-approval broadening). Please confirm you intend to make the following modifications: IAM Statement Changes ┌───┬────────────────────────┬────────┬─────────────────┬───────────────────────────┬────────────────────────────────────────────────────────┐ │ │ Resource │ Effect │ Action │ Principal │ Condition │ ├───┼────────────────────────┼────────┼─────────────────┼───────────────────────────┼────────────────────────────────────────────────────────┤ │ + │ ${CdkExampleQueue.Arn} │ Allow │ sqs:SendMessage │ Service:sns.amazonaws.com │ "ArnEquals": { │ │ │ │ │ │ │ "aws:SourceArn": "${CdkExampleTopic}" │ │ │ │ │ │ │ } │ └───┴────────────────────────┴────────┴─────────────────┴───────────────────────────┴────────────────────────────────────────────────────────┘ (NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299) Do you wish to deploy these changes (y/n)? y ...
[/simterm]
Віповідаємо Y:
[simterm]
... Do you wish to deploy these changes (y/n)? y cdk-example: deploying... [1/1] [0%] start: Publishing 20e979ce16c7aba5e874330247d9054b841ea313261b523b47a50fc4cd1d6662:current_account-current_region [100%] success: Published 20e979ce16c7aba5e874330247d9054b841ea313261b523b47a50fc4cd1d6662:current_account-current_region cdk-example: creating CloudFormation changeset... [███████████████████▎······································] (2/6) 1:48:50 PM | CREATE_IN_PROGRESS | AWS::CloudFormation::Stack | cdk-example 1:48:54 PM | CREATE_IN_PROGRESS | AWS::SQS::Queue | CdkExampleQueue ...
[/simterm]
CDK локально сгенерує файл темплейту cdk.out/cdk-example.template.json
для CloduForamtion та загрузить його до бакету CDK, який був створений під час виконання cdk bootstrap
:
[simterm]
$ aws s3 ls cdk-hnb659fds-assets-264***286-eu-central-1 2023-05-10 13:48:45 5750 20e979ce16c7aba5e874330247d9054b841ea313261b523b47a50fc4cd1d6662.json
[/simterm]
Перевіряємо CloduForamtion стек:
[simterm]
$ aws cloudformation list-stacks { "StackSummaries": [ { "StackId": "arn:aws:cloudformation:eu-central-1:264***286:stack/cdk-example/3c4e4db0-ef20-11ed-9672-0a9a3483d50e", "StackName": "cdk-example", "CreationTime": "2023-05-10T10:48:45.099000+00:00", ...
[/simterm]
Або у AWS Console:
Тим часом деплой завершено:
[simterm]
... ✅ cdk-example ✨ Deployment time: 91.52s Stack ARN: arn:aws:cloudformation:eu-central-1:264***286:stack/cdk-example/3c4e4db0-ef20-11ed-9672-0a9a3483d50e ✨ Total time: 97.99s
[/simterm]
cdk diff
Добре – побачили, як воно все працює на всьому готовому, тепер давайте спробуємо створити щось власне, наприклад – S3 корзину.
До імпортів додаємо aws_s3
as s3 та прибираємо sns/sqs, iam та Duration.
Видаляємо ресурси SQS та SNS з класу CdkExampleStack
, та описуємо створення корзини – беремо приклад з документації PyPI:
from constructs import Construct from aws_cdk import ( Stack, aws_s3 as s3 ) class CdkExampleStack(Stack): def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) bucket = s3.Bucket(self, "MyEncryptedBucket", encryption=s3.BucketEncryption.KMS )
І поглянемо, що нам поверне cdk diff
:
Червоним та -
це те, що буде видалятись, зеленим та +
– створюватись нове (прям дуже terraform plan
нагадало).
Окей – і запускаємо деплой:
[simterm]
$ cdk deploy ✨ Synthesis time: 6.38s cdk-example: building assets... ... Do you wish to deploy these changes (y/n)? y cdk-example: deploying... [1/1] [0%] start: Publishing 5bb8c7fc8643769d69d5eb9712af36c955f9b509cc05d26740d035e9d7225a16:current_account-current_region [100%] success: Published 5bb8c7fc8643769d69d5eb9712af36c955f9b509cc05d26740d035e9d7225a16:current_account-current_region cdk-example: creating CloudFormation changeset... [████████████▉·············································] (2/9) 11:29:47 AM | UPDATE_IN_PROGRESS | AWS::CloudFormation::Stack | cdk-example 11:31:54 AM | CREATE_IN_PROGRESS | AWS::S3::Bucket | MyEncryptedBucket ...
[/simterm]
Глянемо, як воно виглядає в UI:
Готово.
Ну і приберемо за собою – видалимо стек.
cdk destroy
Перевіримо їм’я стеку з list
:
[simterm]
$ cdk ls cdk-example
[/simterm]
І виконуємо cdk destroy
з ім’ям стеку, щоб повністю його видалити:
[simterm]
$ cdk destroy cdk-example Are you sure you want to delete: cdk-example (y/n)? y cdk-example: destroying... [1/1] ✅ cdk-example: destroyed
[/simterm]
Глянемо адмінку:
Але бакет та ключ не видалило. А чому?
AWS CDK RemovalPolicy
Тому що у aws_cdk.core
є окрема RemovalPolicy
, яка по дефолту має значення Retain.
Ця політика контролює дії, які будуть виконані з ресурсами, которі були видалені з-під контролю CloudFormation:
- якщо ресурс видалений з шаблону (взагалі я чомусь думав, що CloudFormation видаляє такі ресурси, але то з досвіду, коли шаблони писались делоїлись вручну через AWS CLI, у випадку з CDK, як бачимо, по дефолту вони таки зберігаються)
- ресурс потребує заміни шляхом створення нового, тож CloudFormation створює новий, а старий видаляє зі свого контролю, але залишає сам ресурс
- CloudFormation стек видалено
Останній пункт у нас і спрацював.
Окей, давайте повторимо експеримент – створимо стек заново, але до корзини додамо параметр removal_policy
для її видалення при видаленні стеку, і auto_delete_objects=True
, щоб видалити всі об’єкти в ній, бо інакше корзину видалити не можна.
Крім того, ключ для корзини треба створити окремим об’єктом і передати йому власний removal_policy,
а потім цей ключ передавати аргументом в параметр encryption_key
корзини:
from constructs import Construct from aws_cdk import ( Stack, RemovalPolicy, aws_s3 as s3, aws_kms as kms ) class CdkExampleStack(Stack): def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) my_key = kms.Key(self, "MyKey", enable_key_rotation=True, removal_policy=RemovalPolicy.DESTROY ) bucket = s3.Bucket(self, "MyEncryptedBucket", encryption=s3.BucketEncryption.KMS, encryption_key=my_key, removal_policy=RemovalPolicy.DESTROY, auto_delete_objects=True )
Виконуємо synth
:
[simterm]
$ cdk synth Resources: MyKey6AB29FA6: Type: AWS::KMS::Key ... UpdateReplacePolicy: Delete DeletionPolicy: Delete ... MyEncryptedBucket9A8D2FE1: Type: AWS::S3::Bucket ... UpdateReplacePolicy: Delete DeletionPolicy: Delete ...
[/simterm]
Ось тепер має спрацювати – у обох ресурсів бачимо DeletionPolicy: Delete
.
деплоймо:
[simterm]
$ cdk deploy ... cdk-example: creating CloudFormation changeset... ✅ cdk-example ✨ Deployment time: 180.7s Stack ARN: arn:aws:cloudformation:eu-central-1:264***286:stack/cdk-example/1f5353d0-efe4-11ed-a627-02deb26b4a5c ✨ Total time: 187.53s
[/simterm]
І видаляємо:
[simterm]
$ cdk destroy Are you sure you want to delete: cdk-example (y/n)? y cdk-example: destroying... [1/1] ✅ cdk-example: destroyed
[/simterm]
Не встиг зробити скрін, але CDK запускав AWS Lambda, яка видаляла об’єкти в корзині, а може й саму корзину.
Ну й поки на цьому все.
У Workshop є ще приклади роботи, більше “продвинуті”, тож рекомендую.