Всеосяжний посібник із розгортання мікросервісів на базі NestJS за допомогою Docker та Apache

В сучасну еру розробки програмного забезпечення архітектура мікросервісів стала основою для створення масштабованих і підтримуваних додатків. NestJS, прогресивний фреймворк для Node.js, надає розробникам потужний набір інструментів для створення ефективних і модульних бекенд-додатків. Однак, розгортання таких додатків надійним і масштабованим чином часто стає значним викликом.

Цей посібник зосереджується на розгортанні NestJS мікросервісів за допомогою Docker та Apache як зворотного проксі-сервера. Поєднуючи ці технології, ви можете забезпечити стабільну поведінку додатків у різних середовищах, спростити процеси розгортання та покращити управління навантаженням.

Чому NestJS для Мікросервісів?

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

Чому Використовувати Docker?

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

Роль Apache у Розгортанні

Apache HTTP Server, один з найбільш використовуваних веб-серверів, виступає як зворотний проксі-сервер у цьому налаштуванні. Він маршрутизує вхідні запити клієнтів до відповідних бекенд-сервісів, обробляє термінацію SSL і навіть може допомогти з кешуванням та балансуванням навантаження.

Що Ви Дізнаєтесь

Наприкінці цього посібника ви:

  1. Налаштуєте та контейнеризуєте кілька NestJS мікросервісів за допомогою Docker.
  2. Налаштуєте Docker Compose для оркестрації мікросервісів.
  3. Налаштуєте Apache як зворотний проксі для маршрутизації трафіку до ваших мікросервісів.
  4. Розгорнете всю інфраструктуру на сервері і протестуєте її від початку до кінця.

Цей посібник передбачає базові знання з Node.js, Docker та Apache. Якщо ви новачок у будь-якому з цих інструментів, не хвилюйтесь — кожен крок буде детально пояснено, щоб ви могли слідувати за інструкціями.

Перед тим, як почати, якщо ви новачок у NestJS або не працювали з мікросервісами в NestJS раніше, я настійно рекомендую ознайомитися з моїм Посібником для Початківців по Мікросервісах у NestJS, опублікованим на The New Stack. У цьому посібнику я проводжу вас через основи налаштування мікросервісів за допомогою NestJS, включаючи створення основної структури, реалізацію комунікації між сервісами та обробку базової логіки сервісів. Дотримання цього посібника допоможе вам підготувати ваші мікросервіси до розгортання, що є метою цього посібника. Щоб не відставати, я коротко охоплю тут основні концепції з цієї статті для освіження пам'яті. Після цього ми перейдемо до практичних кроків з розгортання мікросервісів і налаштування вашого домену для підключення до бекенд-сервісів.

Для початку створіть папку у будь-якому місці на вашому комп'ютері та клонуюйте цей репозиторій. Ви отримаєте код проекту з посібника для початківців по мікросервісах.

pic

Зміст клонованого проекту

Клонирована папка містить 3 папки:

  1. Папка api-gateway: Це головна директорія нашого мікросервісу api-gateway. Гейтвей відповідає за отримання всіх API запитів від клієнта та перенаправлення їх до відповідного мікросервісу.
  2. Папка article-mgt: Це головна директорія нашого мікросервісу керування статтями. Цей мікросервіс відповідає за CRUD операції над статтями.
  3. Папка reader-mgt: Це головна директорія нашого мікросервісу керування читачами.
    Це відповідає за виконання операцій CRUD для наших читачів.

Тепер, коли ми знаємо, чого очікувати від кожної папки, відкриємо всю папку проекту за допомогою будь-якого текстового редактора на ваш вибір, у моєму випадку це VS Code. Ви можете завантажити його тут.

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

pic

Інсталяція залежностей для мікросервісу api-gateway

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

Спочатку налаштуємо механізм комунікації для проекту. Цей проект використовує NATS для міжсервісної комунікації, як можна побачити в мікросервісі api-gateway, де імпортується nats-module в app.module.ts. Щоб все запрацювало, ми встановимо nats-client і запустимо його локально. Після цього налаштуємо базу даних і протестуємо додаток, щоб переконатися, що все працює, як очікується.

Якщо у вас ще не встановлено NATS, відкрийте термінал локально. Зазначте, що ми не встановлюємо це безпосередньо в наш проект. Для користувачів Linux і macOS, встановіть сервер NATS за допомогою brew install nats-server і запустіть nats-server, щоб стартувати сервіс. Для користувачів Windows використовуйте choco install nats-server. Якщо choco не розпізнається, переконайтеся, що Chocolatey встановлений, виконавши:

Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))

Це підготує сервер NATS для обробки комунікації між нашими мікросервісами.

pic

nats-server готовий обробляти наші запити

