DevOps з AWS CDK та майбутнє «інфраструктури як коду»

💡 Усі статті, обговорення, новини про DevOps — в одному місці. Приєднуйтесь до DevOps спільноти!

Мене звати Мозговий Микола, Ex-Sigma Software, наразі старший інженер Bolt. У недалекому минулому щільно працював з Azure та IoT, проте, протягом останнього року доводиться більше взаємодіяти з AWS.

У цій статті я хочу передати свій досвід користування особливою технологією, яка, на мою думку, визначає майбутнє «інфраструктури як коду» в цілому, а саме AWS CDK.

Окрім мого звіту ви також отримаєте зразок застосунку, який зможете використати як шаблон до власних проєктів.

По суті, AWS CDK дозволяє кодувати вашу інфраструктуру використовуючи мову програмування загального призначення за вибором. Наразі підтримуються наступні мови: C#, F#, Go, Java, Python, JavaScript, TypeScript.

TLDR: зразок застосунку з інструкцією щодо його використання можна знайти за посиланням.

Передісторія

Наша зустріч з AWS CDK була випадковою. Необхідно було розробити відносно простий MVP, невеликою командою з 4-5 осіб за термін 1-2 квартали. Бюджет не дозволяв нам мати відокремлену DevOps-позицію, проте управління конфігурацією для декількох середовищ виявилося нагальною потребою вже з перших тижнів.

Проєкт мав використовувати AWS, у той час як мій попередній досвід був в основному пов’язаний з Azure, тому використання більш-менш знайомих ARM- шаблонів або Azure CLI не було можливим. Крім того, ніхто з команди не мав досвіду роботи ні з Terraform, ні з CloudFormation.

AWS CDK виявився вдалою знахідкою. Мені, без перебільшення, вистачило двох днів, щоб розібратись з принципом його роботи та перевірити можливість його застосування для реалізації проєкту.

Оригінальний проєкт сам по собі та його функції неважливі до теми статті, тому в якості зразка ми будемо використовувати дуже просту веб-версію добре знайомої гри Snake.

Зразок архітектури

Архітектура, зображена нижче, досить точно представляє ресурси, що були використані для нашого реального проєкту (зображення 1):

Зображення 1. Цільова архітектура

Ключові особливості:

  1. Це безсерверна архітектура: відсутні зарезервовані обчислювальні потужності та відсутні витрати «наперед».
  2. Amazon API Gateway служить єдиною точкою входу до застосунку.
  3. Сховище S3 використовується для розміщення статичного вебсайту та медіа-контенту (у нашому випадку самої гри).
  4. AWS Lambda використовується як середовище виконання для API.
  5. Dynamo DB зберігає стан системи (у даному випадку це рахунок у грі).
  6. Заради простоти прикладу я маю оминути інтеграцію з Cognito, що в реальному рішенні була використана для ідентифікації користувачів.

Згідно зі схемою вище, репозиторій містить три проєкти:

  • /aws-snake
    • /snake-API # node/express API
    • /snake-client # безпосередньо гра на звичайному JavaScript
    • /snake-iac # проєкт AWS CDK

Оригінальний комерційний проєкт побудований на . NET Core, але для поточного зразка я використовую TypeScript з трьох причин:

  1. TypeScript виглядає чимось середнім між Java/C# і JavaScript.
  2. JavaScript-подібний синтаксис розуміють майже повсюдно.
  3. Щоб довести собі, що CDK справді працює незалежно від обраної мови програмування.

Початок роботи з CDK

Старт роботи з AWS CKD доволі простий, ви можете або слідувати офіційним інструкціям з початку роботи, або виконати вказані нижче кроки:

  • Створити обліковий запис AWS.
  • Створити ключ доступу AWS.
  • Встановити AWS CLI.
  • curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg"
sudo installer -pkg AWSCLIV2.pkg -target / 
  • Сконфігурувати AWS CLI, використовуючи попередньо створений ключ
    aws configure 
  • Встановити Node LTS.
    brew install node # mac 
    sudo apt-get install nodejs # debian 
    
  • Встановити AWS CDK toolkit.
    npm install -g aws-cdk 
  • Встановити цільове середовище виконання (у нашому випадку TypeScript)
