Ручной деплой NestJS и Angular приложений на выделенном сервер через «Docker Compose» и «PM2»

от автора

Предыдущая статья: Сборка приложений на NestJS и Angular и запуск их в двух вариантах: через PM2 и через «Docker Compose»

Покупаем выделенный сервер и настраиваем SSH доступ к нему с локального компьютера.

Добавляем https://github.com/SteveLTN/https-portal в «Docker compose» для автоматической генерации и продления сертификатов.

1. Находим бесплатный или покупаем выделенный сервер

Шаги по этому пункту не буду расписывать, я уже расписывал ранее в разделе «Buy VPS» другого поста — Deploy nestjs project to VPS with dokku.

В рамках текущих постов я купил на том же сайте самый дешевый выделенный сервер.

Операционная система: Ubuntu 22.04 x64
Доступ в мир: 100 MB/sec (shared)
RAM: 2GB
CPU: 2 vCPU
Количество IPv4 адресов: 1

При покупке выделенного сервера мне также дали домен третьего уровня: vps1724252356.tech0.ru, в будущем этот проект переедет на домен https://fullstack.nestjs-mod.com.

2. Настройка подключение к выделенному серверу по SSH

После покупки необходимо установить пароль если он не был установлен, разные сайты имеют свои интерфейсы для этого, в моем случаи установка пароля находится на странице https://ztv.su/clientarea.php?action=productdetails&id=33376#tabChangepw.

Копируем наш локальный публичный SSH на удаленный сервер, при запуске появится запрос на ввод пароля, необъодимо ввести свой пароль и нажать «Enter».

Если SSH-ключа на компьютере не было, то его нужно создать с помощью команды ssh-keygen

Команды

ssh-copy-id root@194.226.49.162 
Вывод консоли
$ ssh-copy-id root@194.226.49.162 /usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/user/.ssh/id_rsa.pub" The authenticity of host '194.226.49.162 (194.226.49.162)' can't be established. ECDSA key fingerprint is SHA256:SOME_SYMBOLS. Are you sure you want to continue connecting (yes/no/[fingerprint])? yes /usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed /usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys root@194.226.49.162's password:  Number of key(s) added: 1  Now try logging into the machine, with:   "ssh 'root@194.226.49.162'" and check to make sure that only the key(s) you wanted were added. 

3. Подключение к удаленному серверу

Команды

ssh root@194.226.49.162 
Вывод консоли
$ ssh root@194.226.49.162 Welcome to Ubuntu 22.04.3 LTS (GNU/Linux 5.15.0-91-generic x86_64)   * Documentation:  https://help.ubuntu.com  * Management:     https://landscape.canonical.com  * Support:        https://ubuntu.com/advantage    System information as of Wed Aug 21 18:42:37 MSK 2024    System load:  0.02734375        Processes:             103   Usage of /:   9.4% of 24.05GB   Users logged in:       0   Memory usage: 10%               IPv4 address for eth0: 194.226.49.162   Swap usage:   0%   * Strictly confined Kubernetes makes edge and IoT secure. Learn how MicroK8s    just raised the bar for easy, resilient and secure K8s cluster deployment.     https://ubuntu.com/engage/secure-kubernetes-at-the-edge  Expanded Security Maintenance for Applications is not enabled.  0 updates can be applied immediately.  Enable ESM Apps to receive additional future security updates. See https://ubuntu.com/esm or run: sudo pro status   The list of available updates is more than a week old. To check for new updates run: sudo apt update  Last login: Wed Aug 21 18:41:46 2024 from X.X.X.X root@vps1724252356:~# 

4. Установка всех необходимых пакетов на выделенный сервер

Команды