Якщо ми наближаємося до фінального етапу, щоб переконатися, що наш проект працює локально, коли ви відкриєте папки reader-mgt або article-mgt і перевірите файл .env, ви знайдете рядок підключення до бази даних, який виглядає так:

DATABASE_URL="mysql://dev:A%24%24rsenal%23256%21@localhost:3306/microservices"

Цей рядок підключення використовується для з’єднання мікросервісу з локальною базою даних MySQL. База даних називається microservices і працює на стандартному порту MySQL, 3306, на локальному хості. Ім’я користувача — dev, а пароль відформатований, оскільки MySQL мав проблеми з обробкою початкового не відформатованого пароля, що містить спеціальні символи ('A$$rsenal#256!'). MySQL може мати проблеми зі спеціальними символами в паролях, такими як #, ! чи $, тому ці символи потрібно правильно екранізувати або форматувати для забезпечення плавного підключення. Наприклад, вам, можливо, потрібно буде замінити певні символи або використовувати лапки навколо пароля.

Якщо ви ще не встановили XAMPP, ви можете завантажити та встановити його звідси, а потім запустити. Після запуску XAMPP створіть базу даних з будь-якою назвою. Проте не забудьте оновити рядок підключення з вашими власними обліковими даними бази даних, оскільки вони можуть відрізнятися від моїх.

Після того як база даних буде налаштована, потрібно запустити міграції prisma, щоб схема вашої бази даних була синхронізована з визначеною схемою в проекті. Перейдіть до папки reader-mgt, а потім виконайте команду npx prisma generate, щоб ініціалізувати prisma.
Після цього виконайте команду npx prisma migrate dev, щоб застосувати міграцію до бази даних.

Тепер відкрийте 3 різні термінали і перейдіть у всі 3 папки. На цьому етапі ми можемо спробувати запустити проект і перевірити, чи він працює. Виконайте команду npm run start:dev у всіх трьох терміналах.

Якщо ви правильно виконали всі кроки, то ваші сервіси повинні запуститися без помилок.

pic

api gateway працює

pic

мікросервіс article-mgt працює

pic

мікросервіс reader-mgt працює

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

Ми надішлемо два POST-запити, по одному для кожного мікросервісу. Перший запит буде надісланий до мікросервісу reader-mgt для збереження нового читача, а другий — до мікросервісу article-mgt для збереження нової статті.

pic

Успішно створено нового читача

pic

Успішно створено нову статтю

Обидва записи були успішно створені, що підтверджує, що наш проект працює як очікується. Вітаю, що ви дійшли до цієї частини! Ми зараз на півдорозі до завершення статті. Далі ми підготуємо і розгорнемо наш проект на сервері Apache та налаштуємо доменне ім’я для його підключення. Слідкуйте за оновленнями!

Спочатку створимо файл docker-compose.yml у головній директорії проекту, а потім перейдемо до налаштування кожного окремого мікросервісу.

version: '3.8'  

services:  
 # MySQL Service  
 mysql:  
 image: mysql:latest  
 container_name: mysql  
 environment:  
 MYSQL_ROOT_PASSWORD: ''  
 MYSQL_DATABASE: microservices  
 MYSQL_USER: root  
 MYSQL_PASSWORD: ''  
 ports:  
 - "3306:3306"  
 volumes:  
 - mysql_data:/var/lib/mysql  
 networks:  
 - my_network  

 # NATS Service  
 nats:  
 image: nats:latest  
 container_name: nats  
 ports:  
 - "4222:4222"  
 - "8222:8222"  
 networks:  
 - my_network  

 # Gateway Service  
 gateway:  
 build: ./api-gateway  
 container_name: gateway  
 environment:  
 DATABASE_HOST: mysql  
 DATABASE_USER: root  
 DATABASE_PASSWORD: ''  
 DATABASE_NAME: microservices  
 NATS_URL: nats://nats:4222  
 ports:  
 - "3000:3000"  
 networks:  
 - my_network  

 # Reader Microservice  
 reader-mgt:  
 build: ./reader-mgt  
 container_name: reader-service  
 environment:  
 DATABASE_HOST: mysql  
 DATABASE_USER: root  
 DATABASE_PASSWORD: ''  
 DATABASE_NAME: microservices  
 NATS_URL: nats://nats:4222  
 networks:  
 - my_network  

 # Article Microservice  
 article-mgt:  
 build: ./article-mgt  
 container_name: article-service  
 environment:  
 DATABASE_HOST: mysql  
 DATABASE_USER: root  
 DATABASE_PASSWORD: ''  
 DATABASE_NAME: microservices  
 NATS_URL: nats://nats:4222  
 volumes:  
 - ./article-mgt/src:/usr/src/app/src  
 networks:  
 - my_network  

networks:  
 my_network:  
 driver: bridge  

volumes:  
 mysql_data:  
 driver: local