.
    npm install -g typescript 
  • Cтворити пустий CDK проєкт.
    mkdir test-iac 
    cd test-iac 
    cdk init app --language typescript 
    
  • Ознайомитись з CDK командами.
    cdk list # Список усіх стеків у програмі 
    cdk synthesize # Синтезує та друкує шаблон CloudFormation для поточного стеку 
    cdk bootstrap # Розгортання стека інструментів CDK у середовищі AWS 
    cdk diff # Порівнює локальний стек із розгорнутим стеком та друкує різницю 
    cdk deploy # Розгортання стека в обліковому записі AWS 
    cdk destroy # Знищити розгорнутий стек 
    

Ось і все! Тепер ви знаєте всі необхідні команди CDK. Хоча здебільшого ви будете використовувати лише дві: cdk diff та cdk deploy.

Налаштування стека

Стек CloudFormation — це колекція ресурсів AWS керованих як одне ціле, що налаштовується як шаблон JSON або YAML (див. як це працює).

По суті, AWS CDK надає обгортку над стеком CloudFormation, що дозволяє створити його, використовуючи мову програмування загального призначення.

cdk init app створює порожній стек з відповідним файлом програмного коду, що залежить від цільової мови. В нашому випадку це lib/snake-iac-stack.ts (див. коміт 37d8d7d)

import * as cdk from '@aws-cdk/core'; 
export class SnakeIacStack extends cdk.Stack { 
    constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { 
       super(scope, id, props); 
    // The code that defines your stack goes here 
    } 
} 

Додавання нового ресурсу до стека — це просто створення нового екземпляру класу, наприклад, щоб створити S3 сховище для хостингу нашої JavaScript гри ми просто додаємо наступні рядки (див. коміт 93ceebe):

// The code that defines your stack goes here 
// Snake Web Client 
const snakeClient = new s3.Bucket(this, nameIt("website-s3"), { 
	bucketName: nameIt("website-s3"), 
	versioned: false, 
	publicReadAccess: true, 
	websiteIndexDocument: "index.html", 
	removalPolicy: cdk.RemovalPolicy.DESTROY // remove on stack destruction 
});

Зверніть увагу, що дуже важливо встановити загальну конвенцію іменування для всіх ресурсів, наприклад: <environment>-<project>-<resource>. Для цього ми використовуємо наступну допоміжну функцію:

const envName = 'test'; 
const nameIt = (name: string) => `${envName}-snake-${name}`.toLowerCase(); 

Сполучення ресурсів

Суттєвою особливістю CDK є можливість логічно зв’язувати ресурси разом, використовуючи їх як залежності. Наприклад, ми можемо встановити налаштований вище s3 як маршрут за замовчуванням (точку входу) до нашого веб-застосунку (див. коміт 59b58dc):

// API Gateway 
const snakeClientIntegration = new integration.HttpProxyIntegration( { 
	method: gate.HttpMethod.GET, 
    url: snakeClientBucket.bucketWebsiteUrl, 
}); 
const httpApi = new gate.HttpApi(this, nameIt("Api-GateWay"), { 
    apiName: nameIt("Api-GateWay"), 
    defaultIntegration: snakeClientIntegration, 
});

Далі ми можемо встановити маршрути API таким же чином (див. коміт 8f6e9e1):

const apiGateway = new gate.HttpApi(this, nameIt("Api-GateWay"), { 
  // ... // 
}); 
// Snake API 
const apiLambda = new lambda.Function(this, nameIt("api-lambda"), { 
  // ... // 
}); 
const snakeApiIntegration = new integration.LambdaProxyIntegration({ 
	handler: apiLambda, // api integration 
}); 
apiGateway.addRoutes({ // api route 
	path: "/api/{proxy+}", 
	methods: [gate.HttpMethod.ANY], 
	integration: snakeApiIntegration 
}); 
apiGateway.addRoutes({ // swagger route 
	path: "/swagger/{proxy+}", 
	methods: [gate.HttpMethod.GET], 
	integration: snakeApiIntegration 
}); 

