Робота з NestJs + розгортання на Elastic Beanstalk

Добрий день, друзі! Як у вас справи? Чи цікавить вас NestJS? А як щодо NestJS на AWS? Так, знаю, звучить захопливо, давайте перейдемо до нашої маленької презентації.

Зачекайте, не поспішайте. Спершу, можливо, варто ознайомитися з цією статтею про Prisma з AWS Lambda. Я б сказав, що вона вийшла достатньо гарною, але, зрештою, вирішувати вам. Вірю, що там є більше деталей щодо Prisma та Apollo Server, тому, якщо хочете, я буду радий, якщо ви ознайомитесь з цим. А тепер повернемося до NestJS, можете приєднуватись.

Спершу, хто цей NestJS і чому я маю про це турбуватися?

Nest (NestJS) — це фреймворк для створення ефективних, масштабованих серверних застосунків на Node.js. Він використовує прогресивний JavaScript, побудований на TypeScript, і поєднує елементи ООП (об’єктно-орієнтованого програмування), ФП (функціонального програмування) і ФРП (функціонального реактивного програмування), але ви могли прочитати про це все в документації. Якщо коротко, це фреймворк, який виглядає та працює дуже схоже на Angular, і з ним цікаво працювати. Щоб ще простіше, це Express на стероїдах з обов'язковою структурою проєкту, досить непоганим CLI та безліччю розширень від сторонніх розробників. Думаю, цього досить, тому давайте приступимо до справи і створимо щось своє. Я думав про GraphQL, Prisma (це ORM) і, можливо, MinIO, щоб завершити це на висоті. О, і документація! Документація теж важлива, вибачте за непослідовність, я трохи поспішаю.

Налаштування проєкту (так, ми будемо слідувати статті в документації)

Документація радить почати з установки CLI, давайте так і зробимо.
npm i -g @nestjs/cli або yarn global add @nestjs/cli (мені не важливо, вибирайте, що вам зручніше). Потім після установки виконуємо nest new project-name. До речі, майстер налаштування запитає, який пакетний менеджер ви хочете використовувати. Класно, чи не так?
Тепер давайте подивимося, що ми отримали.

pic

Рисунок 1. Дуже схоже на Angular, чи не так?

Здається, що цей NestJS зробив багато роботи за нас. Все налаштовано (включаючи тести!) і готово до роботи. Можете ознайомитися і подивитися, як це працює, я тут почекаю, обіцяю.

GraphQL

За замовчуванням Nest CLI створює простий REST API. Але не переживайте, є стаття, яка допоможе з міграцією до GraphQL. Думаю, ми будемо слідувати за нею і подивимося, до чого це нас приведе.
yarn add @nestjs/graphql @nestjs/apollo graphql apollo-server-express
щоб встановити необхідні залежності. Тепер є 2 варіанти: можемо використовувати підхід schema first або code first. Я вибираю підхід code first, тому будемо використовувати його, а оскільки ви застрягли з моєю статтею, — вам, здається, немає вибору, так? Жартую, не соромтесь почитати, що це за підходи, сказав же, що документація чудова.

Тепер давайте перейдемо до app.module.ts (він знаходиться в src) і трохи оновимо його, він має виглядати ось так:

import { Module } from '@nestjs/common';  
import { GraphQLModule } from '@nestjs/graphql';  
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';  
@Module({  
 imports: [  
 GraphQLModule.forRoot({  
 driver: ApolloDriver,  
 playground: true,  
 autoSchemaFile: true,  
 sortSchema: true,  
 }),  
 ],  
})export class AppModule {}