Ось детальне пояснення файлу docker-compose.yml та кожного розділу в ньому:

version: '3.8'

  • Це вказує на версію синтаксису Docker Compose, яку слід використовувати. У цьому випадку це версія 3.8, яка сумісна з новими версіями Docker.

services:

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

1.

mysql` Сервіс (MySQL База Даних)

mysql:  
 image: mysql:latest  
 container_name: mysql  
 environment:  
 MYSQL_ROOT_PASSWORD: ''  
 MYSQL_DATABASE: microservices  
 MYSQL_USER: root  
 MYSQL_PASSWORD: ''  
 ports:  
 - "3306:3306"  
 volumes:  
 - mysql_data:/var/lib/mysql  
 networks:  
 - my_network
  • image: Визначає, який образ використовувати для контейнера. Тут використовується офіційний образ MySQL (mysql:latest).
  • container_name: Назначає контейнеру ім’я mysql для зручності при взаємодії з ним.
  • environment: Визначає змінні середовища всередині контейнера.
  • MYSQL_ROOT_PASSWORD: '': Це встановлює пароль для root користувача MySQL. Тут він є порожнім, що означає, що пароль для користувача root не заданий.
  • MYSQL_DATABASE: microservices: Створює базу даних з ім’ям microservices всередині MySQL.
  • MYSQL_USER: root і MYSQL_PASSWORD: '': Визначає користувача MySQL з ім’ям root та порожнім паролем (це небезпечно для продакшн-середовища).
  • ports: Відкриває порт MySQL 3306 для хостової машини, щоб ви могли підключатися до нього ззовні контейнера (для локального тестування).
  • volumes: Прив’язує Docker том (mysql_data) для збереження даних за адресою /var/lib/mysql всередині контейнера. Це забезпечує збереження даних (бази даних) при перезапуску контейнера.
  • networks: Визначає, що сервіс mysql належить до мережі my_network, що дозволяє йому взаємодіяти з іншими сервісами цієї мережі.

2. nats Сервіс (NATS Система Повідомлень)

nats:  
 image: nats:latest  
 container_name: nats  
 ports:  
 - "4222:4222"  
 - "8222:8222"  
 networks:  
 - my_network
  • image: Використовує офіційний образ NATS (nats:latest) для контейнера.
  • container_name: Назначає контейнеру ім’я nats.
  • ports:
  • 4222:4222: Відкриває стандартний порт сервера NATS (4222) для хоста. Це використовується для зв’язку між мікросервісами.
  • 8222:8222: Відкриває порт для HTTP моніторингу NATS (8222) для налагодження та моніторингу.
  • networks: Як і для MySQL сервісу, сервіс NATS підключений до мережі my_network для внутрішнього зв’язку.

3. gateway Сервіс (API Gateway)

gateway:  
 build: ./api-gateway  
 container_name: gateway  
 environment:  
 DATABASE_HOST: mysql  
 DATABASE_USER: root  
 DATABASE_PASSWORD: ''  
 DATABASE_NAME: microservices  
 NATS_URL: nats://nats:4222  
 ports:  
 - "3000:3000"  
 networks:  
 - my_network
  • build: Визначає директорію (./api-gateway), де слід побудувати Docker-образ для сервісу gateway. Це вказує на файл Dockerfile всередині директорії gateway.
  • container_name: Назначає контейнеру ім’я gateway.
  • environment: Визначає змінні середовища, які будуть доступні всередині контейнера.
  • DATABASE_HOST: mysql: Вказує на сервіс mysql (з файлу docker-compose.yml), до якого потрібно підключитися для роботи з базою даних.
  • DATABASE_USER: root: Вказує ім’я користувача для підключення до бази даних.
  • DATABASE_PASSWORD: '': Вказує пароль для підключення до бази даних (порожній у цьому випадку).
  • DATABASE_NAME: microservices: Вказує на базу даних з іменем microservices.
  • NATS_URL: nats://nats:4222: Вказує на URL для NATS-зв’язку. Це вказує на сервіс nats за допомогою протоколу NATS і правильного порту (4222).
  • ports: Відкриває порт 3000 контейнера для хостової машини. Це дозволяє взаємодіяти з gateway через http://localhost:3000.
  • networks: Забезпечує, щоб сервіс gateway був частиною мережі my_network, що дозволяє йому взаємодіяти з MySQL і NATS.

4.

reader-mgt` Сервіс (Мікросервіс читачів)