Те саме стосується управління контролем доступу, надання доступу до бази даних для API компоненту (див. коміт b20df0a):

// Persistence layer 
const table = new dynamodb.Table(this, nameIt('DynamoDb-Table'), { 
	// ... // 
}); 
 
table.grantReadWriteData(apiLambda); // give api access to dynamo DB table 

Для завершеного зразка конфігурації стека зверніться до репозиторію проєкту.

Перегляд стеку

По-перше, ви можете знайти стеки CloudFormation у веб-консолі AWS (зображення 2):

Зображення 2. Стеки CloudFormation у консолі AWS

По-друге, той самий перелік стеків можна отримати, виконавши наступну команду у терміналі:

aws cloudformation list-stacks

Обробка дрейфу конфігурації

На жаль, це слабке місце як AWS CDK так і CloudFormation, оскільки виявлення та обробка дрейфу конфігурації наразі відсутні «out of the box» (див. відкрите питання).

Обробка дрейфу конфігурації можлива, проте не тривіальна (див. статтю).

Тому я б запропонував почати з запобігання можливості внесення несанкціонованих змін до конфігурації (див. офіційний посібник).

В ідеалі, всі зміни до інфраструктури повинні вноситися програмно, від імені окремого облікового запису, що використовується винятково CDK застосунком.

Цей підхід також може бути автоматизований (див. відкрите питання).

CI/CD

AWS CDK може бути легко інтегрований в процедури безперервного доставлення (continuous delivery). Для цього зразка використовуються GitHub Actions, тоді як для оригінального проєкту ми використали Azure DevOps, відмінності не суттєві.

Зразок конфігурації CI/CD складається з двох важливих частин:

  1. Скрипт для компіляції пакетів, що будуть розгорнуті (./build.sh);
  2. Налаштування GitHub Workflow у вигляді YAML файлу, що описує доставлення до декількох середовищ (test, prod) з підтвердженням адміністратора (./.github/workflows/ci-cd.yml).

Отриманий результат доставлення відображено нижче (зображення 3):

Зображення 3. Результат доставлення GitHub Workflow

Зверніться до інструкції з CI/CD у Readme.MD для налаштування власноруч.

Висновок

На мою думку, AWS CDK створив прецедент для DevOps практики майбутнього, а поточна розробка Terraform CDK доводить існування широкої зацікавленості у використанні мов загального призначення для реалізації «інфраструктури як коду».

Цей підхід робить дисципліну безперервного доставлення доступнішою, дозволяє розробникам бути залученими до підтримки інфраструктури, а також використати існуючі інструменти контролю якості як то статичний аналіз, або навіть модульні тести.

Наостанок, я припускаю, що деякі читачі будуть заперечувати ідею використання мови загального призначення для кодування інфраструктури. Оскільки це питання виходить за межі цієї статті, я маю обмежитися посиланнями (один, два) на відповідні дискусії в Reddit, що містять доволі доречні аргументи за і проти.

👍ПодобаєтьсяСподобалось10
До обраногоВ обраному13
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Якщо будете здавати AWS Solution Architect pro, то там будуть питання по юзкейсу CDK. Правильна відповідь звучить так: Якщо у вас є команда девелоперів, які повністю не знають CloudFormation і хочуть писати на мові програмування, яку вони розуміють. Використання CDK передбачає делегування IaaC девелоперам. Так сам AWS бачить цей продукт. Всі інші варіанти — помилка в архітектурі. Вимагати від девопса знання тайпскрипта буде дорого коштувати вашому проекту

проще и логичнее при конфигурации

Це звичайно суб’єктивні категоріі, проте, що простіше:
— оперувати єдиною мовою на проєкті,
— чи вивчати та підтримувати ще одну domain-specific?

А также поддерживат больше чем один провайдер.

Саме для цього розробляють Terraform CDK

— оперувати єдиною мовою на проєкті,

Чому це важливо? Для чого це потрібно?

