AWS CDK. Infrastructure as a code with TypeScript [ukr]
Talk presentation
I will tell you how we did the project without DevOps. We will go through the basic concepts of AWS CDK. Let's get acquainted with the services, write and deploy an application for user management on AWS.
- In web development for more than 10 years, 9 of which in the company Lemberg Solutions, in which he went from Middle to Head of JavaScript Department.
- He started his career as a PHP Developer, became interested in JavaScript after the release of Angular 2, and later didn't notice how he became the leader of the JavaScript team.
- Had experience with Angular, Node.js, NestJS, React.
- Twitter, Facebook, LinkedIn
Talk transcription
Всім привіт! Радий вас вітати на конференції fwdays з доповіддю на тему "AWS CDK: Infrastructure as Code з TypeScript". Дозвольте розпочати кілька слів про себе, так, як мене представили в IT 11 років тому. Мій шлях розпочався з PHP. На даний момент я обіймаю посаду хеда відділу JavaScript у компанії Lamberth Solutions. Lamberth Solutions нараховує більше 200 співробітників, і відділи у нас розділені за технологіями, не за проектами. І, зрозуміло, я відповідаю за відділ JavaScript, в якому працює приблизно 20 інженерів. Наш фокус - це Angular, Vue, React та Node. Щодо мого власного досвіду, я починав з PHP, а потім перейшов до Node і Angular. Зараз найсильніші технології для мене - це Node, Angular, React і AWS.
Отже, переходимо до теми нашої доповіді. Як завжди, почнемо з постановки проблеми, яку ми намагалися вирішити. Тему доповіді я взяв із проекту, на який звернувся клієнт із проханням розбити великий моноліт на self-contained systems, а не просто на мікросервіси. Ідея полягала в тому, щоб розділити моноліт на частини, які можуть функціонувати незалежно одна від одної. Однак у нас не було достатньо ресурсів у DevOps команди для підтримки цього процесу. Клієнт встановив термін - три місяці на валідацію ідеї.
Ми почали розглядати можливості, як вирішити це завдання без підтримки DevOps і обмеженого часу для розгляду. І ми обрали AWS CDK - Cloud Development Kit від AWS, який дозволяє нам описувати інфраструктуру кодом, використовуючи нашу улюблену мову програмування - TypeScript. Основні концепції CDK включають аплікації, стеки і конструкції, які дозволяють створювати інфраструктуру. Це все можна описати об'єктно-орієнтованим кодом, який генерує CloudFormation Template. Для тих, хто не знайомий з AWS, CloudFormation Template - це засіб автоматизації розгортання AWS сервісів.
Наш код виглядає приблизно так. Ми маємо стек, який складається з конструкцій, таких як створення DynamoDB таблиці, що виглядає ось так. Ми почали розглядати цей підхід і бачимо великий потенціал у використанні AWS CDK для вирішення проблеми самостійного функціонування системи. І це виглядає дуже перспективно для виконання завдання протягом трьох місяців без необхідності великої підтримки DevOps. Це дозволить клієнту ефективно валідувати свою ідею. Так що, ми впевнені, що AWS CDK стане невід'ємною частиною нашого проекту.
На мою думку, першим великим плюсом для нас була невелика команда з трьох людей. Те, що ми можемо використовувати те, що вже знаємо, стає перевагою, оскільки нам не потрібно вивчати щось окремо; ми можемо писати все на TypeScript. Іншою важливою перевагою є можливість створення сервісів без необхідності ходити в консоль або писати важкозрозумілий CloudFormationConfig. Ми можемо просто писати об'єкти і деплоїти нашу інфраструктуру одночасно з кодом. Тут ви можете побачити, наскільки легко це робиться.
Ми маємо код і маємо CDK. CDK має ще одну перевагу, на мою думку, - наявність User-Friendly CLI, за допомогою якої ми можемо швидко генерувати код. Ми можемо швидко деплоїти і розгортати нашу інфраструктуру в AWS. Також важливою перевагою є велика кількість вже побудованих конструкцій, які ми можемо брати і використовувати. Загалом, для кожного AWS сервісу існує конструкція, яку ми можемо використовувати. Плюс, існує багато інших конструкцій, написаних у спільноті, які також можна використовувати. Тепер, як це все виглядає?
Я підготував коротеньку демонстрацію - дуже простий фронтенд для створення, читання, оновлення та видалення користувачів. Фронтенд не є складним, оскільки основний акцент робиться на роботі з CDK. Це проста таблиця, яка допомагає створювати, видаляти і редагувати користувачів. Ми будемо створювати щось подібне. Архітектура дуже проста - у нас є React-додаток, який використовує GraphQL AppSync для спілкування з таблицею DynamoDB через лямбда-функції.
Ми створимо набір лямбда-функцій для створення, редагування, видалення та оновлення користувача. Це все буде пов'язано з GraphQL AppSync. Для цього ми будемо використовувати CloudFormation, який буде білдити все, що ми напишемо. У нас буде лише один Backend Stack, який складатиметься з трьох конструкцій - DynamoDB таблиці, AppSync і лямбда-функцій. Ось вже код. Давайте перейдемо. Я продемонструю цей фронтенд.
Виглядає він приблизно так. Зараз він працює на моках. У кінцевому результаті ми зробимо його працюючим з реальним AWS за допомогою GraphQL. Відзначу, що основна ідея полягає в роботі з CDK. Фронтенд - це просто приклад для ілюстрації. Ми мало задумувалися про фронтенд. Дуже проста таблиця, яка допомагає створювати, видаляти і редагувати користувачів. Приблизно такий ефект ми хочемо отримати. Архітектура цього дуже проста. У нас є React-додаток, який використовує GraphQL AppSync. Це AWS-сервіс, який забезпечує нам GraphQL інтерфейс для взаємодії, у даному випадку, з DynamoDB таблицею UserStable через лямбда-функції.
І давайте спробуємо написати якийсь код. У нас навіть тут є закоментований код для того, щоб щось спробувати і з цим погратися. В даному прикладі використовується SQS-сервіс. Ми можемо просто розкоментувати цей код, зберегти його і спробувати розвернути в AWS. Що нам для цього потрібно? Спочатку ми повинні це збудувати. Десь я зробив якусь помилку. О, він вже з будувався. Тепер нам потрібно запустити NPX CDK Bootstrap, який створить все, що нам необхідно на AWS-консолі. Так, ми бачимо, що зараз ніяких змін не було. І ми можемо запустити CDK Deploy для того, щоб розгорнути це все на AWS-аккаунті. Так, ми бачимо, що зараз створюється CloudFormation-темплейт.
І ми бачимо, що стек починає створюватися. Щоб переконати, що він дійсно створюється, я пропоную зайти в AWS-консоль. Ми бажаємо відкрити CloudFormation-сервіс. Якщо його оновити, ми бачимо, що наш стек почав створюватися. Трішки почекаємо. Поки чекаємо, в принципі, можемо оглянути, що в нас в коді. Відповідно, нам утворився стек, який створює Q. Саме для нас утворилося це в папочці lib. Папочка cdk-out – це за допомогою команди cdk-bootstrap ми генеруємо це, що потрібно для розгортання.
І, увага ваша, я би хотів звернути на cdk-json. Це файлик, який вказує, що цей проект збудований на CDK. Він виглядає так. Як ми бачимо, консоль повідомляє, що все створено. Можемо йти в SDK дивитися. Ми бачимо, що стек create-complete. Якщо ми його відкриємо, ми можемо глянути, які ресурси було створено в цьому стеку. І ми бачимо, що створено ресурс, який називається fwdsq. І також CDK створює свій ресурс, який називається cdk-metadata. Перевіримо, чи дійсно Q було створено. Для цього перейдемо в SQS консольку. І так, ми бачимо, що Q було створено. І так само ми можемо це все почистити за собою, за допомогою команди cdk-destroy. Так, нас перепитається, чи ми дійсно впевнені, що ми хочемо видалити весь стек.
Таким чином, ми натискатимемо на це, і можемо побачити, що наш стек видаляється. І досить швидко він самостійно зніме це, і видалить всі сервіси, які були створені. Отже, ми можемо досить просто маніпулювати сервісами. На мою думку, це набагато зручніше, ніж робити це через CloudFormation. Звісно, це набагато зручніше, ніж робити це прямо з консолі, оскільки це взагалі не є хорошим продакшн-рішенням.
Відповідно, знаючи це, ми приступили до роботи. І, в принципі, з цим проєктом, про який я розповідав на початку, за три місяці ми впоралися. Але давайте демонструвати вам... Так, зараз ми бачимо, що це вже видалилося у новій консолі. Дозвольте мені продемонструвати вам, як працює та програма, про яку я розповідав, із створенням користувачів. Для цього ми також перейдемо до коду. Щоб заощадити час, я написав весь код наперед.
Отже, я розглядатиму його і коментуватиму, не буду писати його зараз знову. По-перше, нам потрібно створити таблицю DynamoDB. Структура файлів виглядає наступним чином: у нас є файл із стеком і папка з конструкціями, де розташовані наші AppSync, AppSync Lambda, ConstructBuilder – це інтерфейс, який я створив для того, щоб у всіх наших конструкцій був метод build, і ми могли їх легко викликати однаково в стеку. Почнемо з бази даних. Для цього структура файлів виглядає наступним чином: ми маємо файл із стеком, і є папка конструкцій, де розташовані наші AppSync, AppSync Lambda, ConstructBuilder – це інтерфейс, який я створив для того, щоб у всіх наших конструкцій був метод build, і ми могли їх легко викликати однаково в стеку. Почнемо з бази даних. Для цього в даному прикладі я обрав DynamoDB, але ви можете вибрати будь-яку іншу базу даних за вашим вибором. Ви можете використовувати все, що доступно в AWS. Ми створюємо таблицю. Ви бачите, що ми беремо конструкцію AWS DynamoDB з пакету AWS CDK lib.
В нього вже є написаний для нас клас table. New table. Перший параметр цих конструкцій - це стек, до якого вони належать. Другий - ідентифікатор або назва таблиці. Я назвав це users table. І також ми маємо вказати ID для таблиці. Тут я хочу особливо звернути вашу увагу на removal policy. Отже, в AWS стеки бувають двох типів: persistent і звичайні. Звичайні конструкції за замовчуванням не видаляються, коли ми знищуємо стек. Це зроблено для того, щоб, коли ми працюємо з даними, і ми видаляємо наш стек, наші дані не втрачалися.
Тому нам потрібно вказати це вручну, що ми хочемо видаляти таблицю разом зі стеком, і всі пов'язані з нею ресурси. Це важливо, коли ви розробляєте щось, або ви експериментуєте, оскільки, якщо не видаляти таблицю, то через якийсь час AWS може почати чарджити вас за цю таблицю, але ви вже не користуєтеся нею. Тому важливо не забувати про цей removal policy. Отже, у нас є база даних, у якій створено таблицю. Після цього у нас створюється сервіс AppSync. Сервіс AppSync створюється аналогічно. Ми маємо GraphQL API, яке ми беремо з CDK Lib.
Перший параметр, також, передається стек. Другий параметр - це ідентифікатор, і далі йдуть властивості, специфічні для цієї конструкції. Ми можемо вказати назву і вказати шлях до схеми. Тут дуже зручно, оскільки я організував frontend і backend в одному репозиторії, і в обох вони використовують ту саму GraphQL-схему, і нам не потрібно думати, як їх синхронізувати. Ми можемо просто вказати шлях до неї. X-Ray Enabled - це для дебагу, і авторизація. У цьому прикладі ми вибрали авторизацію по ключу API, щоб просто на демо підключити API-ключ, і frontend буде взаємодіяти з backend через нього. В продакшні, можливо, вам знадобиться щось інше.
Також я хочу звернути вашу увагу на CFN-Output. CDK подумав за нас і вигадав таку фішку, як аутпут, через яку ми можемо щось виводити. Щоб працювати з frontend, ми повинні знати endpoint GraphQL, на який ми будемо звертатися, і ми також повинні знати цей API-ключ, який ми будемо використовувати для з'єднання з GraphQL. Тому ми виводимо їх в CFN-Output. І наступний крок, який я покажу трошки пізніше, наглядно, але під час деплою наших змін, цей ендпоінт може змінюватися, так само як і APK буде згенеровано для нас, і ці дані будуть записані в JSON-файл, який ми будемо використовувати на фронтенді як конфігурацію. Інша річ, яка нам потрібна, крім AppSync і таблиці, це сами лямбди, які оброблять записи. Вони також створюються... В принципі, я не буду глибоко на них зупинятися. Вони також мають свої властивості, які вже відносяться до лямбд.
Отже, нам потрібно вказати runtime, нам потрібно вказати обробник, нам потрібно вказати, де знаходиться код. У цьому випадку він знаходиться в папці lambda.graphql. Отже, тут у мене є папка lambda, папка graphql, і тут розташовані самі лямбди, які відповідають за створення. Так, наприклад, так виглядає створення користувача в таблиці DynamoDB. Для цього я вже використовую AWS SDK, щоб маніпулювати даними, які вже є в таблиці.
І ще одне важливе, нам потрібно вказати джерело даних для нашого GraphQL. Ці лямбди, які ми визначили, ми повинні вказати як джерело даних для GraphQL. Це робиться на 26-й строці. Також ми визначаємо резольвери для GraphQL-запитів і мутацій. Нам потрібно отримати користувача за його ідентифікатором, отримати всіх користувачів, створити користувача, оновити користувача та видалити користувача. Це все, що стосується лямбд.
І останнім кроком є надання доступу. Так само за допомогою AWS SDK ми можемо не тільки створювати сервіси, але і надавати доступ одному сервісу до іншого. Для демонстрації я використовував метод grant.fullaccess, який є в таблиці. Насправді в реальному проєкті я б рекомендував не використовувати fullaccess, а давати стільки прав, скільки потрібно лише для створення та читання даних з таблиць. Окей, давайте спробуємо це розгорнути. Перед цим ми запускаємо наш відомий вже CDK Bootstrap і деплой. Як бачите, CDK перевірив, які зміни потрібно застосувати, і запитав, чи ми дійсно хочемо це робити. Ми можемо вказати yes, і процес розгортання розпочнеться. Також ми можемо побачити це в стеку CloudFormation. Наш стек зараз знаходиться в стані review in progress, create in progress.
Поки він створюється, давайте, можливо, зазирнемо, щоб переглянути ресурси. Для нас була створена таблиця користувачів. Ми бачимо, що вона вже створена. GraphQL зараз в стані in progress, і AppSync Lambda також в стані in progress. Оскільки таблиця вже створена, і щоб не витрачати час на чекання, давайте перейдемо до DynamoDB. Ми бачимо таблиці. Users table вже створена. Ми можемо її відкрити. На даний момент тут немає жодних записів. Як стосується нашого стеку? Create complete.
Як бачите, ми бачимо, що все вдалося створити. Я пропоную перейти до AppSync. І продемонструю, як ви можете з цим працювати за допомогою консолі. Ми бачимо назву нашого AWS AppSync. Ми бачимо ендпоінт, який був створений. Ми бачимо AppID. І ми бачимо APK. Єдине, що я трошки забув, коли деплоював це, є одна важлива річ. Як я говорив щодо фронтенду, ми повинні будемо мати ці виводи на фронтенді.
Щоб їх використовувати, нам потрібно буде, під час деплою, вказати наступне. Куди помістити виводи? Для цього ми можемо зробити... Ми бачимо наші виводи тут, зараз в консолі, але переносити їх з консолі є не дуже зручно, тому ми можемо це автоматизувати і зробити так. Outputs files. Outputs file. І шлях, куди ми хочемо помістити наш вивід. У цьому випадку я хочу помістити його в папку front-end. В папку SRC. APK, Export. Зроблено. Так, зараз у нас повинна бути створена папка. Давайте перевіримо, чи дійсно це сталося. Для цього перейдемо в папку front-end. В SRC, CDK, Export, JSON. Ми бачимо файлик, який має Absinthe-K і Endpoint. Відмінно.
Тепер повернемося до AWS консолі. Якщо ми перейдемо в GraphQL, ми можемо подивитися на якийсь query. Пропоную створити перший mutation і створити юзера. Для цього вкажемо якийсь email, ID. DynamoDB не вміє сам генерувати ID. На фронті ми будемо використовувати ID для цього. Тут зараз я пропишу якусь ID сам. І test name, наприклад. Тепер ми можемо це все виконати. Бачите, що воно виконалося. І щоб пересвідчитися, що воно виконалося, перейдемо в DynamoDB табличку. І так, наш юзер створився. Бачимо, що він є. Так само ми можемо, наприклад, викликати query, юзер list, наприклад, нам потрібно email і name. Так, ми бачимо, що юзер для нас повертається. З точки зору AWS все працює. Так само ми можемо пересвідчитися, що наші лямбди були задеплоєні. Тут ми бачимо лямбди. Можемо подивитися їхній код. Так, всі функції, які були написані для створення, видалення юзерів, вони є тут.
Окей. Останній крок, що нам залишилося, це змусити наш фронт-енд працювати не з mocks, а з реальним upsync endpoint. Для цього перейдемо в код. Перейдемо в front-end. Front-end – це React-апка, як я говорив. Для економії часу я наперед заготовив в кожному з компонентів такі речі, як кастомні хуки для витягування, наприклад, для витягування юзерів. Він виглядає таким чином, ми використовуємо Apollo Client, як я казав, для того, щоб витягнути список юзерів. І я прописав, якщо не mocks, то ми тягнемо дані з Apollo Client, а якщо mocks, то ми тягнемо їх з mocks.
Відповідно, ми можемо просто перейти в user table, видалити сюди mock. Навіть спочатку заберемо його в get users і подивимось, чи все працює. Так, зараз ми бачимо цього юзера, який був у нас в DynamoDB. Давайте заберемо mock з видалення юзера. Так само нам потрібно розкоментувати код, для того, щоб коли ми видалимо юзера, працював Apollo Cache. Так, спробуємо видалити юзера. Бачимо, що юзер видалений. Також ми можемо зробити з Edit, Create. Так, виходить все. Подивимось, як працює створення юзера через консоль.
Бачимо, що запит прийшов, і юзер створився. Перевіримо, чи він створився в DynamoDB табличці. Так, він створився в DynamoDB табличці. Отже, ми переконались, що все працює. І останнім кроком, після тестування всього, я пропоную прибрати за собою. Зараз CDK спитає, чи ми дійсно хочемо видалити. Так, ми дійсно хочемо видалити наш стек і всі пов'язані з ним ресурси. І ми бачимо, що процес видалення розпочався. Загалом можемо перейти до заключної частини слайдів і підбити підсумки.
Отже, якщо говорити про підсумки реального проекту, на якому я працював, то він був трошки складніший і використовував більше сервісів. Ми використовували DynamoDB таблиці, SQS. Мали три різних черги, які ми мусили якось об'єднувати. З старим монолітом працювали через SQS-черги і старалися не торкати його. Ми під'єднувались до черг, які використовував він, і будували цю нову систему. І я можу сказати, що систему ми збудували. Проблема, з якою ми стикнулися, була та, що кожен розробник працював окремо. Під кожного розробника розгортався окремий AWS Developer Account. Я трошки обманув вас, коли говорив, що це було без DevOps. DevOps були задіяні для створення цих акаунтів і управління доступом, щоб не витрачати багато коштів.
Але вже всередині цієї пісочниці, яка нам була виділена, ми могли бавитися, як хотіли, в принципі, що ми і робили. Немає ніякого нормального емулятора для того, щоб бавитися з цим локально. Емулятор, який є, він нам не підходив. Деколи на емуляторі все працює, на реальному AWS-аккаунті не працювало. Відповідно, ми від цього відмовилися, і ми працювали, напряму, з реальними AWS-аккаунтами. І проблема, яка також в нас була, це тести. Одно з вимог було, що ми маємо покрити хоч якось НТН-тестами, там не говорилося ні про яке повне покриття за три місяці. Але хоч щось, основні речі ми мусили покрити.
І, мінімум, для себе ми мусили впевнитися, що дані, які приходять до нас, не ламають нічого на моноліті і не ламають нічого на цій новій системі. Відповідно, НТН-тести, в кінці кінців ми їх все-таки написали, використовуючи ще один AWS-сервіс, називається Step Functions, який, в принципі, як працював? Він запускався Step-функцією. Першим її кроком було підняти це все середовище, другим кроком прогнати тести, третім кроком, якщо тести окей, якщо раніше був фейл, відправити через SNS-пуш, що щось не так, і почистити все за собою. Це, насправді, не дуже була тривіальна задача, не дуже типова.
Готових рішень ми не знайшли, відповідно, ми це писали самі. Але рішення для цього є. А які плюси? Плюси, найбільшим плюсом, я вважаю, це те, що ми пишемо інфраструктуру кодом. Особисто мені, більше працював з АОП, для мене взагалі не було проблеми переключитися і описувати нашу інфраструктуру об'єктами. Друге – це те, що ми можемо деплоїти одночасно і код, і сервіс. Тобто, якщо нам потрібен якийсь новий сервіс, ми просто його створюємо, і просто за допомогою CDK Deploy він опиняється в нас в AWS-аккаунті. Дуже все просто. Ми не тратимо операційний час на те, щоб прокомунікувати з DevOps, що нам потрібен такий сервіс, який нам потрібен і так далі.
І так само ми можемо дуже легко і дуже гнучко зменшувати наші permissions . Я маю на увагу permissions для сервісів, які надаються одним сервісам для доступу до інших. Так само ми все робимо з коду. Останній пункт, який я говорив, що можливо написати аплікацію без DevOps, але насправді не дуже правда, тому що DevOps нам потрібні були для того, щоб насетапити нам аккаунти і permissions, хоча ми могли це зробити насправді самі, але це більше про зони відповідальності.
І так само девелопери. У нас було три девелопери, які це писали, і всі вони були, в принципі, дотичані до AWS, всі мали якийсь попередній досвід з AWS і розуміли, які сервіси нам потрібні, знали, якими сервіси будемо використовувати, як їх конфігурувати. Відповідно, це було все так. Останнім – це набір лінків, які я підготував. Лінки про AWS CDK, конструкції – це набір конструкцій, які ви можете брати готові і використовувати. Ця демо-презентація залита на мій GitHub, також тут є в лінках. І мій LinkedIn, мій Facebook. Дякую вам всім за увагу. Можемо переходити до запитань.