sudo snap install curl  curl -fsSL https://get.docker.com -o get-docker.sh sh get-docker.sh sudo groupadd docker sudo usermod -aG docker $USER  sudo apt install git -y  curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash export NVM_DIR="$HOME/.nvm" && . "$NVM_DIR/nvm.sh" --no-use nvm install v20 nvm use v20  npm install --global yarn  sudo apt install default-jre -y  sudo apt install -y unzip  echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p  sudo reboot 
Вывод консоли
root@vps1724252356:~# sudo snap install curl curl 8.1.2 from Wouter van Bommel (woutervb) installed  root@vps1724252356:~# curl -fsSL https://get.docker.com -o get-docker.sh sh get-docker.sh sudo groupadd docker sudo usermod -aG docker $USER # Executing docker install script, commit: 0d6f72e671ba87f7aa4c6991646a1a5b9f9dae84 + sh -c apt-get update -qq >/dev/null + sh -c DEBIAN_FRONTEND=noninteractive apt-get install -y -qq ca-certificates curl >/dev/null + sh -c install -m 0755 -d /etc/apt/keyrings + sh -c curl -fsSL "https://download.docker.com/linux/ubuntu/gpg" -o /etc/apt/keyrings/docker.asc + sh -c chmod a+r /etc/apt/keyrings/docker.asc + sh -c echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu jammy stable" > /etc/apt/sources.list.d/docker.list + sh -c apt-get update -qq >/dev/null + sh -c DEBIAN_FRONTEND=noninteractive apt-get install -y -qq docker-ce docker-ce-cli containerd.io docker-compose-plugin docker-ce-rootless-extras docker-buildx-plugin >/dev/null + sh -c docker version Client: Docker Engine - Community  Version:           27.1.2  API version:       1.46  Go version:        go1.21.13  Git commit:        d01f264  Built:             Mon Aug 12 11:50:12 2024  OS/Arch:           linux/amd64  Context:           default  Server: Docker Engine - Community  Engine:   Version:          27.1.2   API version:      1.46 (minimum version 1.24)   Go version:       go1.21.13   Git commit:       f9522e5   Built:            Mon Aug 12 11:50:12 2024   OS/Arch:          linux/amd64   Experimental:     false  containerd:   Version:          1.7.20   GitCommit:        8fc6bcff51318944179630522a095cc9dbf9f353  runc:   Version:          1.1.13   GitCommit:        v1.1.13-0-g58aa920  docker-init:   Version:          0.19.0   GitCommit:        de40ad0  ================================================================================  To run Docker as a non-privileged user, consider setting up the Docker daemon in rootless mode for your user:      dockerd-rootless-setuptool.sh install  Visit https://docs.docker.com/go/rootless/ to learn about rootless mode.   To run the Docker daemon as a fully privileged service, but granting non-root users access, refer to https://docs.docker.com/go/daemon-access/  WARNING: Access to the remote API on a privileged Docker daemon is equivalent          to root access on the host. Refer to the 'Docker daemon attack surface'          documentation for details: https://docs.docker.com/go/attack-surface/  ================================================================================  groupadd: group 'docker' already exists  root@vps1724252356:~# sudo apt install git -y Reading package lists... Done Building dependency tree... Done Reading state information... Done Suggested packages:   git-daemon-run | git-daemon-sysvinit git-doc git-email git-gui gitk gitweb git-cvs git-mediawiki git-svn The following packages will be upgraded:   git 1 upgraded, 0 newly installed, 0 to remove and 137 not upgraded. Need to get 3165 kB of archives. After this operation, 16.4 kB of additional disk space will be used. Get:1 http://archive.ubuntu.com/ubuntu jammy-updates/main amd64 git amd64 1:2.34.1-1ubuntu1.11 [3165 kB] Fetched 3165 kB in 1s (2408 kB/s) (Reading database ... 94596 files and directories currently installed.) Preparing to unpack .../git_1%3a2.34.1-1ubuntu1.11_amd64.deb ... Unpacking git (1:2.34.1-1ubuntu1.11) over (1:2.34.1-1ubuntu1.10) ... Setting up git (1:2.34.1-1ubuntu1.11) ... Scanning processes... Scanning linux images...  Running kernel seems to be up-to-date.  No services need to be restarted.  No containers need to be restarted.  No user sessions are running outdated binaries.  No VM guests are running outdated hypervisor (qemu) binaries on this host.  root@vps1724252356:~# curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash export NVM_DIR="$HOME/.nvm" && . "$NVM_DIR/nvm.sh" --no-use nvm install v20 nvm use v20   % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current                                  Dload  Upload   Total   Spent    Left  Speed 100 15037  100 15037    0     0  36871      0 --:--:-- --:--:-- --:--:-- 36945 => Downloading nvm from git to '/root/.nvm' => Cloning into '/root/.nvm'... remote: Enumerating objects: 376, done. remote: Counting objects: 100% (376/376), done. remote: Compressing objects: 100% (320/320), done. remote: Total 376 (delta 46), reused 176 (delta 29), pack-reused 0 (from 0) Receiving objects: 100% (376/376), 372.57 KiB | 2.13 MiB/s, done. Resolving deltas: 100% (46/46), done. * (HEAD detached at FETCH_HEAD)   master => Compressing and cleaning up git repository  => Appending nvm source string to /root/.bashrc => Appending bash_completion source string to /root/.bashrc => Close and reopen your terminal to start using nvm or run the following to use it now:  export NVM_DIR="$HOME/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion Downloading and installing node v20.16.0... Downloading https://nodejs.org/dist/v20.16.0/node-v20.16.0-linux-x64.tar.xz... ################################################################################################################################################################ 100.0% Computing checksum with sha256sum Checksums matched! Now using node v20.16.0 (npm v10.8.1) Creating default alias: default -> v20 (-> v20.16.0) Now using node v20.16.0 (npm v10.8.1)  root@vps1724252356:~# npm install --global yarn  added 1 package in 2s npm notice npm notice New patch version of npm available! 10.8.1 -> 10.8.2 npm notice Changelog: https://github.com/npm/cli/releases/tag/v10.8.2 npm notice To update run: npm install -g npm@10.8.2 npm notice  root@vps1724252356:~# sudo apt install -y unzip Reading package lists... Done Building dependency tree... Done Reading state information... Done Suggested packages:   zip The following NEW packages will be installed:   unzip 0 upgraded, 1 newly installed, 0 to remove and 137 not upgraded. Need to get 175 kB of archives. After this operation, 386 kB of additional disk space will be used. Get:1 http://archive.ubuntu.com/ubuntu jammy-updates/main amd64 unzip amd64 6.0-26ubuntu3.2 [175 kB] Fetched 175 kB in 0s (485 kB/s) Selecting previously unselected package unzip. (Reading database ... 96234 files and directories currently installed.) Preparing to unpack .../unzip_6.0-26ubuntu3.2_amd64.deb ... Unpacking unzip (6.0-26ubuntu3.2) ... Setting up unzip (6.0-26ubuntu3.2) ... Processing triggers for man-db (2.10.2-1) ... Scanning processes... Scanning linux images...  Running kernel seems to be up-to-date.  No services need to be restarted.  No containers need to be restarted.  No user sessions are running outdated binaries.  No VM guests are running outdated hypervisor (qemu) binaries on this host.  root@vps1724252356:~# sudo reboot Connection to 194.226.49.162 closed by remote host. Connection to 194.226.49.162 closed. 

5. Генерация SSH-ключа на выделенном сервере

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

Команды

ssh-keygen 
Вывод консоли
root@vps1724252356:~# ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /root/.ssh/id_rsa Your public key has been saved in /root/.ssh/id_rsa.pub The key fingerprint is: SHA256:SOME_RANDOM_SYMBOLS root@vps1724252356 The key's randomart image is: +---[RSA 3072]----+ |  .o*oo.         | +----[SHA256]-----+ 

6. Привязка публичного ключа выдленного сервера к GitHub

Необходимо запустить команды cat /root/.ssh/id_rsa.pub и ответ вставить в настройки проекта, у меня они тут: https://github.com/nestjs-mod/nestjs-mod-fullstack/settings/keys/new

Команды

root@vps1724252356:~# cat /root/.ssh/id_rsa.pub 
Вывод консоли
# cat /root/.ssh/id_rsa.pub ssh-rsa AAAARANDOM_SYMBOLS= root@vps1724252356 

7. Клонируем проект

Команды

git clone git@github.com:nestjs-mod/nestjs-mod-fullstack.git cd nestjs-mod-fullstack 
Вывод консоли
root@vps1724252356:~# git clone git@github.com:nestjs-mod/nestjs-mod-fullstack.git Cloning into 'nestjs-mod-fullstack'... The authenticity of host 'github.com (140.82.121.3)' can't be established. ED25519 key fingerprint is SHA256:+SOME_RANDOM_SYMBOLS. This key is not known by any other names Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added 'github.com' (ED25519) to the list of known hosts. remote: Enumerating objects: 426, done. remote: Counting objects: 100% (426/426), done. remote: Compressing objects: 100% (294/294), done. remote: Total 426 (delta 190), reused 340 (delta 106), pack-reused 0 (from 0) Receiving objects: 100% (426/426), 536.69 KiB | 1.56 MiB/s, done. Resolving deltas: 100% (190/190), done.  root@vps1724252356:~# cd nestjs-mod-fullstack root@vps1724252356:~/nestjs-mod-fullstack# 

8. Устанавливаем зависимости и прогоняем запуск и тестирование двух режимов: PM2-продакшен и «Docker Compose»

E2E-тесты запускаем указывая внешний глобальный ИП адрес приложений.

Команды

npm i npx playwright install npx playwright install-deps  npm run test  cp example.env .env npm run pm2-full:prod:start export BASE_URL=http://194.226.49.162:3000 && npm run test:e2e npm run pm2-full:prod:stop  npm run docker-compose-full:prod:start export BASE_URL=http://194.226.49.162:8080 && npm run test:e2e npm run docker-compose-full:prod:stop 

E2E-тестирование в режиме «Docker Compose» упало с ошибкой, в следующих шагах будем их исправлять на локальном компьютере и затем через коммит в репозиторий проекта, донесем их на выделенный сервер.

9. На локальном компьютере, обновляем и добавляем новые npm-скрипты