Якби hcl був чимось типу хаскеля, то може і була б проблема. Синтаксично мова проста.
А АПІ (як створити той чи інший сервіс) доведеться вивчити і в CDK.

Це вже не до вас

А также поддерживат больше чем один провайдер.

Оце дуже специфічна фіча терраформа. Необхідність розгортати частини інфраструктури проекта в різних клаудах — дуже рідкісна вимога, з мого досвіду. А перенести «ламбда+черга+база» між різними клаудами якось не вийде без зміни коду, бо банально в різних клаудах різні сервіси.

Оце дуже специфічна фіча терраформа. Необхідність розгортати частини інфраструктури проекта в різних клаудах — дуже рідкісна вимога, з мого досвіду. А перенести «ламбда+черга+база» між різними клаудами якось не вийде без зміни коду, бо банально в різних клаудах різні сервіси.

Это круто не столько из-за мульти клауда. Основной бонус в том, что можно с помощью одного инструмента интегрировать между собой кучу всего.
Самый простой пример:
Создаётся сервис в PagerDuty, в нём создаётся CloudWatch интеграция. В AWS создаётся SNS и подписка на интеграцию в PagerDuty. Клеишь это в модуль, и можно легко, буквально за пару строк HCL в которых ты вызываешь модуль, любой алёртинг из AWS слать в PagerDuty, сразу в нужную команду, с настроенными политиками нотификаций.
И это самый простой пример.
+ куча модулей позволяет управлять из TF практически чем угодно. Начиная от менеджмента GitHub (репы, пользователи, доступы и т.д.), и заканчивая конфигом приложений (условный Graylog)

Так і не зрозумів які переваги (крім трішки більш лаконічного коду_ в порівнняні з Terraform де можна створювати мулті-клауд ресурси.

переваги наступні:

  • використання звичної та\або єдиної мови програмування для проєкту
  • можливість додавати дозвільну імперативну логіку
  • можливість використовувати існуючи засоби контролю якості коду
AWS CDK дійстно «wendor specific», але Terraform теж розвивається у цьому напряму (див. github.com/hashicorp/terraform-cdk)

Використання «звичайної» мови программування як-раз може створити конкретний зоопарк з різних мов на відмінну від терраформ. Імперативний підхід теж являється сумнівною перевагою.

можливість додавати дозвільну імперативну логіку

Оце, для мене, скоріше недолік. Сценарії коли це хотілося б мати я можу згадати (умовно кількість інстансів на прод/дев), але такі речі менш ризиковано вирішувати через параметри для декларативного коду.

використання звичної та\або єдиної мови програмування для проєкту

От тільки мова — це проблема хіба що для джунів. Основні проблеми будуть в тому щоб зрозуміти АПІ (як називаються ті чи інші поля) та розумінні самого конкретного клауда (наприклад, ті ж ІАМ ролі)

можливість використовувати існуючи засоби контролю якості коду

Це важлива перевага. Але виглядає трохи як «самі придумали проблему, самі вирішили»:
— тести допоможуть точно відповісти на питання «Чи розгорнеться кластер __коректно__?»
— власне, яку «якість коду» треба перевіряти в декларативних конфігах? Для тераформа точно немає якихось лінтерів?

використання звичної та\або єдиної мови програмування для проєкту

А потом приходишь на проект а там скрипты на баше, го, пайтоне, руби и перле. Зато каждый найдёт что-то для себя)

можливість додавати дозвільну імперативну логіку

Требует очень хорошей дисциплины и устоявшихся практик на проекте. Иначе очень быстро может скатиться в бездну трудночитабельной и трудноподдерживаемой простыни кода.

можливість використовувати існуючи засоби контролю якості коду

Для HCL уже давно есть линтеры, форматтеры и секьюрити сканеры

Доречі, вашу архітектуру можна трохи покращити використавши CloudFront для дистриб’юції статичного вебсайту та медіа-контенту

Доречне зауваження! Початковий задум був саме таким, але зважаючи на обмежену кількість користувачів, S3 здалося простішою опцією.

