Розгортка проектів на VPS

В даній статті я хочу описати процес розгортки додатків, який я використовую. Основною особливістю мого підходу є те що я не використовую контейнеризацію, тим самим пропускаючи доволі довгий крок зі збіркою образу додатку. Натомість для моніторингу роботи сервісів я використовую PM2.

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

Завантаження коду на сервер

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

Стандартним підходом в даному випадку буде клонування git репозиторію на сервер. Таким чином ми зможемо отримувати актувальну версію коду, без зайвих проблем. Репозиторій з кодом ми можемо розмістити на якомусь хмарному провайдері на кшталт GitHub, GitLab або створити bare репозиторій безпосередньо на нашому сервері.

Після того як ми розмістили наш код у віддаленому репозиторії ми можемо його клонувати безпосередньо на сервер в робочу директорію з проектами. Я би радив використовувати для цього або домашню директорію користувача /home/our-user/ або теку /opt/ (попередньо видавши необхідні дозволи нашому користувачу).

Власний хостинг

Якщо ми хостимо репозиторій на нашому сервері, то клонувати наш репозиторій ми можемо командою

git clone /srv/git/cern-ibm5100

Github

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

ssh-keygen -f ~/.ssh/github-cern-ibm5100 # Генеруємо ключ для приватного репозиторія
cat ~/.ssh/github-cern-ibm5100.pub       # Копіюємо вивід команди в налаштування репозиторія на гітхабі Settings -> Deploy keys
vim ~/.ssh/config                        # Налаштовуємо використання згенерованого ключа під ssh аліас

В файлі ssh конфігурації необхідно додати аліас який дозволить нам без зайвих команд використовувати саме цей ключ при роботі з цим репозиторієм

Host github-cern-ibm5100
	Hostname github.com
	IdentityFile /home/adam/.ssh/github-cern-ibm5100

Дані рядки означають що для хостнейму github-cern-ibm5100, треба використоувати ключ /home/adam/.ssh/github-cern-ibm5100 і переадресувати запити на github.com.

Після цього ми можемо клонувати наш репозиторій командою

git clone git@github-cern-ibm5100:AltairInglorious/cern-ibm5100.git

Важливо в даному посилані використовувати github-cern-ibm5100 замість стандартного github.com, таким чином буде застосована наша конфігурація.

Налаштування проекту

Після клонування в поточній теці утвориться нова директорія з назвою cern-ibm5100. Перейшовши в неї ми можемо виконати всі необхідні налаштування як ось встановлення пакетів, створення файлів конфігурації, застосування міграцій БД ітд. Дані кроки залежатимуть від вашого проекту.

Підготовка PM2

Якщо у вас на сервері ще не встановлений PM2, ви можете це зробити виконавши команду

bun i -g pm2 # Встановлення PM2
pm2 startup  # Налаштування автозапуску демону PM2 (ви отримаєте інструкцію, для вашого дистрибутиву)

Завершивши налаштування проекту ми можемо підготувати його до запуску в PM2. Я надаю перевагу декларативному методу запуску через файл ecosystem.config.(c)js. Для його створення варто виконати команду

pm2 init simple

Після цього у вас з’явиться новий файл з налаштуваннями. По своїй суті він аналогічний до docker-compose.yml, в ньому ви зможете налаштувати запуск ваших додатків (авторестарт, змінні оточення, логування ітд). Більше детальніше про налаштування цього файлу ви можете прочитати в документації.

Я наведу приклад цього файлу для запуску API на Bun та NextJS додатку з монорепозиторію з двома теками api та web відповідно

module.exports = {
  apps : [{
    name   : "api",
    script : "index.ts",
    interpreter: "/home/adam/.bun/bin/bun",
    cwd: "./api",
    time: true,
    env: {
      NODE_ENV: "production",
      SHOW_REQ: "true",
      PORT: 4000,
    },
  },{
    name   : "admin",
    script : "./node_modules/next/dist/bin/next",
    args: 'start',
    cwd: "./web",
    time: true,
    env: {
        NODE_ENV: 'production',
        PORT: 4001,
        NEXT_PUBLIC_API_ENDPOINT: 'https://example.com/api',
    }
  }]
}

Створивши даний файл ми можемо запустити наш проект виконавши

pm2 start ecosystem.config.js # Запуск нашого проекту
pm2 save                      # Збереження проекту в конфігу для автозапуску при старті серверу

В залежності від налаштувань свого проекту на TypeScript ви можете отримати помилку пов’язану з CommonJS. Для її вирішення достатньо перейменувати ваш файл на ecosystem.config.cjs.

Оновлення

Для оновлення нашого проекту ми можемо написати bash скрипт наступного вигляду

#!/bin/sh

# Оновлення репозиторію
git pull

# ...тут дії специфічні для вашого проекту, для прикладу
bun i -p --frozen-lockfile

# Перезапуск проекту
pm2 restart ecosystem.config.js

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

#!/bin/bash

for opt in $@
do
  if [ "$opt" = "-h" ]; then
    echo "-c        Update core
-s        Update web.shop
-a        Update web.admin
-v        Update web.vendor"
    exit 0
  fi
done

echo 'Updating repository...'
git pull

while getopts ":savc" opt; do
  case ${opt} in
    s)
      echo 'Rebuilding web.shop...'
      cd web.shop
      bun i --frozen-lockfile
      bun run build
      cd ..
      ;;
    a)
      echo 'Rebuilding web.admin...'
      cd web.admin
      bun i --frozen-lockfile
      bun run build
      cd ..
      ;;
    v)
      echo 'Rebuilding web.vendor...'
      cd web.vendor
      bun i --frozen-lockfile
      bun run build
      cd ..
      ;;
    c)
      echo 'Update core...'
      cd core
      bun i --frozen-lockfile
      cd ..
      ;;
    ?)
      echo "Invalid option: -${OPTARG}."
      ;;
  esac
done

echo 'Restarting services...'
pm2 restart ecosystem.config.js