reader-mgt:  
 build: ./reader-mgt  
 container_name: reader-service  
 environment:  
 DATABASE_HOST: mysql  
 DATABASE_USER: root  
 DATABASE_PASSWORD: ''  
 DATABASE_NAME: microservices  
 NATS_URL: nats://nats:4222  
 networks:  
 - my_network
  • build: Визначає директорію (./reader-mgt), де буде побудований Docker-образ для мікросервісу reader-mgt.
  • container_name: Назначає контейнеру ім’я reader-service.
  • environment: Визначає змінні середовища для підключення до MySQL і NATS.
  • DATABASE_HOST: mysql: Вказує на сервіс mysql для підключення до бази даних.
  • DATABASE_USER: root: Вказує користувача MySQL для підключення.
  • DATABASE_PASSWORD: '': Встановлює пароль як порожній рядок.
  • DATABASE_NAME: microservices: Вказує на базу даних microservices.
  • NATS_URL: nats://nats:4222: Налаштовує сервіс для зв’язку з сервером NATS.
  • networks: Забезпечує, щоб reader-mgt був частиною мережі my_network.

5. article-mgt Сервіс (Мікросервіс статей)

article-mgt:  
 build: ./article-mgt  
 container_name: article-service  
 environment:  
 DATABASE_HOST: mysql  
 DATABASE_USER: root  
 DATABASE_PASSWORD: ''  
 DATABASE_NAME: microservices  
 NATS_URL: nats://nats:4222  
 volumes:  
 - ./article-mgt/src:/usr/src/app/src  
 networks:  
 - my_network
  • build: Визначає директорію (./article-mgt), де буде побудований Docker-образ для мікросервісу article-mgt.
  • container_name: Назначає контейнеру ім’я article-service.
  • environment: Визначає змінні середовища для підключення до MySQL і NATS.
  • DATABASE_HOST: mysql: Вказує на сервіс mysql.
  • DATABASE_USER: root: Вказує користувача MySQL.
  • DATABASE_PASSWORD: '': Використовує порожній пароль для підключення.
  • DATABASE_NAME: microservices: Вказує ім’я бази даних.
  • NATS_URL: nats://nats:4222: Налаштовує сервіс для зв’язку з NATS.
  • volumes: Прив’язує директорію article-mgt/src з хост-машини до директорії контейнера /usr/src/app/src, що дозволяє вносити зміни в код без необхідності перезавантажувати контейнер та зберігати дані.
  • networks: Забезпечує, щоб article-mgt був частиною мережі my_network.

Мережі та Томи

networks:  
 my_network:  
 driver: bridge
volumes:  
 mysql_data:  
 driver: local
  • networks:
  • my_network: Це визначає користувацьку мережу з іменем my_network, використовуючи драйвер bridge. Усі сервіси повинні бути в цій мережі, щоб мати змогу взаємодіяти між собою.
  • volumes:
  • mysql_data: Це створює постійний том для сервісу mysql для збереження даних у /var/lib/mysql. Тому надається локальне середовище в Docker.

Підсумок:

  • MySQL: Запускає контейнер з базою даних MySQL без пароля і з базою даних під назвою microservices.
  • NATS: Запускає сервер повідомлень NATS для міжсервісної комунікації, доступний на портах 4222 і 8222.
  • Gateway: Виконує функцію API Gateway, з’єднуючи сервіси та керуючи запитами до мікросервісів reader-mgt і article-mgt.
  • Мікросервіси Reader і Article: Обробляють бізнес-логіку та взаємодіють з MySQL і NATS.

Далі ми перейдемо до директорії gateway та створимо два файли: .dockerignore і Dockerfile, кожен з яких має своє призначення.

  • .dockerignore: Цей файл використовується для вказівки файлів і директорій, які слід виключити з контексту побудови Docker. Це допомагає зменшити розмір Docker-образу та запобігає включенню непотрібних або чутливих файлів (наприклад, логів, node_modules, файлів .env).
  • Dockerfile: Цей файл містить інструкції для побудови Docker-образу для сервісу gateway.
    Він визначає базовий образ, налаштовує середовище, копіює необхідні файли, встановлює залежності та вказує, як сервіс має працювати всередині контейнера.

Додайте наступний код до файлу .dockerignore.

node_modules  
build

Це виключить папки node_modules та build з контексту побудови Docker. Переконайтесь, що зміни збережено, і потім переходьте до Dockerfile.

Додайте наступний код до Dockerfile.

FROM node:18  

WORKDIR /usr/src/app  

COPY package.json ./  
COPY package-lock.json ./  

RUN npm install   

COPY . .  

RUN npm run build  

EXPOSE 3000  

CMD ["npm", "run", "start:prod"]

Щоб дати вам контекст, ось що робить цей код.

FROM node:18

  • Визначає базовий образ для Docker-контейнера. У цьому випадку використовується офіційний образ Node.js 18, який уже має встановлені Node.js і npm. Це гарантує, що контейнер має стабільне середовище для запуску вашого застосунку.

WORKDIR /usr/src/app

  • Встановлює робочу директорію всередині контейнера в /usr/src/app. Усі наступні команди будуть виконуватись відносно цієї директорії. Якщо директорія не існує, вона буде створена.