Використовуємо CDK на нашому поточному проекті з досить складною інфраструктурою

Маю сказати, що CDK дуже спрощує життя, коли використвуються десятки або навіть сотні різних AWS ресурсів. Описати все за допомогою CloudFormation або створити все вручну — було б нереально.

Також, CDK допомагає, коли є кілька сервісів з аналогічною архітектурою. В цому разі, створивши один раз CDK дефініцію, можна використовувати її створення нових компонентів системи.

Цікаво! Власне в мене особисто були сумніви щодо готовності технологіі до великих проєктів, так як мій досвід був обмежений проєктом відносно скромного маштабу. А серед спеціалізованих девопсів домінує Terraform.

Можу підтвердити, що технологія працює на проекті середнього розміру, де створюється кілька сотень різноманітних AWS ресурсів — починаючи від IAM roles і закінчуючи Route53, CloudFront, ApiGateway, Lambda, S3, StepFunctions і тд і пт. Є нюанси, інколи є необхідність кастомізації (мінімальної), але в цілому — все чітко працює.

Описати все за допомогою CloudFormation або створити все вручну — було б нереально.

Про «вручну» тут зрозуміло. А в чому нереальність використання CloudFormation або Terraform?

Про Terraform не знаю, а що до CloudFormation — як на мене, дуже важко підтримувати інфраструктурну конфігурацію, яка описана в кількох десятках файлів, кожен з яких має розмір кілька десятків кілобайтів.
Ще складніше стає, коли є необхідність мати конфігурацію для кількох енайронментів (бета, гамма, прод)

А можете примерно структуру описать для которой понадобятся *десятки* файлов? Сложно представить для каких задач такое нужно.
Это Cross или Nested stacks?
Или там по каждый регион свой набор стеков-файлов?

Даже учитывая beta/gamma/prod — можно же Conditions и Mappings использовать? Чтобы без большого дублирования

Например, когда система состоит из нескольких веб приложений, нескольких десятков API эндпоинтов, нескольких StepFunctions для процессинга (а для каждой StepFunction — 10+ Lambdas). Кроме того, несколько баз данных, S3 хранилищ. А также — IAM конфигурации, сервисы аутентификации/авторизации, конфигурации CodePipeline и тд и тп

як на мене, дуже важко підтримувати інфраструктурну конфігурацію, яка описана в кількох десятках файлів, кожен з яких має розмір кілька десятків кілобайтів.

Ееее? Якщо у вас декларативний запис стільки займає, то CDK лише додасть оверхед на синтаксис мови.

Ще складніше стає, коли є необхідність мати конфігурацію для кількох енайронментів (бета, гамма, прод)

В тераформі точно можна передавати параметри, в клаудформейшені наче теж, але не факт

CDK додасть оверхед на початковому етапі. Але буде спрощувати життя в подальшому — в процесі підтримки, розширення, рефакторингу інфраструктури

Але буде спрощувати життя в подальшому — в процесі підтримки, розширення, рефакторингу інфраструктури

Хотілось би зрозуміти за рахунок чого? (Якщо що я не пробую тут довести що «CDK не потрібно», я хочу зрозуміти чи треба його тягнути в наступний проект)

У Azure тоже ж есть подобная SDK уже несколько лет.

У Azure SDK совсем другой принцип действия, оно шлет серию HTTP запросов, оно не генерирует ARM template (хотя его можно экспортировать в последствии). Соответсвенно нет простой возможности увидеть превью вносимых изменений, как в случае использования ARM templates (what-if).
Характерно, что я ниразу не слышал, что бы Microsoft рекомендовал Azure SDK для реализации Infrastructure as Code. Например в их гайде упоминаються Azure Resource Manager (ARM), Terraform, и Azure CLI, но не Azure SDK.
Но(!) возможно я чего то не знаю.

Я тоже не видел, правда и не искал.
Но я использовал его еще только когда она появилась внезапно потому что ARM очень ограничены в своей функциональности. У Azure CLI та же проблема — нельзя протестить темлпейт.

У Azure тоже ж есть подобная SDK уже несколько лет.

