Запуск в продакшн

Мы написали основной функционал простенького магазина, и теперь пришло время выгрузить его на хостинг.

Админка будет сгенерирована в статичные файлы html, js и css, а вот публичный сайт мы запустим в режиме серверного рендеринга - ssr.

Серверный рендеринг нужен для того, чтобы ваш сайт могли индексировать поисковые машины. Хоть они давно и заявляют о поддержке SPA приложений, но на практике это не очень работает. Да и прочитать те же теги OpenGraph без серверного рендера никак не получится.

Конфигурации будет как для Nginx, так и для Apache2.

Настройки .env

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

SITE_URL=http://vesp-shop.test/
API_URL=http://vesp-shop.test/api/

Если вы планируете собирать проект локально и выгружать на сервер - в этом файле нужно прописать правильные url конечного, а не локального, сайта.

Если же вы будете собирать проект сразу на хостинге, то там уже должен лежать .env c корректными настройками и ничего менять не нужно.

Генерация приложений

Первым делом собираем наш проект в статическом виде.

composer node:generate

В результате мы получаем две директории внутри frontend/dist: admin и site. В каждой лежат статические файлы, которые будут отдаваться пользователю веб-сервером.

Html страниц ровно одна - 200.html, больше нам и не нужно. Её задача вывести индикатор загрузки и ссылки на js и css файлы, которые отрисуют наш код.

В принципе, Nuxt может статически генерировать все маршруты, но при нашем подходе они просто не нужны. А так, да, можно статически сгенерировать небольшой сайт, например с документацией, и запустить его без ssr.

Попробуйте ради интереса закомментировать build.exclude в nuxt.config.js и посмотреть, что получится после генерации

Config.generate = {
  cache: false,
  dir: 'dist/admin',
//  exclude: [/^\//],
}

Сборка и запуск сервера приложения

Теперь самое интересное - запуск site в режиме ssr.

Nuxt сам по себе вполне веб-сервер, который запускается из консоли и слушает определённый порт. Вы уже видели это в режиме разработки dev, а теперь нам нужно запустить тот же сервер, но уже в production режиме.

Делается это командой composer node:start, которая соберёт site, а потом запустит менеджер процессов pm2 для управления Nuxt.

Зачем нам менджер процессов? Ну как минимум для того, чтобы следить за состоянием Nuxt, логировать его сообщения и перезапускать, если он упадёт. Конфигурация pm2 находится в файле frontend/ecosystem.config.js:

// Определяем режим работы
const prod = process.argv.includes('production')

module.exports = {
  apps: [
    {
      // Имя приложения
      name: 'site',
      exec_mode: 'cluster',
      // Процесс для работы - наш Nuxt
      script: './node_modules/nuxt/bin/nuxt.js',
      // Аргументы для передачи Nuxt зависят от режима работы
      // Здесь мы прописывааем порт, который будет слушать сервер
      args: (prod ? 'start ' : '') + '--config-file ./src/site/nuxt.config.js -H 127.0.0.1 -p 20001',
      // Количество процессов
      instances: 2,
      autorestart: true,
      max_memory_restart: '1G',
      // рабочий режим - основной
      env_production: {
        NODE_ENV: 'production',
        // Не следить за изменением файов
        watch: false,
      },
      // режим разработки, обычно не используется
      env_development: {
        NODE_ENV: 'development',
        watch: true,
      },
    },
  ],
}

Все настройки можно посмотреть в документации.

Вот, что мы видим после запуска сервера:

В конфигурации прописан порт 20001, значит можно открывать http://127.0.0.1:20001.

Когда сервер запущен, вы можете вызывать ./frontend/node_modules/.bin/pm2 с разными командами, например проверить состояние приложений ./frontend/node_modules/.bin/pm2 status, logs выведет сообщения, а monit запустит своеобразный центр управления.

Остановка сервера производится командой composer node:stop

Деплой на сервер

Итак, у нас есть статически собранная админка, и сайт в двух вариантах: статически собранный и в режиме серверного рендера.

Теперь берём любой хостинг с NodeJS и PHP, например https://modhost.pro, главное, чтобы давали не менее 1 гигабайта свободного места, потому что javascript зависимости занимают прилично мегабайт.

На modhost это получается тариф "Минимальный" за 180 руб.

У меня вот такие настройки Deployment в PhpStorm, через него я и выгружаю свой проект на хостинг:

После выгрузки заходим на сервер через SSH.

У modhost.pro в консоли по умолчанию используется php 7.0, а нам нужен минимум 7.4, так что делаем ссылку на правильную версию:

mkdir ~/bin
ln -s /usr/bin/php7.4 ~/bin/php
source ~/.profile
php -v

В результате должен быть PHP 7.4.27 (cli) (built: Dec 20 2021 21:27:56) ( NTS ) или типа того.

Теперь можно делать

composer install
composer node:install

Дальше переименовываем стандартный файл настроек и редактируем

mv .env.dist .env
nano .env

Я пишу свои настройки, вам нужно указать свои:

APP_NAME="Vesp Framework"