COPY package.json ./

  • Копіює файл package.json з вашої локальної директорії проєкту в робочу директорію всередині контейнера.

COPY package-lock.json ./

  • Так само копіює файл package-lock.json в робочу директорію. Це гарантує, що будуть встановлені точно ті версії залежностей.

RUN npm install

  • Встановлює всі залежності, зазначені в package.json, за допомогою npm. Він використовує package-lock.json, щоб забезпечити відтворювану структуру залежностей.

COPY . .

  • Копіює всі файли з локальної директорії проєкту в робочу директорію контейнера, за винятком файлів, зазначених у .dockerignore.

RUN npm run build

  • Виконує команду build, визначену в package.json. Зазвичай ця команда компілює або трансформує вихідний код (наприклад, з TypeScript в JavaScript) і готує його до використання в продакшн.

EXPOSE 3000

  • Оголошує, що контейнер буде слухати на порту 3000. Хоча це не відкриває порт, це служить як документація і допомагає інструментам типу Docker Compose правильно мапити порти.

CMD ["npm", "run", "start:prod"]

  • Вказує команду, яку потрібно виконати, коли контейнер стартує. Тут вона запускає команду start:prod, визначену в package.json, що зазвичай означає запуск застосунку в продакшн-режимі.

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

У файлі .dockerignore ми будемо виключати папку node_modules.

node_modules

У Dockerfile ми додамо команду prisma, щоб Prisma-клієнт генерувався під час побудови.

FROM node:18  

WORKDIR /usr/src/app  

COPY package.json ./  
COPY package-lock.json ./  

RUN npm install  

COPY . .  

RUN npx prisma generate  

RUN npm run build  

CMD ["npm", "run", "start:prod"]

Переконайтесь, що ви створили обидва файли .dockerignore та Dockerfile у директоріях мікросервісів reader-mgt та article-mgt. Ці файли є важливими для контейнеризації кожного мікросервісу та підготовки їх до деплою.

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

На даному етапі нам потрібно вирішити, як пушити код на сервер. Є кілька варіантів для цього:

  1. Використання GitHub:
  • Створіть репозиторій на GitHub для вашого проєкту, запуште код до репозиторію, а потім клоніть його на сервер.
  • Цей метод ідеальний для збереження контролю версій та співпраці з іншими.

2.
**Використання інструментів для передачі файлів
:

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

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

У вашому випадку, якщо у вас ще немає репозиторію, створіть його і запуште код вашого проєкту туди. Як тільки код буде на GitHub, ми перейдемо до його клонування на сервері і продовжимо налаштування.

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

1. Створення Docker мережі:

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

2. Адаптація конфігурації для серверного середовища:

  • Наразі рядок підключення до бази даних і конфігурація клієнта NATS налаштовані для локального середовища розробки. Наприклад:
    • База даних може бути налаштована для підключення до localhost, що не працюватиме на сервері.
    • Аналогічно, клієнт NATS, ймовірно, налаштований на локальні параметри.
    • Ці конфігурації потрібно оновити, щоб вони вказували на серверні деталі, такі як:
    • IP-адреса або ім’я хоста сервера бази даних.
    • Конфігурація NATS у серверному середовищі.

3. Коригування коду для сумісності:

  • Ми змінемо код, щоб:
    • Приймати змінні середовища для рядків підключення до бази даних, конфігурацій NATS та інших налаштувань, специфічних для сервісу.
    • Використовувати файл .env або Docker secrets для безпечного керування чутливою інформацією.
    • Такий підхід забезпечить портативність і адаптивність застосунку до різних середовищ.

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

Перша зміна, яку ми зробимо, стосується конфігурації nats-клієнта в api-gateway.

pic

зміни в конфігурації nats клієнта

Замість того, щоб вказувати на локальний сервер NATS через localhost:4222, що є стандартним портом для NATS, ми налаштуємо його на nats-server:4222.

Ось чому і як це працює:

  • Чому nats-server:4222?
    • У Docker-середовищі контейнери спілкуються між собою, використовуючи свої імена контейнерів як імена хостів.
    • Назвавши контейнер сервера NATS як nats-server, інші контейнери (як API gateway, так і мікросервіси) зможуть легко знайти його через внутрішній DNS Docker.
  • Що станеться?
    • Замість того, щоб звертатися до локальної інстанції NATS (наприклад, на localhost), наші мікросервіси тепер будуть комунікувати з контейнером сервера NATS, використовуючи його ім’я nats-server та відкритий порт 4222.
    • Це налаштування забезпечить безперешкодну комунікацію в межах Docker мережі.