При локальной разработке для запуска использовался отдельно установленное приложение «docker-compose», а на выделенном сервере используется compose встроенный в Docker.

Удаляем скрипт дя копирования статики copy-front-to-backend, так как теперь мы напрямую из бэкенд-приложения идем смотреть собранную статику фронтенда.

Для создания баз данных и применения миграций создали отдельный npm-скрипт, так как при попытке запуска двух npm комманд внутри контейнера, запускался только один.

Обновляем файл package.json

{   "scripts": {     // ...     "_____docker-compose-full prod infra_____": "_____docker-compose-full prod infra_____",     "docker-compose-full:prod:start": "export NX_DAEMON=false && export DISABLE_SERVE_STATIC=true && npm run generate && npm run build -- -c production && npm run docker:build:server:latest && export COMPOSE_INTERACTIVE_NO_CLI=1 && docker compose -f ./.docker/docker-compose-full.yml --env-file ./.docker/docker-compose-full.env --compatibility up -d",     "docker-compose-full:prod:stop": "export COMPOSE_INTERACTIVE_NO_CLI=1 && docker compose -f ./.docker/docker-compose-full.yml --env-file ./.docker/docker-compose-full.env --compatibility down",     "docker-compose-full:prod:only-start": "export COMPOSE_INTERACTIVE_NO_CLI=1 && docker compose -f ./.docker/docker-compose-full.yml --env-file ./.docker/docker-compose-full.env --compatibility up -d",     "docker-compose-full:prod:fill:database": "npm run db:create && npm run flyway:migrate",     // ...     "_____docker-compose prod-infra_____": "_____docker-compose prod-infra_____",     "docker-compose:start-prod:server": "export COMPOSE_INTERACTIVE_NO_CLI=1 && docker compose -f ./apps/server/docker-compose-prod.yml --env-file ./apps/server/docker-compose-prod.env --compatibility up -d",     "docker-compose:stop-prod:server": "export COMPOSE_INTERACTIVE_NO_CLI=1 && docker compose -f ./apps/server/docker-compose-prod.yml --env-file ./apps/server/docker-compose-prod.env down"   } } 

10. На локальном компьютере, обновляем параметры контейнера с миграциями в «Docker Compose» — файле

Обновленный файл .docker/docker-compose-full.yml

version: '3' networks:   nestjs-mod-fullstack-network:     driver: 'bridge' services:   nestjs-mod-fullstack-postgre-sql:     image: 'bitnami/postgresql:15.5.0'     container_name: 'nestjs-mod-fullstack-postgre-sql'     networks:       - 'nestjs-mod-fullstack-network'     volumes:       - 'nestjs-mod-fullstack-postgre-sql-volume:/bitnami/postgresql'     healthcheck:       test:         - 'CMD-SHELL'         - 'pg_isready -U postgres'       interval: '5s'       timeout: '5s'       retries: 5     tty: true     restart: 'always'     environment:       POSTGRESQL_USERNAME: '${SERVER_POSTGRE_SQL_POSTGRESQL_USERNAME}'       POSTGRESQL_PASSWORD: '${SERVER_POSTGRE_SQL_POSTGRESQL_PASSWORD}'       POSTGRESQL_DATABASE: '${SERVER_POSTGRE_SQL_POSTGRESQL_DATABASE}'   nestjs-mod-fullstack-postgre-sql-migrations:     image: 'node:20-bullseye-slim'     container_name: 'nestjs-mod-fullstack-postgre-sql-migrations'     networks:       - 'nestjs-mod-fullstack-network'     working_dir: '/app'     volumes:       - './../:/app'     command: 'npm run docker-compose-full:prod:fill:database'     tty: true     environment:       NX_SKIP_NX_CACHE: 'true'       SERVER_ROOT_DATABASE_URL: '${SERVER_ROOT_DATABASE_URL}'       SERVER_APP_DATABASE_URL: '${SERVER_APP_DATABASE_URL}'     depends_on:       nestjs-mod-fullstack-postgre-sql:         condition: 'service_healthy'   nestjs-mod-fullstack-server:     image: 'nestjs-mod-fullstack-server:latest'     container_name: 'nestjs-mod-fullstack-server'     networks:       - 'nestjs-mod-fullstack-network'     healthcheck:       test: ['CMD-SHELL', 'npx -y wait-on --timeout= --interval=1000 --window --verbose --log http://localhost:8080/api/health']       interval: 30s       timeout: 10s       retries: 10     tty: true     environment:       SERVER_APP_DATABASE_URL: '${SERVER_APP_DATABASE_URL}'     restart: 'always'     depends_on:       nestjs-mod-fullstack-postgre-sql:         condition: service_healthy       nestjs-mod-fullstack-postgre-sql-migrations:         condition: service_completed_successfully   nestjs-mod-fullstack-nginx:     image: nginx:alpine     container_name: 'nestjs-mod-fullstack-nginx'     networks:       - 'nestjs-mod-fullstack-network'     volumes:       - ../.docker/nginx:/etc/nginx/conf.d       - ../dist/apps/client/browser:/usr/share/nginx/html     restart: 'always'     depends_on:       nestjs-mod-fullstack-server:         condition: service_healthy     ports:       - '8080:8080'   nestjs-mod-fullstack-https-portal:     image: steveltn/https-portal:1     container_name: 'nestjs-mod-fullstack-https-portal'     networks:       - 'nestjs-mod-fullstack-network'     ports:       - '80:80'       - '443:443'     links:       - nestjs-mod-fullstack-nginx     restart: always     environment:       STAGE: '${HTTPS_PORTAL_STAGE}'       FORCE_RENEW: 'true'       DOMAINS: '${SERVER_DOMAIN} -> http://nestjs-mod-fullstack-nginx:8080'     depends_on:       nestjs-mod-fullstack-nginx:         condition: service_started     volumes:       - nestjs-mod-fullstack-https-portal-volume:/var/lib/https-portal volumes:   nestjs-mod-fullstack-postgre-sql-volume:     name: 'nestjs-mod-fullstack-postgre-sql-volume'   nestjs-mod-fullstack-https-portal-volume:     name: 'nestjs-mod-fullstack-https-portal-volume' 

11. На локальном компьютере, открываем доступ к Swagger-интерфейсу бэкенда в Nginx

Обновленный файл .docker/nginx/nginx.conf

map $sent_http_content_type $expires {     "text/html" epoch;     "text/html; charset=utf-8" epoch;     default off; }  map $http_upgrade $connection_upgrade {     default upgrade;     '' close; }  server {     listen 8080;     server_name localhost;      gzip on;     gzip_proxied any;     gzip_types text/plain application/xml text/css application/javascript application/json;     gzip_min_length 1000;     gzip_vary on;     gzip_disable "MSIE [1-6]\.(?!.*SV1)";      client_max_body_size 50m;     proxy_connect_timeout 5m;     proxy_send_timeout 5m;     proxy_read_timeout 5m;     send_timeout 5m;      proxy_max_temp_file_size 0;      root /usr/share/nginx/html;     index index.html;       location /api {         proxy_pass http://nestjs-mod-fullstack-server:8080;         proxy_http_version 1.1;         proxy_set_header Upgrade $http_upgrade;         proxy_set_header Connection $connection_upgrade;         proxy_set_header Host $host;         proxy_set_header Origin $http_origin;         proxy_set_header X-Real-IP $remote_addr;         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;          # kill cache         add_header Last-Modified $date_gmt;         add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';         if_modified_since off;         expires off;         etag off;     }      location /swagger {         proxy_pass http://nestjs-mod-fullstack-server:8080;         proxy_http_version 1.1;         proxy_set_header Upgrade $http_upgrade;         proxy_set_header Connection $connection_upgrade;         proxy_set_header Host $host;         proxy_set_header Origin $http_origin;         proxy_set_header X-Real-IP $remote_addr;         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;          # kill cache         add_header Last-Modified $date_gmt;         add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';         if_modified_since off;         expires off;         etag off;     }      location / {         expires $expires;         proxy_redirect off;         proxy_set_header Host $host;         proxy_set_header Origin $http_origin;         proxy_set_header X-Real-IP $remote_addr;         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;         proxy_set_header X-Forwarded-Proto $scheme;         proxy_read_timeout 1m;         proxy_connect_timeout 1m;         proxy_intercept_errors on;         error_page 404 =200 /index.html;         root /usr/share/nginx/html;     } } 

12. На локальном компьютере, добавляем возможность отключения NestJS модуля по выдаче статики

Так как в режиме «Docker Compose» мы раздаем статику через Nginx, то на не нужно собирать бэкенд с встроенной статикой и значит не нужны дополнительные обработчики для поддержки выдачи статики.

Обновленный файл apps/server/src/app/app.module.ts

import { createNestModule, NestModuleCategory } from '@nestjs-mod/common';  import { PrismaModule } from '@nestjs-mod/prisma'; import { ServeStaticModule } from '@nestjs/serve-static'; import { join } from 'path'; import { AppController } from './app.controller'; import { AppService } from './app.service';  export const { AppModule } = createNestModule({   moduleName: 'AppModule',   moduleCategory: NestModuleCategory.feature,   imports: [     PrismaModule.forFeature({ featureModuleName: 'app' }),     ...(process.env.DISABLE_SERVE_STATIC       ? []       : [           ServeStaticModule.forRoot({             rootPath: join(__dirname, 'assets', 'client'),           }),         ]),   ],   controllers: [AppController],   providers: [AppService], }); 

13. На локальном компьютере, повтороно прогоняем запуск и тестирование двух режимов: PM2-продакшен и «Docker Compose»

E2E-тесты запускаем указывая внешний глобальный ИП адрес приложений.

Команды

npm i npx playwright install npx playwright install-deps  npm run test  cp example.env .env npm run pm2-full:prod:start export BASE_URL=http://194.226.49.162:3000 && npm run test:e2e npm run pm2-full:prod:stop  npm run docker-compose-full:prod:start export BASE_URL=http://194.226.49.162:8080 && npm run test:e2e npm run docker-compose-full:prod:stop 

Тесты успешно прошли

14. На локальном компьютере, добавляем дополнительный контейнер с https-portal в docker-compose-full.yml для проксирования http в https и добавляем во все (кроме migrations) контейнеры опцию restart: ‘always’

Обновленный файл .docker/docker-compose-full.yml

version: '3' networks:   nestjs-mod-fullstack-network:     driver: 'bridge' services:   nestjs-mod-fullstack-postgre-sql:     image: 'bitnami/postgresql:15.5.0'     container_name: 'nestjs-mod-fullstack-postgre-sql'     networks:       - 'nestjs-mod-fullstack-network'     volumes:       - 'nestjs-mod-fullstack-postgre-sql-volume:/bitnami/postgresql'     healthcheck:       test:         - 'CMD-SHELL'         - 'pg_isready -U postgres'       interval: '5s'       timeout: '5s'       retries: 5     tty: true     restart: 'always'     environment:       POSTGRESQL_USERNAME: '${SERVER_POSTGRE_SQL_POSTGRESQL_USERNAME}'       POSTGRESQL_PASSWORD: '${SERVER_POSTGRE_SQL_POSTGRESQL_PASSWORD}'       POSTGRESQL_DATABASE: '${SERVER_POSTGRE_SQL_POSTGRESQL_DATABASE}'   nestjs-mod-fullstack-postgre-sql-migrations:     image: 'node:20-bullseye-slim'     container_name: 'nestjs-mod-fullstack-postgre-sql-migrations'     networks:       - 'nestjs-mod-fullstack-network'     working_dir: '/app'     volumes:       - './../:/app'     command: 'npm run docker-compose-full:prod:fill:database'     tty: true     environment:       NX_SKIP_NX_CACHE: 'true'       SERVER_ROOT_DATABASE_URL: '${SERVER_ROOT_DATABASE_URL}'       SERVER_APP_DATABASE_URL: '${SERVER_APP_DATABASE_URL}'     depends_on:       nestjs-mod-fullstack-postgre-sql:         condition: 'service_healthy'   nestjs-mod-fullstack-server:     image: 'nestjs-mod-fullstack-server:latest'     container_name: 'nestjs-mod-fullstack-server'     networks:       - 'nestjs-mod-fullstack-network'     healthcheck:       test: ['CMD-SHELL', 'npx -y wait-on --timeout= --interval=1000 --window --verbose --log http://localhost:8080/api/health']       interval: 30s       timeout: 10s       retries: 10     tty: true     environment:       SERVER_APP_DATABASE_URL: '${SERVER_APP_DATABASE_URL}'     restart: 'always'     depends_on:       nestjs-mod-fullstack-postgre-sql:         condition: service_healthy       nestjs-mod-fullstack-postgre-sql-migrations:         condition: service_completed_successfully   nestjs-mod-fullstack-nginx:     image: nginx:alpine     container_name: 'nestjs-mod-fullstack-nginx'     networks:       - 'nestjs-mod-fullstack-network'     volumes:       - ../.docker/nginx:/etc/nginx/conf.d       - ../dist/apps/client/browser:/usr/share/nginx/html     restart: 'always'     depends_on:       nestjs-mod-fullstack-server:         condition: service_healthy     ports:       - '8080:8080'   nestjs-mod-fullstack-https-portal:     image: steveltn/https-portal:1     container_name: 'nestjs-mod-fullstack-https-portal'     networks:       - 'nestjs-mod-fullstack-network'     ports:       - '80:80'       - '443:443'     links:       - nestjs-mod-fullstack-nginx     restart: always     environment:       STAGE: '${HTTPS_PORTAL_STAGE}'       FORCE_RENEW: 'true'       DOMAINS: '${SERVER_DOMAIN} -> http://nestjs-mod-fullstack-nginx:8080'     depends_on:       nestjs-mod-fullstack-nginx:         condition: service_started     volumes:       - nestjs-mod-fullstack-https-portal-volume:/var/lib/https-portal volumes:   nestjs-mod-fullstack-postgre-sql-volume:     name: 'nestjs-mod-fullstack-postgre-sql-volume'   nestjs-mod-fullstack-https-portal-volume:     name: 'nestjs-mod-fullstack-https-portal-volume' 

15. На локальном компьютере, добавляем новую переменную окружения с нашим доменом vps1724252356.tech0.ru

Обновленный файл .docker/docker-compose-full.env

SERVER_ROOT_DATABASE_URL=postgres://postgres:postgres_password@nestjs-mod-fullstack-postgre-sql:5432/postgres?schema=public SERVER_APP_DATABASE_URL=postgres://app:app_password@nestjs-mod-fullstack-postgre-sql:5432/app?schema=public SERVER_POSTGRE_SQL_POSTGRESQL_USERNAME=postgres SERVER_POSTGRE_SQL_POSTGRESQL_PASSWORD=postgres_password SERVER_POSTGRE_SQL_POSTGRESQL_DATABASE=postgres SERVER_DOMAIN=vps1724252356.tech0.ru HTTPS_PORTAL_STAGE=production # local|production 

17. На локальном компьютере, коммитим изменения в репозиторий

Команды

git commit -m "fix: some updates" git push 

18. Подключаемся к удаленному серверу, получаем новые изменения и останавливаем «PM2» и «Docker Compose» режимы

Команды

ssh root@194.226.49.162 cd nestjs-mod-fullstack npm run pm2-full:prod:stop npm run docker-compose-full:prod:stop 

19. Перезапускаем все приложения в режиме «Docker Compose» и прогоняем E2E-тесты, адрес сервера указываем https://vps1724252356.tech0.ru

Команды

npm run docker-compose-full:prod:start export BASE_URL=https://vps1724252356.tech0.ru && npm run test:e2e 
Вывод консоли
root@vps1724252356:~/nestjs-mod-fullstack# npm run docker-compose-full:prod:start  > @nestjs-mod-fullstack/source@0.0.0 docker-compose-full:prod:start > export NX_DAEMON=false && export DISABLE_SERVE_STATIC=true && npm run generate && npm run build -- -c production && npm run docker:build:server:latest && export COMPOSE_INTERACTIVE_NO_CLI=1 && docker compose -f ./.docker/docker-compose-full.yml --env-file ./.docker/docker-compose-full.env --compatibility up -d   > @nestjs-mod-fullstack/source@0.0.0 generate > ./node_modules/.bin/nx run-many --exclude=@nestjs-mod-fullstack/source --all -t=generate --skip-nx-cache=true && npm run make-ts-list && npm run lint:fix      ✔  nx run server:generate (42s)  ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————   NX   Successfully ran target generate for project server (42s)   > @nestjs-mod-fullstack/source@0.0.0 make-ts-list > ./node_modules/.bin/rucken make-ts-list   > @nestjs-mod-fullstack/source@0.0.0 lint:fix > npm run tsc:lint && ./node_modules/.bin/nx run-many --exclude=@nestjs-mod-fullstack/source --all -t=lint --fix   > @nestjs-mod-fullstack/source@0.0.0 tsc:lint > ./node_modules/.bin/tsc --noEmit -p tsconfig.base.json      ✔  nx run app-angular-rest-sdk:lint  [existing outputs match the cache, left as is]    ✔  nx run server:lint  [existing outputs match the cache, left as is]    ✔  nx run client:lint  [existing outputs match the cache, left as is]    ✔  nx run server-e2e:lint  [existing outputs match the cache, left as is]  ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————   NX   Successfully ran target lint for 4 projects (357ms)        With additional flags:         --fix=true  Nx read the output from the cache instead of running the command for 4 out of 4 tasks.   > @nestjs-mod-fullstack/source@0.0.0 build > npm run generate && npm run tsc:lint && ./node_modules/.bin/nx run-many --exclude=@nestjs-mod-fullstack/source --all -t=build --skip-nx-cache=true -c production   > @nestjs-mod-fullstack/source@0.0.0 generate > ./node_modules/.bin/nx run-many --exclude=@nestjs-mod-fullstack/source --all -t=generate --skip-nx-cache=true && npm run make-ts-list && npm run lint:fix      ✔  nx run server:generate (38s)  ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————   NX   Successfully ran target generate for project server (38s)   > @nestjs-mod-fullstack/source@0.0.0 make-ts-list > ./node_modules/.bin/rucken make-ts-list   > @nestjs-mod-fullstack/source@0.0.0 lint:fix > npm run tsc:lint && ./node_modules/.bin/nx run-many --exclude=@nestjs-mod-fullstack/source --all -t=lint --fix   > @nestjs-mod-fullstack/source@0.0.0 tsc:lint > ./node_modules/.bin/tsc --noEmit -p tsconfig.base.json      ✔  nx run app-angular-rest-sdk:lint  [existing outputs match the cache, left as is]    ✔  nx run server:lint  [existing outputs match the cache, left as is]    ✔  nx run client:lint  [existing outputs match the cache, left as is]    ✔  nx run server-e2e:lint  [existing outputs match the cache, left as is]  ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————   NX   Successfully ran target lint for 4 projects (350ms)        With additional flags:         --fix=true  Nx read the output from the cache instead of running the command for 4 out of 4 tasks.   > @nestjs-mod-fullstack/source@0.0.0 tsc:lint > ./node_modules/.bin/tsc --noEmit -p tsconfig.base.json      ✔  nx run app-rest-sdk:build (8s)    ✔  nx run app-angular-rest-sdk:build:production (9s)    ✔  nx run server:build:production (13s)    ✔  nx run client:build:production (19s)  ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————   NX   Successfully ran target build for 4 projects (28s)   > @nestjs-mod-fullstack/source@0.0.0 docker:build:server:latest > docker build -t nestjs-mod-fullstack-server:latest -f ./.docker/server.Dockerfile . --progress=plain  #0 building with "default" instance using docker driver  #1 [internal] load build definition from server.Dockerfile #1 transferring dockerfile: 1.65kB done #1 DONE 0.0s  #2 [internal] load metadata for docker.io/library/node:20.16.0-alpine #2 DONE 1.2s  #3 [internal] load .dockerignore #3 transferring context: 79B 0.0s done #3 DONE 0.0s  #4 [builder 1/7] FROM docker.io/library/node:20.16.0-alpine@sha256:eb8101caae9ac02229bd64c024919fe3d4504ff7f329da79ca60a04db08cef52 #4 DONE 0.0s  #5 [internal] load build context #5 transferring context: 1.20MB 0.2s done #5 DONE 0.2s  #6 [builder 2/7] WORKDIR /usr/src/app #6 CACHED  #7 [builder 3/7] COPY . . #7 DONE 0.2s  #8 [builder 4/7] RUN apk add dumb-init #8 0.630 fetch https://dl-cdn.alpinelinux.org/alpine/v3.20/main/x86_64/APKINDEX.tar.gz #8 1.055 fetch https://dl-cdn.alpinelinux.org/alpine/v3.20/community/x86_64/APKINDEX.tar.gz #8 1.791 (1/1) Installing dumb-init (1.2.5-r3) #8 1.820 Executing busybox-1.36.1-r29.trigger #8 1.836 OK: 11 MiB in 17 packages #8 DONE 2.0s  #9 [builder 5/7] RUN apk add jq #9 1.136 (1/2) Installing oniguruma (6.9.9-r0) #9 1.302 (2/2) Installing jq (1.7.1-r0) #9 1.361 Executing busybox-1.36.1-r29.trigger #9 1.379 OK: 12 MiB in 19 packages #9 DONE 1.7s  #10 [builder 6/7] RUN echo $(cat package.json | jq 'del(.devDependencies)') > package.json #10 DONE 0.5s  #11 [builder 7/7] RUN rm -rf nx.json package-lock.json .dockerignore &&     cp .docker/nx.json nx.json &&     cp .docker/.dockerignore .dockerignore &&     npm install &&     npm install --save-dev nx@19.5.3 prisma@5.18.0 prisma-class-generator@0.2.11 &&     echo '' > .env &&     npm run prisma:generate &&     rm -rf /usr/src/app/node_modules/@nx &&     rm -rf /usr/src/app/node_modules/@prisma-class-generator &&     rm -rf /usr/src/app/node_modules/@angular  &&     rm -rf /usr/src/app/node_modules/@swc  &&     rm -rf /usr/src/app/node_modules/@babel  &&     rm -rf /usr/src/app/node_modules/@angular-devkit &&     rm -rf /usr/src/app/node_modules/@ngneat &&     rm -rf /usr/src/app/node_modules/@types &&     rm -rf /usr/src/app/node_modules/@ng-packagr #11 112.9 #11 112.9 added 408 packages, and audited 409 packages in 2m #11 112.9 #11 112.9 53 packages are looking for funding #11 112.9   run `npm fund` for details #11 112.9 #11 112.9 5 moderate severity vulnerabilities #11 112.9 #11 112.9 To address all issues (including breaking changes), run: #11 112.9   npm audit fix --force #11 112.9 #11 112.9 Run `npm audit` for details. #11 112.9 npm notice #11 112.9 npm notice New patch version of npm available! 10.8.1 -> 10.8.2 #11 112.9 npm notice Changelog: https://github.com/npm/cli/releases/tag/v10.8.2 #11 112.9 npm notice To update run: npm install -g npm@10.8.2 #11 112.9 npm notice #11 166.3 #11 166.3 added 106 packages, and audited 515 packages in 53s #11 166.3 #11 166.3 66 packages are looking for funding #11 166.3   run `npm fund` for details #11 166.4 #11 166.4 5 moderate severity vulnerabilities #11 166.4 #11 166.4 To address all issues (including breaking changes), run: #11 166.4   npm audit fix --force #11 166.4 #11 166.4 Run `npm audit` for details. #11 166.7 #11 166.7 > @nestjs-mod-fullstack/source@0.0.0 prisma:generate #11 166.7 > ./node_modules/.bin/nx run-many -t=prisma-generate #11 166.7 #11 169.4 #11 169.4  NX   Running target prisma-generate for project server: #11 169.4 #11 169.4 - server #11 169.4 #11 169.4 #11 177.6 #11 177.6 > nx run server:prisma-generate #11 177.6 #11 177.6 > ./node_modules/.bin/prisma generate --schema=./apps/server/src/prisma/app-schema.prisma #11 177.6 #11 177.6 Environment variables loaded from .env #11 177.6 Prisma schema loaded from apps/server/src/prisma/app-schema.prisma #11 177.6 prisma:info [Prisma Class Generator]:Handler Registered. #11 177.6 prisma:info [Prisma Class Generator]:Generate /usr/src/app/apps/server/src/app/generated/rest/dto/app_demo.ts #11 177.6 prisma:info [Prisma Class Generator]:Generate /usr/src/app/apps/server/src/app/generated/rest/dto/migrations.ts #11 177.6 #11 177.6 ✔ Generated Prisma Client (v5.18.0, engine=binary) to ./node_modules/@prisma/app-client in 308ms #11 177.6 #11 177.6 ✔ Generated Prisma Class Generator to ./apps/server/src/app/generated/rest/dto in 408ms #11 177.6 #11 177.6 Start by importing your Prisma Client (See: http://pris.ly/d/importing-client) #11 177.6 #11 177.6 Tip: Easily identify and fix slow SQL queries in your app. Optimize helps you enhance your visibility: https://pris.ly/--optimize #11 177.6 #11 177.6 #11 177.6 #11 177.6 #11 177.6  NX   Successfully ran target prisma-generate for project server #11 177.6 #11 177.6 #11 DONE 178.9s  #12 [stage-1 3/4] COPY --from=builder /usr/src/app/ /usr/src/app/ #12 DONE 8.3s  #13 [stage-1 4/4] COPY --from=builder /usr/bin/dumb-init /usr/bin/dumb-init #13 DONE 0.1s  #14 exporting to image #14 exporting layers #14 exporting layers 8.7s done #14 writing image sha256:049c6f471ddfe18b811e6aac12cccd777dfab0bb1d599b00c91beb51d1ff188f done #14 naming to docker.io/library/nestjs-mod-fullstack-server:latest done #14 DONE 8.7s WARN[0000] /root/nestjs-mod-fullstack/.docker/docker-compose-full.yml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion [+] Running 5/5  ✔ Container nestjs-mod-fullstack-postgre-sql             Healthy                                                                                                13.9s  ✔ Container nestjs-mod-fullstack-postgre-sql-migrations  Exited                                                                                                 30.9s  ✔ Container nestjs-mod-fullstack-server                  Healthy                                                                                                65.1s  ✔ Container nestjs-mod-fullstack-nginx                   Started                                                                                                65.1s  ✔ Container nestjs-mod-fullstack-https-portal            Started                                                                                                65.2s  root@vps1724252356:~/nestjs-mod-fullstack# export BASE_URL=https://vps1724252356.tech0.ru && npm run test:e2e  > @nestjs-mod-fullstack/source@0.0.0 test:e2e > ./node_modules/.bin/nx run-many --exclude=@nestjs-mod-fullstack/source --all -t=e2e --skip-nx-cache=true --output-style=stream-without-prefixes    > nx run client-e2e:e2e  > playwright test   NX   Running target e2e for 2 projects and 1 task they depend on    NX   Running target e2e for 2 projects and 1 task they depend on     →  Executing 1/3 remaining tasks...      ✔  nx run client-e2e:e2e (25s)  ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————   ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————    ✔  nx run server:build:production (14s)  ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————    ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————   NX   Running target e2e for 2 projects and 1 task they depend on     →  Executing 1/1 remaining tasks...     ⠹  nx run server-e2e:e2e     ✔  2/2 succeeded [0 read from cache]   PASS   server-e2e  apps/server-e2e/src/server/server.spec.ts   GET /api     ✓ should return a message (160 ms)    ✔  nx run server-e2e:e2e (10s)  ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————   NX   Successfully ran target e2e for 2 projects and 1 task they depend on (50s) 

20. Перезапускаем все приложения в режиме PM2-продакшен и прогоняем E2E-тесты, адрес сервера указываем http://vps1724252356.tech0.ru:3000

Команды

npm run pm2-full:prod:start export BASE_URL=http://vps1724252356.tech0.ru:3000 && npm run test:e2e 
Вывод консоли
root@vps1724252356:~/nestjs-mod-fullstack# npm run pm2-full:prod:start  > @nestjs-mod-fullstack/source@0.0.0 pm2-full:prod:start > npm run generate && npm run build -- -c production && npm run docker-compose:start-prod:server && npm run db:create && npm run flyway:migrate && npm run pm2:start   > @nestjs-mod-fullstack/source@0.0.0 generate > ./node_modules/.bin/nx run-many --exclude=@nestjs-mod-fullstack/source --all -t=generate --skip-nx-cache=true && npm run make-ts-list && npm run lint:fix      ✔  nx run server:generate (1m)  ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————   NX   Successfully ran target generate for project server (1m)   > @nestjs-mod-fullstack/source@0.0.0 make-ts-list > ./node_modules/.bin/rucken make-ts-list   > @nestjs-mod-fullstack/source@0.0.0 lint:fix > npm run tsc:lint && ./node_modules/.bin/nx run-many --exclude=@nestjs-mod-fullstack/source --all -t=lint --fix   > @nestjs-mod-fullstack/source@0.0.0 tsc:lint > ./node_modules/.bin/tsc --noEmit -p tsconfig.base.json      ✔  nx run app-angular-rest-sdk:lint  [existing outputs match the cache, left as is]    ✔  nx run client:lint  [existing outputs match the cache, left as is]    ✔  nx run server-e2e:lint  [existing outputs match the cache, left as is]    ✔  nx run server:lint (4s)  ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————   NX   Successfully ran target lint for 4 projects (5s)        With additional flags:         --fix=true  Nx read the output from the cache instead of running the command for 3 out of 4 tasks.   > @nestjs-mod-fullstack/source@0.0.0 build > npm run generate && npm run tsc:lint && ./node_modules/.bin/nx run-many --exclude=@nestjs-mod-fullstack/source --all -t=build --skip-nx-cache=true -c production   > @nestjs-mod-fullstack/source@0.0.0 generate > ./node_modules/.bin/nx run-many --exclude=@nestjs-mod-fullstack/source --all -t=generate --skip-nx-cache=true && npm run make-ts-list && npm run lint:fix      ✔  nx run server:generate (43s)  ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————   NX   Successfully ran target generate for project server (43s)   > @nestjs-mod-fullstack/source@0.0.0 make-ts-list > ./node_modules/.bin/rucken make-ts-list   > @nestjs-mod-fullstack/source@0.0.0 lint:fix > npm run tsc:lint && ./node_modules/.bin/nx run-many --exclude=@nestjs-mod-fullstack/source --all -t=lint --fix   > @nestjs-mod-fullstack/source@0.0.0 tsc:lint > ./node_modules/.bin/tsc --noEmit -p tsconfig.base.json      ✔  nx run app-angular-rest-sdk:lint  [existing outputs match the cache, left as is]    ✔  nx run client:lint  [existing outputs match the cache, left as is]    ✔  nx run server:lint  [existing outputs match the cache, left as is]    ✔  nx run server-e2e:lint  [existing outputs match the cache, left as is]  ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————   NX   Successfully ran target lint for 4 projects (373ms)        With additional flags:         --fix=true  Nx read the output from the cache instead of running the command for 4 out of 4 tasks.   > @nestjs-mod-fullstack/source@0.0.0 tsc:lint > ./node_modules/.bin/tsc --noEmit -p tsconfig.base.json      ✔  nx run app-rest-sdk:build (1m)    ✔  nx run app-angular-rest-sdk:build:production (1m)    ✔  nx run server:build:production (1m)    ✔  nx run client:build:production (23s)  ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————   NX   Successfully ran target build for 4 projects (2m)   > @nestjs-mod-fullstack/source@0.0.0 docker-compose:start-prod:server > export COMPOSE_INTERACTIVE_NO_CLI=1 && docker compose -f ./apps/server/docker-compose-prod.yml --env-file ./apps/server/docker-compose-prod.env --compatibility up -d  WARN[0000] /root/nestjs-mod-fullstack/apps/server/docker-compose-prod.yml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion [+] Running 1/1  ✔ Container server-postgre-sql  Started                                                                                                                          0.7s  > @nestjs-mod-fullstack/source@0.0.0 db:create > ./node_modules/.bin/nx run-many -t=db-create      ✔  nx run server:db-create (3s)  ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————   NX   Successfully ran target db-create for project server (4s)   > @nestjs-mod-fullstack/source@0.0.0 flyway:migrate > ./node_modules/.bin/nx run-many -t=flyway-migrate      ✔  nx run server:flyway-migrate (5s)  ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————   NX   Successfully ran target flyway-migrate for project server (5s)   > @nestjs-mod-fullstack/source@0.0.0 pm2:start > ./node_modules/.bin/pm2 start ./ecosystem-prod.config.json && npm run wait-on -- --log http://localhost:3000/api/health --log http://localhost:3000  [PM2] Spawning PM2 daemon with pm2_home=/root/.pm2 [PM2] PM2 Successfully daemonized [PM2][WARN] Applications nestjs-mod-fullstack not running, starting... [PM2] App [nestjs-mod-fullstack] launched (1 instances) ┌────┬─────────────────────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐ │ id │ name                    │ namespace   │ version │ mode    │ pid      │ uptime │ ↺    │ status    │ cpu      │ mem      │ user     │ watching │ ├────┼─────────────────────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤ │ 0  │ nestjs-mod-fullstack    │ default     │ N/A     │ fork    │ 505154   │ 0s     │ 0    │ online    │ 0%       │ 28.8mb   │ root     │ disabled │ └────┴─────────────────────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘  > @nestjs-mod-fullstack/source@0.0.0 wait-on > ./node_modules/.bin/wait-on --timeout=240000 --interval=1000 --window --verbose --log http://localhost:3000/api/health --log http://localhost:3000  waiting for 2 resources: http://localhost:3000/api/health, http://localhost:3000 making HTTP(S) head request to  url:http://localhost:3000/api/health ... making HTTP(S) head request to  url:http://localhost:3000 ...   HTTP(S) error for http://localhost:3000/api/health AggregateError   HTTP(S) error for http://localhost:3000 AggregateError making HTTP(S) head request to  url:http://localhost:3000/api/health ... making HTTP(S) head request to  url:http://localhost:3000 ...   HTTP(S) error for http://localhost:3000/api/health AggregateError   HTTP(S) error for http://localhost:3000 AggregateError making HTTP(S) head request to  url:http://localhost:3000/api/health ...   HTTP(S) error for http://localhost:3000/api/health AggregateError making HTTP(S) head request to  url:http://localhost:3000 ...   HTTP(S) error for http://localhost:3000 AggregateError making HTTP(S) head request to  url:http://localhost:3000/api/health ... making HTTP(S) head request to  url:http://localhost:3000 ...   HTTP(S) result for http://localhost:3000/api/health: {   status: 200,   statusText: 'OK',   headers: Object [AxiosHeaders] {     'x-powered-by': 'Express',     vary: 'Origin',     'access-control-allow-credentials': 'true',     'x-request-id': 'bd416d0d-2e53-4f4a-8402-58e367c833c5',     'cache-control': 'no-cache, no-store, must-revalidate',     'content-type': 'application/json; charset=utf-8',     'content-length': '107',     etag: 'W/"6b-ouXVoNOXyOxnMfI7caewF8/p97A"',     date: 'Thu, 22 Aug 2024 18:58:09 GMT',     connection: 'keep-alive',     'keep-alive': 'timeout=5'   },   data: '' } waiting for 1 resources: http://localhost:3000   HTTP(S) result for http://localhost:3000: {   status: 200,   statusText: 'OK',   headers: Object [AxiosHeaders] {     'x-powered-by': 'Express',     vary: 'Origin',     'access-control-allow-credentials': 'true',     'accept-ranges': 'bytes',     'cache-control': 'public, max-age=0',     'last-modified': 'Thu, 22 Aug 2024 18:57:47 GMT',     etag: 'W/"8e8-1917b73ba3d"',     'content-type': 'text/html; charset=UTF-8',     'content-length': '2280',     date: 'Thu, 22 Aug 2024 18:58:09 GMT',     connection: 'keep-alive',     'keep-alive': 'timeout=5'   },   data: '' } wait-on(505210) complete  root@vps1724252356:~/nestjs-mod-fullstack# export BASE_URL=http://vps1724252356.tech0.ru:3000 && npm run test:e2e  > @nestjs-mod-fullstack/source@0.0.0 test:e2e > ./node_modules/.bin/nx run-many --exclude=@nestjs-mod-fullstack/source --all -t=e2e --skip-nx-cache=true --output-style=stream-without-prefixes    > nx run client-e2e:e2e  > playwright test   NX   Running target e2e for 2 projects and 1 task they depend on    NX   Running target e2e for 2 projects and 1 task they depend on     →  Executing 1/3 remaining tasks...      ✔  nx run client-e2e:e2e (39s)  ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————   ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————    ✔  nx run server:build:production (16s)  ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————    ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————   NX   Running target e2e for 2 projects and 1 task they depend on     →  Executing 1/1 remaining tasks...     ⠴  nx run server-e2e:e2e     ✔  2/2 succeeded [0 read from cache]   PASS   server-e2e  apps/server-e2e/src/server/server.spec.ts   GET /api     ✓ should return a message (74 ms)    ✔  nx run server-e2e:e2e (11s)  ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————   NX   Successfully ran target e2e for 2 projects and 1 task they depend on (1m) 

21. Настраиваем автоматический перезапуск PM2-продакшен режима при перезагрузке выделенного сервера

Команды

./node_modules/.bin/pm2 startup ./node_modules/.bin/pm2 save 
Вывод консоли
root@vps1724252356:~/nestjs-mod-fullstack# ./node_modules/.bin/pm2 startup [PM2] Init System found: systemd Platform systemd Template [Unit] Description=PM2 process manager Documentation=https://pm2.keymetrics.io/ After=network.target  [Service] Type=forking User=root LimitNOFILE=infinity LimitNPROC=infinity LimitCORE=infinity Environment=PATH=/root/.vscode-server/cli/servers/Stable-fee1edb8d6d72a0ddff41e5f71a671c23ed924b9/server/bin/remote-cli:/root/.nvm/versions/node/v20.16.0/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin Environment=PM2_HOME=/root/.pm2 PIDFile=/root/.pm2/pm2.pid Restart=on-failure  ExecStart=/root/nestjs-mod-fullstack/node_modules/pm2/bin/pm2 resurrect ExecReload=/root/nestjs-mod-fullstack/node_modules/pm2/bin/pm2 reload all ExecStop=/root/nestjs-mod-fullstack/node_modules/pm2/bin/pm2 kill  [Install] WantedBy=multi-user.target  Target path /etc/systemd/system/pm2-root.service Command list [ 'systemctl enable pm2-root' ] [PM2] Writing init configuration in /etc/systemd/system/pm2-root.service [PM2] Making script booting at startup... [PM2] [-] Executing: systemctl enable pm2-root... Created symlink /etc/systemd/system/multi-user.target.wants/pm2-root.service → /etc/systemd/system/pm2-root.service. [PM2] [v] Command successfully executed. +---------------------------------------+ [PM2] Freeze a process list on reboot via: $ pm2 save  [PM2] Remove init script via: $ pm2 unstartup systemd  root@vps1724252356:~/nestjs-mod-fullstack# ./node_modules/.bin/pm2 save [PM2] Saving current process list... [PM2] Successfully saved in /root/.pm2/dump.pm2 

22. Перезагружаем и проверяем через E2E-тест работу обоих режимом

Команды

sudo reboot  cd nestjs-mod-fullstack export BASE_URL=http://vps1724252356.tech0.ru:3000 && npm run test:e2e export BASE_URL=https://vps1724252356.tech0.ru && npm run test:e2e 
Вывод консоли
root@vps1724252356:~/nestjs-mod-fullstack# export BASE_URL=http://vps1724252356.tech0.ru:3000 && npm run test:e2e  > @nestjs-mod-fullstack/source@0.0.0 test:e2e > ./node_modules/.bin/nx run-many --exclude=@nestjs-mod-fullstack/source --all -t=e2e --skip-nx-cache=true --output-style=stream-without-prefixes    > nx run client-e2e:e2e  > playwright test   NX   Running target e2e for 2 projects and 1 task they depend on    NX   Running target e2e for 2 projects and 1 task they depend on     →  Executing 1/3 remaining tasks...      ✔  nx run client-e2e:e2e (29s)  ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————   ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————    ✔  nx run server:build:production (15s)  ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————    ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————   NX   Running target e2e for 2 projects and 1 task they depend on     →  Executing 1/1 remaining tasks...     ⠏  nx run server-e2e:e2e     ✔  2/2 succeeded [0 read from cache]   PASS   server-e2e  apps/server-e2e/src/server/server.spec.ts   GET /api     ✓ should return a message (73 ms)    ✔  nx run server-e2e:e2e (12s)  ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————   NX   Successfully ran target e2e for 2 projects and 1 task they depend on (56s) 

В следующем посте я добавлю CI/CD конфиг для деплоя на выделенный сервер с помощью GitHub Actions…

Ссылки

https://nestjs.com — официальный сайт фреймворка
https://nestjs-mod.com — официальный сайт дополнительных утилит
https://github.com/nestjs-mod/nestjs-mod-fullstack — проект из поста
https://github.com/nestjs-mod/nestjs-mod-fullstack/commit/b9715a203364877a4ffae6e7cb3a1dff59ede6d6 — коммит на текущие изменения


ссылка на оригинал статьи https://habr.com/ru/articles/839400/