SITE_URL=http://s30069.h10.modhost.pro/
API_URL=http://s30069.h10.modhost.pro/api/

CORS=1

DB_DRIVER=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_PREFIX=app_
DB_DATABASE=s30069
DB_USERNAME=s30069
DB_PASSWORD=password
DB_CHARSET=utf8mb4
DB_COLLATION=utf8mb4_general_ci
DB_FOREIGN_KEYS=1

JWT_SECRET=secret
JWT_EXPIRE=2592000
JWT_MAX=3

UPLOAD_DIR=/home/s30069/upload/
CACHE_DIR=/home/s30069/tmp/

Теперь можно создать и засеять таблицы

composer db:migrate
composer db:seed

Ну и собрать приложения через composer node:generate

Финальный штрих - запуск pm2 composer node:start

Обратите внимание, что порт 20001 уже может быть занят на сервере, если кто-то другой запустил свой проект на Vesp раньше вас. Придётся указать любой другой свободный порт в frontend/ecosystem.config.js, например 20002, 20003 и т.д.

Все приложения готовы, осталось только указать веб-серверу, как с ними работать.

Настройка Nginx

Вот мой рабочий конфиг на modhost с комментариями

# Отдельная обработка PHP
location ~ \.php$ {
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    # Имя обработчика зависит от вашего юзера
    fastcgi_pass backend-s30069;
}

# Эти запросы улетают в API
location ~ ^/(api|__clockwork)/ {
    rewrite ^/(api|__clockwork)/(.*)$ /api.php;
}

# К запросу в админку добавляем слэш на конце
location /admin {
    return 301 /admin/;
}

# Файлы админки обсуживаются прямо оттуда, куда они собираются
location /admin/ {
    root /home/s30069/frontend/dist/;
    # Логировать запросы в админку нам не нужно
    access_log off;
    # Настройки кэширования статики
    gzip on;
    gzip_types text/css application/javascript application/x-javascript text/javascript image/svg+xml;
    expires 1y;
    # Указание страницы по умолчанию
    try_files $uri /admin/200.html;
}

location / {  
    # Серверный режим
    # Передаём все запросы в Nuxt
    proxy_http_version 1.1;
    # Тут лучше ничего не менять
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header Host $http_host;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_redirect off;
    proxy_read_timeout 240s;

    # А вот и адрес нашего сервера
    # Напоминаю, что порт может быть занят - и тогда вы указываете здесь другой
    proxy_pass http://127.0.0.1:20001;

    # Сайт так же можно запустить и в статическом режиме
    # root /home/s30069/frontend/dist/site/;
    # access_log off;
    # gzip on;
    # gzip_types text/css application/javascript application/x-javascript text/javascript image/svg+xml;
    # expires 1y;
    # try_files $uri /200.html;
}

В итоге всё работает как положено:

Настройка Apache2

Файл с примером правил Apache2 лежит по адресу www/.ht.access, но в нём нет настроек для работы ssr.

В отличие от Nginx, я не смог настроить чтение статических файлов из директории выше www, так что вам нужно будет перенести содержимое ~/frontend/dist/site в корень ~/www/, сохранив при этом api.php.

Вот рабочая конфигурация с одного моего проекта:

DirectoryIndex 200.html

RewriteEngine On
RewriteBase /

# Обработка запросов в API
RewriteCond %{REQUEST_URI} ^/(api|__clockwork)/ [NC]
RewriteRule ^(.*)$ api.php [L,QSA]

# Обработка запросов в админку, когда запрошенный адрес не найден
# Это для страниц сайта
RewriteCond %{REQUEST_URI} ^/admin/? [NC]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /admin/200.html [L,QSA]

# Тоже админка, но файл существует - это для скриптов и стилей
RewriteCond %{REQUEST_URI} ^/admin/? [NC]
RewriteRule ^(.*)$ $0 [L,QSA]

# А вот это передача всех оставшихся запросов на сервер Nuxt
# Флаг [P] говорит о проксировании
RewriteCond %{REQUEST_URI} ^/
RewriteRule ^(.*)$ http://127.0.0.1:20001/$1 [P]

Честно говоря, я не большой специалист по Apache2, он мне никогда не нравился, так что конфиг может быть не оптимален. Так же нет 100% уверенности, что все хостинги позволят ему проксировать запросы.

В любом случае, если на хостинге есть возможность настроить Nginx - лучше использовать именно его.

Заключение

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

Единственная сложность - это режим серверного рендера, но с готовыми конфигами проблем возникнуть не должно.

На следующем занятии подводим итоги, и на этом курс окончен.

Обновлено 18 октября 2022

Перенёс рабочий сайт на свой сервер, чтобы не платить деньги за чужой хостинг. Проект работает в статическом режиме, без SSR.

Можете заходить по адресу https://shop.vesp.pro и в админку https://shop.vesp.pro/admin/ (логин и пароль user). Всё открыто только для чтения, изменить ничего нельзя.

← Предыдущая заметка
Оформление заказов
Следующая заметка →
Завершение курса + всякие полезности
Комментарии (46)
bezumkinВасилий Наумкин
08.07.2022 07:46