Як реалізувати?

  1. Оновіть конфігурацію NATS у вашому коді, замінивши localhost:4222 на nats-server:4222.
  2. Переконайтесь, що у файлі docker-compose.yml (якщо ви використовуєте Docker Compose) або в Docker командах чітко вказано ім’я контейнера NATS як nats-server.
    3.
    Переконайтесь, що всі контейнери є частиною однієї Docker мережі.

З цією конфігурацією ваш клієнт NATS і мікросервіси зможуть ефективно комунікувати в Docker-середовищі.

Другу зміну ми внесемо у відповідні файли .env

pic

Зміна рядка підключення до бази даних

Тепер, коли ми завершили необхідні зміни конфігурації, можемо запушити код на GitHub. Спочатку використовуйте команду git add . для того, щоб додати всі зміни в репозиторії, переконавшись, що всі нові або змінені файли будуть включені в коміт. Далі виконайте команду git commit -m "Your commit message", щоб створити коміт з коротким повідомленням, яке описує зроблені зміни. І нарешті, використовуйте команду git push, щоб завантажити локальні коміти у ваш віддалений репозиторій на GitHub. Після успішного пушу, ми можемо підключитися до сервера через SSH і продовжити налаштування і деплой на сервері.

Ви можете придбати VPS у будь-якого провайдера на ваш вибір. В моєму випадку я використовую Hostinger і буду використовувати один зі своїх серверів з IP-адресою 191.101.233.12. Це сервер, який я використовую для тестування під час розробки, тому на ньому немає нічого критичного. Ви можете обрати будь-якого VPS-постачальника, який відповідає вашим вимогам, і продовжити налаштування серверного середовища.

Якщо ви використовуєте macOS або Linux, ви можете виконати команду ssh @, щоб підключитися до вашого сервера через SSH. Однак ця команда не підтримується в Windows. Користувачі Windows повинні використовувати клієнт SSH, наприклад PuTTY, щоб встановити з’єднання через SSH. Альтернативно, якщо ви використовуєте Windows 10 або новішу версію, ви можете скористатися вбудованим клієнтом OpenSSH у PowerShell, виконавши команду ssh @.

pic

підключено до сервера через ssh

Далі ми встановимо все необхідне програмне забезпечення, включаючи MySQL, Docker, UFW (Uncomplicated Firewall) і будь-які інші залежності, потрібні для проєкту. Перед тим, як створювати контейнери, давайте спершу переконаємось, що все необхідне ПЗ встановлено і налаштовано правильно.

Ось загальні кроки для встановлення програмного забезпечення:

  1. Оновлення індексу пакетів:
sudo apt-get update

2. Встановлення MySQL: Щоб встановити MySQL на ваш сервер, скористайтесь наступними командами:

sudo apt-get install mysql-server  
sudo mysql_secure_installation

Під час установки MySQL вам буде запропоновано пройти кілька запитів. Будь ласка, уважно читайте і спробуйте зрозуміти, що відбувається.

3. Встановлення Docker: Щоб встановити Docker, виконайте:

sudo apt-get install apt-transport-https ca-certificates curl software-properties-common  
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -  
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"  
sudo apt-get update  
sudo apt-get install docker-ce

4. Встановлення Docker Compose: Щоб встановити Docker Compose, виконайте:

sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose  
sudo chmod +x /usr/local/bin/docker-compose

5. Встановлення UFW (Uncomplicated Firewall): Щоб встановити та налаштувати UFW, виконайте:

sudo apt-get install ufw  
sudo ufw allow OpenSSH  
sudo ufw enable  
sudo ufw status

6. Встановлення Git: Щоб встановити Git, використовуйте:

sudo apt-get install git

На цьому етапі ми можемо приступити до налаштування нашого проєкту на сервері. Спершу ми клонуватимемо репозиторій з GitHub на сервер. Перейдіть на вашу сторінку на GitHub і клацніть на репозиторій, куди ви запушили ваш код.
Тепер ми скопіюємо URL цього репозиторію.

pic

Скопіюйте посилання на репозиторій

Якщо ви скопіювали посилання, поверніться до термінала, де ви взаємодіяли з вашим сервером, і виконайте команду sudo git clone. Додаємо термін sudo, щоб команда виконувалася з абсолютними правами. Після клонування буде створено директорію, і ця директорія міститиме весь наш код, який ми раніше запушили. Ви можете підтвердити це за допомогою команди ls у терміналі.

pic

Створена директорія після клонування репозиторію

Тепер ми перейдемо до цієї папки за допомогою команди cd. Виконайте cd microservices, щоб увійти в директорію. Не забудьте замінити microservices на назву вашої папки. Перед тим, як будувати Docker-образи, нам потрібно зупинити mysql-server, оскільки він використовує порт за замовчуванням 3306, а наш Docker-контейнер MySQL буде використовувати той самий порт. Тому виконайте команду sudo systemctl stop mysql, щоб звільнити порт 3306. Потім запустіть команду sudo docker-compose up --build -d, щоб побудувати образи та запустити контейнери. Прапор -d дозволяє будувати образи без заповнення екрану журналами.