А можна детальніше? Бо десь рік назад мав розмову і Азур девопси ще не знали про аналок AWS CDK (реліз був десь всередині 2019) для Азура

Вопрос топикстартеру или пользователям CDK

Нет ли каких-то сложностей в плане документации или примеров для каких-то менее популярных AWS ресурсов?
Вопрос такой возник потому что я такую особенность замечал даже в CloudFormation — не для всего есть примеры, а CDK насколько я понимаю обновляется по остаточному принципу, после того как обновилась документация в CloudFormation с некоторой задержкой.

Привет. У меня лично такой проблемы не возникало.
API reference выглядит весьма подробным: docs.aws.amazon.com/...​ws-construct-library.html
Однако, я использовал только малую долю возможных ресурсов, тоже правда.

Є такі проблеми, але всі можна вирішити тим чи іншим чином. Все досить легко діагностується. Після генерації можна передивитись CloudFormation template. Є доступ до коду компонентів. Можна писати свої компоненти або змінювати поведінку готових.

Я правильно розумію, що на CDK лише описується бажана структура ресурсів, а не виконується фактичне їх створення? Тобто, наприклад, для видалення DynamoDB буде достатньо видалити відповідний рядок і розгорнути стек знову?

давно не грався із CDK, але наскільки пам"ятаю то там зводиться до того, що буде створений CloudFormation стек, який власне описує що саме буде створено в ресурсах. Щодо DynamoDB там же штука в тому, що якщо у твоєму CF стеку вже є цей ресурс то для його видалення треба зробити ченж сет (але треба ще звертати увагу на RemovalPolicy), і його можна зробити прибравши лінії коду відповідальні за цей ресурс

для видалення DynamoDB буде достатньо видалити відповідний рядок і розгорнути стек знову?

Если очень упрощенно то да, если какой-то ресурс был создан через CloudFormation/CDK, а потом из шаблона(кода) пропал то в changeset он помечается как такой что нужно удалить на стороне AWS тоже.

Как с удалением DynamoDB таблиц точно не помню, для каких-то ресурсов могут быть ограничения самого AWS как например нельзя удалить непустой S3 bucket.

DeletionPolicy attribute такое еще желательно освоить для полного понимания жизненного цикла ресурсов CloudFormation/CDK

на CDK лише описується бажана структура ресурсів

— так

для видалення DynamoDB буде достатньо видалити відповідний рядок і розгорнути стек знову

— саме так!

а хіба таблиця данних не буде просто відв’язана від стеку, але фізично лишиться?

нет, таблица не будет отвязана от стека, она удалится.

DeletionPolicy относится к моменту когда удаляется весь стек, а не к моменту когда таблица удаляется из файла конфигурации.

Source:
docs.aws.amazon.com/...​ibute-deletionpolicy.html

Верно. Если не хочется потерять данные, то наиболее надежный способ — запретить CloudFormation удалять определенные ресурсы.

Делается новая IAM роль и подвязывается к CloudFormation — docs.aws.amazon.com/...​sing-iam-servicerole.html

В этой роли запрещаются delete* операции на определенных ресурсах (условно все, что может хранить какие-то важные данные, которые невозможно восстановить, включая S3, DDB, RDS, EFS и так далее.

Если какие-то данные «гибридные» (например, есть S3 bucket с важными данными и есть другой S3 bucket с логами, то можно попытаться дополнительно отконфигурировать права с помощью тегов, которые будут помещаться на объекты, но легко накосячить)

Далее CloudFormation запускается только из-под этой роли (ни в коем случае не под своим админским акаунтом).

Если CloudFormation попытается удалить ресурс, то ему это не позволит сделать система безопасности и он упадет. Нужно будет идти и ручками фиксить когда нужно что-то реально удалить из CFN (временно добавить нужные права в IAM роль и перезапустить CFN).

Это все, разумеется, периодически тестить (попытаться что-то ненужное удалить и убедиться, что ничего не удаляется).

там уже есть встроенная защита, например если в s3 бакет что то записать , то удалить через темплейт не получится

Підписатись на коментарі