Честно говоря, мне никогда не нравилась идея затачивать свои сайты под какие-то технологии поисковиков. Что AMP от Google, что Турбо от Яндекс. Так что здесь особо порадовать нечем.

А про всё остальное - читай заключительную заметку курса.

bezumkinВасилий Наумкин
08.07.2022 15:30

Так можно делать только на своём сервере.

А на публичном придётся писать скрипт для cron, который будет проверять pm2, и запускать, если не нашёл.

bezumkinВасилий Наумкин
10.07.2022 14:27

Хм, не знаю.

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

bezumkinВасилий Наумкин
10.07.2022 16:57

Отлично!

bezumkinВасилий Наумкин
13.08.2022 08:18

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

Такое запросто может быть на недорогих хостингах.

bezumkinВасилий Наумкин
15.10.2022 02:52

А ты, случаем, composer node:install не забыл?

bezumkinВасилий Наумкин
15.10.2022 10:53

У тебя поди свободного места на хостинге не хватает, чтобы установить все зависимости.

bezumkinВасилий Наумкин
16.10.2022 07:34

Ты в каком режиме-то запускаешь? Если в SSR, то нужно и PM2 запустить, то есть сервер Nuxt для обработки запросов. Именно для этого режима написан конфиг.

Если в статическом, то в конфиге нужно убрать эту часть и раскомментировать другую:

location / {  
    root /home/s30805/frontend/dist/site/;
    access_log off;
    gzip on;
    gzip_types text/css application/javascript application/x-javascript text/javascript image/svg+xml;
    expires 1y;
    try_files $uri /200.html;
}

Админка же у тебя в статическом режиме работает - http://s30805.h10.modhost.pro/admin/

bezumkinВасилий Наумкин
17.10.2022 03:19

Насколько я вижу - разобрался =)

bezumkinВасилий Наумкин
17.10.2022 15:55

Интересно, я с таким не сталкивался.

bezumkinВасилий Наумкин
18.10.2022 03:57

А сайта на modhost уже и нет - срок вышел, надо деньги платить.

Перенёс на свой сервер, чтобы больше не беспокоиться.

bezumkinВасилий Наумкин
19.10.2022 03:18

Общая документация по мета-тегам вот здесь, но конкретно по атрибутам тега html там ничего нет.

Но не проблема нагуглить. Добавляй в nuxt.config.js:

Config.head.htmlAttrs = {lang: 'ru'}
bezumkinВасилий Наумкин
19.10.2022 11:07

Ура!

bezumkinВасилий Наумкин
27.10.2022 17:24

Поздравляю!

Надеюсь, было не очень скучно.

bezumkinВасилий Наумкин
29.10.2022 11:02

Vesp на сайте подключается в минимальной комплектации, так что там нет моих добавок для мультиязычности.

Попробуй включить их в конфиге:

пора раздел открывать на сайте вопросы по Vesp

Пока что мы общаемся в комментариях к платному курсу. Собственно, именно поэтому он и платный =)

bezumkinВасилий Наумкин
03.11.2022 14:16

Конечно, её вообще нет смысла запускать SSR - никто не индексирует.

Ошибка 500 - это всегда ошибка сервера, то есть бэкенд. Так что смотри логи сервера, дело не во фронтенде.

bezumkinВасилий Наумкин
03.11.2022 15:42

Ошибку нашел, но не пойму как ее решить:

Откуда он берет s30805.h10.modhost.pro - не пойму?

После изменения .env нужно пересобрать фронтенд - API_URL прошивается в него в момент сборки, всё статичное.

bezumkinВасилий Наумкин
24.01.2023 15:28

Ну, какая-то ошибка - смотри логи pm2

У меня такое было раньше, потом ошибку нашёл и исправил, сейчас это можно увидеть только при обновлении сайта и перезапуске pm2.

bezumkin
Василий Наумкин
09.04.2024 01:45
Ошибка 500 Это не похоже на ошибку Nginx, это скорее всего ошибка PHP - надо смотреть его логи. Во...
futuris
Futuris
04.04.2024 05:56
Я просто немного запутался. Когда в абзаце "Vesp/Core" ты пишешь про "новый trait Fil...
bezumkin
Василий Наумкин
20.03.2024 18:21
Volledig!
Андрей
14.03.2024 10:47
Василий! Как всегда очень круто! Моё почтение!
russelgal
russel gal
09.03.2024 17:17
А этот стоило написать хотя бы затем, чтобы получить комментарий от юзера, который ничего не писал ...
inetlover
Александр Наумов
27.01.2024 00:06
Василий, спасибо! Извини, тупанул.
bezumkin
Василий Наумкин
22.01.2024 04:43
Давай-давай!
bezumkin
Василий Наумкин
24.12.2023 11:26
Спасибо!
bezumkin
Василий Наумкин
27.11.2023 02:43
Ура!
bezumkin
Василий Наумкин
25.11.2023 08:30
Vesp тянет 2 зависимости: vesp-frontent для фронта и vesp-core для бэкенда. Их можно обновлять, но э...