pic

Будуємо Docker-образи

Коли ми виконаємо команду docker ps, ми побачимо, що працюють 2 контейнери: API-шлюз і сервер NATS. Однак контейнери для reader-mgt, article-mgt мікросервісів і MySQL не працюють. Тому для вирішення цієї проблеми ми перевіримо логи кожного сервісу, щоб зрозуміти, в чому проблема.

Щоб вирішити проблему з контейнером MySQL, виконаємо команду sudo docker logs mysql.

pic

Логи, отримані під час побудови контейнера MySQL

Повідомлення про помилку вказує на те, що MySQL не може ініціалізуватися, оскільки для цього потрібно встановити пароль root під час запуску контейнера. Змінна середовища MYSQL_ROOT_PASSWORD або відсутня, або неправильно налаштована. Пам’ятайте, що в нашому файлі docker-compose.yml ми задали її як порожній рядок, тому нам потрібно повернутися і виправити цю помилку, встановивши пароль. Я використаю простий пароль для демонстрації, але в разі публікації на продакшн, переконайтесь, що використовуєте надійний пароль.

Також нам потрібно виявити причину, чому мікросервіси reader і article-mgt не працюють. Ми використаємо один з них як приклад і виконаємо команду sudo docker logs reader-service.

pic

Логи з контейнера мікросервісу reader-service під час побудови

Виявляється, що ми не змінили рядок підключення до сервера NATS у мікросервісі reader-mgt, і, ймовірно, це ж причина, чому мікросервіс article-mgt не працює. Щоб підтвердити це, ми виконаємо команду sudo docker logs article-service.

pic

Підтвердження, що обидва сервіси мають однакову помилку

Тепер, коли ми підтвердили, що обидва сервіси мають однакову помилку, спершу повертаємось до файлу docker-compose.yml і додаємо пароль.

pic

Додано пароль для користувача root MySQL

Далі ми змінюємо рядок підключення в обох мікросервісах. Для цього відкрийте обидва файли main.ts у папках reader-mgt і article-mgt і внесіть зміни, як показано на зображенні нижче.

pic

Зміна рядка підключення до сервера NATS

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

DATABASE_URL="mysql://root:123@mysql:3306/microservices"

Тепер ми запушимо код на GitHub і оновимо те, що вже є там, потім повернемося до сервера, оновимо код і спробуємо знову побудувати і запустити контейнери. Використовуємо ті самі команди, які ми використовували раніше, щоб запушити наш код.

Після цього, поверніться до термінала, де ви взаємодіяли з сервером, і виконайте команду sudo git pull origin. Це оновить код у проектній директорії.

pic

Код оновлений за допомогою команди git pull origin

Тепер ми можемо знову побудувати образи та використати команду docker ps, щоб перевірити, чи працюють контейнери належним чином.

pic

Контейнери працюють

Як ви можете побачити, всі 5 контейнерів працюють як очікувалося. Тепер нам потрібно переконатися, що схема синхронізована з базою даних. Спочатку увійдемо в термінал MySQL і створимо базу даних.

docker exec -it mysql mysql -u root -p //команда для входу в оболонку MySQL

Вас попросять ввести пароль, який ви встановили. Після цього створимо базу даних microservices і перевіримо, чи успішно вона створена.

CREATE DATABASE microservices; //команда для створення бази даних  
SHOW DATABASES; //база даних microservices повинна з'явитися в списку

Після цього увійдемо в оболонку контейнера reader_mgt і виконаємо команду npx prisma migrate dev, щоб синхронізувати схему проекту з базою даних. Далі дозволимо трафік через порт 3000, щоб ми могли надсилати запити і перевірити, чи працюють сервіси як очікується. Для цього виконаємо команду sudo ufw allow 3000/tcp. Це дозволить вхідні запити через порт 3000. Тепер спробуємо зберегти статтю, підключившись до сервера.

pic

Успішний запит на створення

Тепер, коли ми успішно створили статтю, це означає, що наші сервіси працюють як очікувалося, що є великим досягненням, враховуючи, наскільки далеко ми просунулися. Однак є ще одна річ, яку потрібно зробити. Якщо подивитися на URL, до якого ми надсилаємо запити, то видно, що IP-адреса сервера та порт сервісу http://191.101.233.12:3000/api/save-article є відкритими, що не є ідеальним. Тому ми повинні заховати це, оскільки це створює ризик безпеки. Все, що нам потрібно, це домен, на якому ми зареєструємо піддомен. Потім ми вкажемо цей піддомен на сервер, створимо для нього конфігураційний файл на сервері та також створимо SSL-сертифікат для нього, щоб всі запити надходили через HTTPS, що є безпечнішим, ніж те, що ми використовуємо зараз.