До речі, нам не знадобляться src/app.controller, src/app.service, src/app.controller.spec і test/, так що можете їх видалити. Тепер давайте визначимо декілька резолверів і перевіримо, чи працює наш додаток.
"nest g module books"для створення модуля і давайте створимо файлbooks.resolver.tsв цьому модулі (так, вручну). І, ймовірно, нам знадобиться визначення типу GraphQL, тож давайте визначимо його одразу (додайтеbook.model.tsв ту ж саму папку).
До речі, зверніть увагу на
app.module`. Він був оновлений, коли ви створили новий модуль. Класно!

src/books/book.model.tsimport { Field, Int, ObjectType, ID } from '@nestjs/graphql';@ObjectType()  
export class Book {  
 @Field(() => ID)  
 id: string; @Field()  
 title: string;   

 @Field()  
 author: string; @Field(() => Int, { nullable: true })  
 price?: number;  
}

Зовсім не важливе зауваження: Medium починає мене дратувати, додаючи посилання на Twitter до кожного @ символу. Будь ласка, зупиніться, це реально боляче видаляти їх.

Нічого складного, але цього достатньо для демонстрації. Резолвер виглядатиме ось так:

src/books/books.resolver.tsimport { Args, Query, Resolver } from '@nestjs/graphql';  
import { Book } from './book.model';  
import { BooksService } from './books.service';@Resolver(() => Book)  
export class BooksResolver {  
 constructor(private booksService: BooksService) {} @Query(() => Book)  
 async book(@Args('id', { type: () => String }) id: string) {  
 return this.booksService.findOneById(id);  
 }  
}

Можливо, ви помітили books.service, ми ще не створили цей файл, тож давайте це зробимо. nest g service books зробить свою справу (так, він оновить модуль). Також ви можете побачити в цьому фрагменті, як працює ін’єкція залежностей в Nest. Чудова функція, якщо ви запитаєте мене. Щоб наш сервіс працював, нам знадобиться база даних, тож давайте зробимо крок назад і налаштуємо Prisma (і, можливо, ще щось, побачимо, як піде).

Звичайно, є ще одна стаття для Prisma

Почнемо з залежностей. yarn add -D prisma, yarn prisma init і давайте оновимо prisma/schema.prisma:

prisma/schema.prisma generator client {  
 provider = "prisma-client-js"  
}datasource db {  
 provider = "postgresql"  
 url = env("DATABASE_URL")  
}model Book {  
 id String [@id](http://twitter.com/id) [@default](http://twitter.com/default)(cuid())  
 title String  
 author String  
 price Int?  
}

Він очікує, що у нас буде встановлено DATABASE_URL в змінних середовища, тож давайте додамо docker-compose.yml з усіма необхідними сервісами:

version: '3'  
services:  
 postgres:  
 image: postgres:14.0  
 restart: always  
 environment:  
 - POSTGRES_PASSWORD=password  
 - POSTGRES_USER=postgres  
 ports:  
 - '5432:5432'  
 volumes:  
 - postgres:/var/lib/postgresql/data  
 minio:  
 image: minio/minio  
 ports:  
 - '9000:9000'  
 - '9001:9001'  
 volumes:  
 - ./storage/minio:/data  
 command: server /datavolumes:  
 postgres:

І docker compose up -d має зробити свою магію, спробуйте це. Мабуть, одного дня ми також замінемо наш додаток на Docker, але це вже зовсім інша історія. О, і не забудьте оновити .env.

# Змінні середовища, оголошені в цьому файлі, автоматично доступні для Prisma.  
# Дивіться документацію для більше деталей: [https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema](https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema)# Prisma підтримує нативний формат рядка з’єднання для PostgreSQL, MySQL, SQLite, SQL Server, MongoDB та CockroachDB.  
# Дивіться документацію для всіх опцій рядків підключення: [https://pris.ly/d/connection-strings](https://pris.ly/d/connection-strings)DATABASE_URL="postgresql://postgres:password@localhost:5432/mydb?schema=public"

Давайте перевіримо нашу наполегливу роботу та згенеруємо початковий скрипт міграції (хто сказав, що ми не можемо поєднувати?) за допомогою yarn prisma migrate dev --name init. Тепер давайте додамо @prisma/client, цей пакет нам знадобиться для написання запитів (yarn add @prisma/client, на випадок, якщо ви забули).
"nest g module books"для створення модуля і давайте створимо файлbooks.resolver.tsв цьому модулі (так, вручну). І, ймовірно, нам знадобиться визначення типу GraphQL, тож давайте визначимо його одразу (додайтеbook.model.tsв ту ж саму папку).
До речі, зверніть увагу на
app.module`. Він був оновлений, коли ви створили новий модуль. Класно!

src/books/book.model.tsimport { Field, Int, ObjectType, ID } from '@nestjs/graphql';@ObjectType()  
export class Book {  
 @Field(() => ID)  
 id: string; @Field()  
 title: string;   

 @Field()  
 author: string; @Field(() => Int, { nullable: true })  
 price?: number;  
}

Зовсім не важливе зауваження: Medium починає мене дратувати, додаючи посилання на Twitter до кожного @ символу. Будь ласка, зупиніться, це реально боляче видаляти їх.

Нічого складного, але цього достатньо для демонстрації. Резолвер виглядатиме ось так:

src/books/books.resolver.tsimport { Args, Query, Resolver } from '@nestjs/graphql';  
import { Book } from './book.model';  
import { BooksService } from './books.service';@Resolver(() => Book)  
export class BooksResolver {  
 constructor(private booksService: BooksService) {} @Query(() => Book)  
 async book(@Args('id', { type: () => String }) id: string) {  
 return this.booksService.findOneById(id);  
 }  
}

Можливо, ви помітили books.service, ми ще не створили цей файл, тож давайте це зробимо. nest g service books зробить свою справу (так, він оновить модуль). Також ви можете побачити в цьому фрагменті, як працює ін’єкція залежностей в Nest. Чудова функція, якщо ви запитаєте мене. Щоб наш сервіс працював, нам знадобиться база даних, тож давайте зробимо крок назад і налаштуємо Prisma (і, можливо, ще щось, побачимо, як піде).

Звичайно, є ще одна стаття для Prisma

Почнемо з залежностей. yarn add -D prisma, yarn prisma init і давайте оновимо prisma/schema.prisma:

prisma/schema.prisma generator client {  
 provider = "prisma-client-js"  
}datasource db {  
 provider = "postgresql"  
 url = env("DATABASE_URL")  
}model Book {  
 id String [@id](http://twitter.com/id) [@default](http://twitter.com/default)(cuid())  
 title String  
 author String  
 price Int?  
}

Він очікує, що у нас буде встановлено DATABASE_URL в змінних середовища, тож давайте додамо docker-compose.yml з усіма необхідними сервісами:

version: '3'  
services:  
 postgres:  
 image: postgres:14.0  
 restart: always  
 environment:  
 - POSTGRES_PASSWORD=password  
 - POSTGRES_USER=postgres  
 ports:  
 - '5432:5432'  
 volumes:  
 - postgres:/var/lib/postgresql/data  
 minio:  
 image: minio/minio  
 ports:  
 - '9000:9000'  
 - '9001:9001'  
 volumes:  
 - ./storage/minio:/data  
 command: server /datavolumes:  
 postgres:

І docker compose up -d має зробити свою магію, спробуйте це. Мабуть, одного дня ми також замінемо наш додаток на Docker, але це вже зовсім інша історія. О, і не забудьте оновити .env.

# Змінні середовища, оголошені в цьому файлі, автоматично доступні для Prisma.  
# Дивіться документацію для більше деталей: [https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema](https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema)# Prisma підтримує нативний формат рядка з’єднання для PostgreSQL, MySQL, SQLite, SQL Server, MongoDB та CockroachDB.  
# Дивіться документацію для всіх опцій рядків підключення: [https://pris.ly/d/connection-strings](https://pris.ly/d/connection-strings)DATABASE_URL="postgresql://postgres:password@localhost:5432/mydb?schema=public"

Давайте перевіримо нашу наполегливу роботу та згенеруємо початковий скрипт міграції (хто сказав, що ми не можемо поєднувати?) за допомогою yarn prisma migrate dev --name init. Тепер давайте додамо @prisma/client, цей пакет нам знадобиться для написання запитів (yarn add @prisma/client, на випадок, якщо ви забули).
Elastic Beanstalk має стандартну групу безпеки, яка працює як брандмауер для інстансів.

Консоль Elastic Beanstalk використовується для взаємодії з Elastic Beanstalk. Також можна використовувати командний інтерфейс AWS або високорівневий інтерфейс.

Після того як ви створите та розгорнете додаток, інформація про ваш додаток стане доступною в консолі Elastic Beanstalk.

Чому Elastic Beanstalk?

Це залежить від того, хто питає.
Клієнти: EB спрощує управління інфраструктурою, навіть якщо вона складна. Крім того, ви можете налаштувати додаткові ресурси AWS через .ebextensions, він працює з більшістю платформ і середовищ та загалом полегшує життя розробникам. (легке життя для розробника == дешевша розробка)
Розробники: Ви читали розділ «Що таке EB»? Він перетворює гарбузи на карети. Ну, я ж не повинен пояснювати?

Попередні вимоги

Нам знадобиться вже готовий додаток на NestJS (добре, що ми його побудували, так?), акаунти Github та AWS. Залишимо GitHub на деякий час, я повідомлю вас, коли він буде потрібен. Увійдіть в AWS і спробуйте знайти EB, я почекаю. Якщо у вас виникнуть труднощі — не соромтеся звертатися до мене через пагер або надіслати голуба.

Весела частина

Якщо ви використовуєте yarn і зневажаєте npm усім своїм серцем, вам потрібно буде встановити yarn у хук перед збіркою, інакше станеться щось погане. У будь-якому разі, ось скрипт, просто покладіть його в директорію і забудьте про це:

.platform/hooks/prebuild/yarn.sh# додати yarn  
sudo wget [https://dl.yarnpkg.com/rpm/yarn.repo](https://dl.yarnpkg.com/rpm/yarn.repo) -O /etc/yum.repos.d/yarn.repo #встановити yarn  
sudo yum -y install yarn# встановити залежності  
cd /var/app/staging/  
yarn

Вірогідно, у вас ще немає директорії .platform, тому час її створити. Ну ж, всі круті хлопці вже мають її, ви запізнилися на вечірку. Але ще є час виправити цю фатальну помилку. Швидше! О, і не забудьте зробити yarn.sh виконуваним, інакше станеться щось погане.
chmod +x .platform/hooks/prebuild/yarn.sh

І це все, друзі (ви вже уявляли картину з Поркі Піґом, чи не так?), ви готові до роботи! Після того, як ви розгорнете свій код в EB, залежності будуть встановлені через yarn, а npm може спокійно відпочивати в п̶и̶с̶с̶і̶ мирі, де йому і належить бути.

pic

Рисунок 2: Консоль управління AWS

Після того, як ви клікнете на Elastic Beanstalk, ви будете направлені на сторінку панелі управління EB. Давайте натиснемо «Створити додаток» і подивимося, як це буде.

pic

Рисунок 3: Панель управління AWS Elastic Beanstalk

Ви можете використовувати майстра «Створення веб-додатку» в консолі для створення вашого тестового додатку.

  1. Вкажіть інформацію про ім’я вашого додатку Elastic Beanstalk (наприклад, my-app, але я назву його «video-app», оскільки я закінчив налаштовувати MinIO, і мій додаток знає, як працювати з відео, але ви ще не зробили цього, тож використовуйте свою уяву. Може бути «unicorn-app», мені байдуже).
  2. Клікніть на випадаючий список «Платформа» і виберіть вашу платформу (я вірю, ми використовуємо NodeJS, але звідки мені знати). Гілка платформи та версія платформи будуть автозаповнені (якщо тільки вам не потрібна NodeJS 8.x, у такому разі виберіть її).
  3. Виберіть «Зразок додатку» як код додатку і натисніть кнопку «Створити додаток». Це займе кілька хвилин для запуску вашого додатку.

pic

Рисунок 4. Створення веб-додатку

pic

Рисунок 5. Все ще створюється веб-додаток

pic

Рисунок 6. Все ще чекаємо, поки EB створить додаток

Коли ваш додаток буде успішно запущено, ви побачите зелену галочку.
Це достатньо велике, щоб не пропустити.

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

pic

Рисунок 7. Сторінка огляду середовища

У верхній частині сторінки огляду ви побачите URL середовища під назвою середовища, натисніть на цей URL, щоб подивитися на свій гарбуз.

pic

Рисунок 8. Посилання та стрілка

pic

Рисунок 9. Чудовий зразок додатку

Тепер, коли ваш додаток успішно розгорнуто, ми налаштуємо конвеєр для CI/CD за допомогою AWS CodePipeline (без скорочень?!). Послуги AWS CodePipeline допомагають вам створювати, тестувати та розгортати код, коли в вашому вихідному коді є зміни. По суті, це як і будь-який інший конвеєр, але на AWS.

Ми дізнаємося, як створити простий конвеєр, який витягує код з GitHub репозиторію та автоматично розгортає на AWS EC2 (ось він! Elastic Compute Cloud. Бачите, що вони зробили тут?) інстансах. До речі, не забудьте надіслати ваш код у репозиторій. Ми всі буквально любимо створювати репозиторії з одним комітом, чи не так?

Налаштування CodePipeline

Знайдіть службу CodePipeline за допомогою пошукової стрічки та виберіть службу зі списку, як показано на малюнку нижче:

pic

Рисунок 10. CodePipeline в пошуковій стрічці

На панелі управління CodePipeline виберіть "Create Pipeline" (Створити конвеєр).

pic

Рисунок 11. Кнопка "Create Pipeline"

pic

Рисунок 12. Майстер створення конвеєра

До речі, джерело AWS має бути в тому ж регіоні, що й ваш конвеєр.

Крок 1: Вибір налаштувань конвеєра

  1. У полі "Pipeline Name" введіть назву конвеєра, наприклад: "videoApp-pipeline". Назву конвеєра неможливо змінити після його створення.
  2. У полі "Service Role" виберіть "New Service Role" (Нова роль служби), це дозволить CodePipeline створити нову роль служби в IAM (вам обов'язково слід прочитати про IAM, найсмішніше, що я коли-небудь читав).
  3. Я думаю, ви можете залишити "Role name" як є.
  4. Натисніть кнопку "Next" (Далі), як показано на малюнку вище.

Крок 2: Додати етап джерела

  1. На сторінці Source (Джерело), під Source Provider (Постачальник джерела), виберіть GitHub (Version 1).
  2. Якщо ви ще не створили з'єднання з GitHub, натисніть на кнопку "Connect to GitHub" (Підключитися до GitHub), щоб надати дозволи AWS CodePipeline на доступ до вашого репозиторію GitHub. Це допоможе AWS CodePipeline завантажувати ваші коміти з GitHub до AWS Code Pipeline.
  3. З'явиться сторінка для підключення до GitHub, як показано нижче.

pic

Рисунок 13. Вікно підключення

  1. Натисніть "Authorize aws-codesuite". Це надасть дозвіл на підключення до GitHub.
  2. Після успішного підключення ви побачите повідомлення про успішне підключення в зеленому полі.

pic

Рисунок 14. Все ще на етапі джерела

До речі, я вірю, ви бачите ту інформаційну секцію, яка говорить не вибирати версію 1.

pic

Рисунок 15. Все той самий етап, але з гарно прихованим ім'ям репозиторію

  1. Для репозиторію виберіть ім'я вашого репозиторію (якщо вам потрібне це пояснення).
  2. У полі Branch (Гілка) виберіть ім'я гілки, на якій ви хочете, щоб ваш конвеєр виявляв вихідний код. (Не те, щоб у нас було багато гілок, але все ж)
  3. Виберіть GitHub webhooks (веб-хуки GitHub), це дозволить вам запускати конвеєр щоразу, коли будуть зміни в коді репозиторію.
  4. Натисніть кнопку NEXT (Далі).

pic

_Рисунок 16.
Вищезгадані кроки, візуалізовані

Крок 3: Додати етап побудови

1) Пропустіть етап побудови, просто натисніть кнопку “skip”, заради бога і мого розуму, я вас благаю.

pic

Рисунок 17. Ось тут, “Skip build stage”

pic

Рисунок 18. Звісно, ми впевнені.

Крок 4: Додати деталі етапу розгортання

  1. В полі Deploy provider виберіть AWS Elastic Beanstalk.
  2. У полі Region виберіть регіон, де ви створили свій додаток.
  3. У полі Application введіть ім’я або виберіть ім’я, яке ви вже створили в консолі EB.
  4. У полі Environment name введіть або виберіть ім’я, яке ви вже створили в консолі EB.
  5. Натисніть кнопку “Next”.
  6. Вас перенаправить на сторінку огляду конвеєра.

pic

Рисунок 19. Вищезгадані кроки, візуалізовані

Деталі сторінки огляду конвеєра:

  • Налаштування конвеєра,
  • Додати етап джерела
  • Додати етап побудови
  • Додати етап розгортання

Натискайте кнопку “Create pipeline” (Створити конвеєр).

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

pic

Рисунок 20. Панель конвеєра

pic

Рисунок 21. Сторінка огляду середовища після розгортання

Кінь

Я вважаю, що ми завершили. Ви можете натискати на посилання, яке я вже згадував, під назвою середовища, щоб перевірити, чи все працює правильно. Якщо ні — ви знаєте, як зі мною зв’язатися. Голуби та пейджер!
І з цим доволі простим, але виснажливим натисканням кнопок “next” ми змогли перетворити наш гарбуз на карету. І підсумовуючи все, скажу тільки, що EB — потужний інструмент, він не вимагає багато розуміння з боку розробників і дозволяє легко налаштовувати та керувати інфраструктурою. І якби цього було мало, хлопці з Amazon вирішили побудувати це на основі CloudFormation, що означає, що ви можете автоматично надавати інші ресурси AWS прямо там. Дуже круто, якщо ви запитаєте мене.

Подяка Nazarii Marko

Перекладено з: Working with NestJs + deployment on Elastic Beanstalk

Leave a Reply

Your email address will not be published. Required fields are marked *