Для цієї статті я придбав домен zziwaraymondian.blog, і саме його ми будемо використовувати. Тепер, що нам потрібно зробити, це створити запис типу A Name або CName в DNS-записах домену та вказати його на IP-адресу сервера, а також додати значення TTL (час життя).

pic

Створення запису типу A Name

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

Спочатку потрібно встановити apache2. Для цього виконайте наступні команди.

sudo apt update //команда для оновлення всіх встановлених пакетів  
sudo apt install apache2 //команда для встановлення apache2

Після завершення інсталяції потрібно створити конфігурацію віртуального хоста. Вона буде обробляти редирекцію з HTTP на HTTPS та прокситиме трафік до нашого сервісу, що працює на порту 3000.

Виконайте наступну команду, щоб створити та відкрити новий конфігураційний файл в редакторі nano.

sudo nano /etc/apache2/sites-available/api.zziwaraymondian.blog.conf

Тепер скопіюйте та вставте нижче наведене конфігураційне налаштування.
Зверніть увагу, що шляхи до SSL-файлів закоментовані, оскільки це викличе помилку. Ми ще не встановили ці сертифікати.

# HTTP редирект на HTTPS  

 ServerName api.zziwaraymondian.blog  

 # Редирект HTTP на HTTPS  
 Redirect permanent / https://api.zziwaraymondian.blog/  


# HTTPS конфігурація  

 ServerName api.zziwaraymondian.blog  

 # SSL конфігурація  
 SSLEngine on  
 # SSLCertificateFile /etc/letsencrypt/live/api.zziwaraymondian.blog/fullchain.pem  
 # SSLCertificateKeyFile /etc/letsencrypt/live/api.zziwaraymondian.blog/privkey.pem  
 # SSLCertificateChainFile /etc/letsencrypt/live/api.zziwaraymondian.blog/chain.pem  

 # Конфігурація проксі для пересилання трафіку до вашого сервісу на порту 3000  
 ProxyPass / http://localhost:3000/  
 ProxyPassReverse / http://localhost:3000/  

 # Опційно - логування та інші налаштування  
 ErrorLog ${APACHE_LOG_DIR}/error.log  
 CustomLog ${APACHE_LOG_DIR}/access.log combined  

Ця конфігурація, по суті, робить наступне: перший блок VirtualHost слухає на порту 80 і перенаправляє весь HTTP-трафік на HTTPS. Другий блок слухає на порту 443 (HTTPS) і пересилає запити до нашого сервісу, що працює на порту 3000.

Далі потрібно увімкнути сайт, виконавши цю команду.

sudo a2ensite api.zziwaraymondian.blog.conf

Вас попросять виконати команду systemctl reload apache2 для активації нової конфігурації.

Якщо apache2 успішно перезавантажиться, це означає, що вся конфігурація була успішно виконана, і ми тепер можемо встановити SSL-сертифікати для нашого домену. Для цього ми будемо використовувати cerbot, оскільки він надає безкоштовні SSL-сертифікати. Виконайте команду нижче для встановлення certbot на сервер.

sudo apt install certbot python3-certbot-apache

Після завершення інсталяції потрібно відкрити порти 80 і 443, інакше certbot поверне помилку, коли ми спробуємо запросити SSL-сертифікат.

Для відкриття цих портів виконайте наступні команди.

sudo ufw allow 80/tcp  
sudo ufw allow 443/tcp

Потім виконайте команду для початку процесу отримання SSL-сертифікату.

sudo certbot --apache -d api.zziwaraymondian.blog

Дотримуйтесь інструкцій, поки сертифікат буде встановлений.

pic

SSL-сертифікат отримано та встановлено

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

Фу… як ви можете бачити, наш запит на створення статті на https://api.zziwaraymondian.blog/api/save-article був успішним, що означає, що вся наша конфігурація була виконана правильно.

Підсумовуючи, розгортання NestJS мікросервісів за допомогою Docker та Apache — це потужний підхід до створення масштабованих, безпечних і підтримуваних додатків. Docker спрощує процес розгортання, інкапсулюючи кожен сервіс у ізольовані контейнери, в той час як Apache виступає як універсальний зворотний проксі, безперешкодно керуючи трафіком і забезпечуючи безпечну HTTPS-комунікацію.

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

Оскільки бізнеси все більше покладаються на мікросервіси для задоволення сучасних вимог, оволодіння цими стратегіями розгортання стає важливим вмінням для кожного розробника. Завдяки Docker та Apache ваші додатки добре підготовлені до успіху в динамічному та швидко змінюваному технічному середовищі.

Перекладено з: Comprehensive Guide to Deploying NestJS Microservices with Docker and Apache

Leave a Reply

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