Как выложить приложение на сервер: Python, Node.js и PHP

На своей машине приложение запускается одной командой. На сервере эта же команда работает ровно до момента, пока открыт терминал: закрыли окно - процесс умер. Между работающим кодом на ноутбуке и работающим сервисом в интернете лежат несколько шагов, и почти все они одинаковы независимо от языка.

Разберём порядок целиком: как доставить код на сервер, где взять зависимости, куда девать пароли, как заставить приложение пережить выход из сессии и перезагрузку, и как потом обновлять его так, чтобы можно было откатиться.

Из чего состоит деплой

путь от кода на ноутбуке до работающего сервиса и что отвечает за каждый участок.
путь от кода на ноутбуке до работающего сервиса и что отвечает за каждый участок.

Разложим по слоям, потому что путаница обычно тут: приложение само по себе не умеет ни выживать после закрытия терминала, ни принимать запросы на 443 порту.

  • Код лежит в каталоге на сервере.
  • Окружение - интерпретатор нужной версии и библиотеки, от которых код зависит.
  • Служба следит, чтобы процесс работал всегда и поднимался после перезагрузки.
  • Веб-сервер принимает запросы снаружи, отдаёт статику и передаёт остальное приложению.

Каждый слой чинится отдельно, и понимание границ экономит часы: если сайт не открывается, вопрос в том, упал процесс или не доехал запрос.

Шаг 1. Доставить код

Три рабочих способа, от лучшего к терпимому.

Git. На сервере ставится клиент, репозиторий клонируется, обновление сводится к git pull. Для приватного репозитория заводят отдельный ключ только на чтение.

git clone https://git.example.com/user/project.git /var/www/project

rsync. Если репозитория нет, код синхронизируют напрямую с рабочей машины. Удобно тем, что переносит только изменившееся:

rsync -avz --exclude '.git' ./ user@203.0.113.10:/var/www/project/

Архив. Разово залить .tar.gz и распаковать - нормально для одноразового стенда, но для регулярных обновлений неудобно и легко забыть, что именно вы залили. Другие способы передачи файлов разобраны в статье про подключение по SSH.

Отдельное правило: в репозиторий не кладут файлы с паролями. Про них отдельный шаг ниже.

Шаг 2. Поставить окружение

Здесь единственное место, где языки заметно расходятся.

Python. Зависимости ставят в виртуальное окружение, а не в систему: системный интерпретатор используется самой ОС, и ломать его пакетами приложения не нужно.

python3 -m venv venv
./venv/bin/pip install -r requirements.txt

Node.js. Ставится нужная версия, зависимости разворачиваются строго по локфайлу, чтобы на сервере оказалось ровно то же, что у вас:

npm ci --omit=dev

PHP. Пакеты ставит composer, для боевого сервера с оптимизацией автозагрузчика:

composer install --no-dev --optimize-autoloader

Общий принцип для всех трёх: версии фиксируются файлом блокировки. Установка без него на сервере может подтянуть свежие библиотеки, и приложение поведёт себя иначе, чем у вас на машине. Это классический источник ошибок вида работает у меня.

Шаг 3. Настройки и секреты

Пароль от базы, ключи к внешним сервисам, токены - всё это не должно лежать в коде. Стандартное решение - переменные окружения: приложение читает их при запуске, а значения хранятся в отдельном файле на сервере.

Файл кладут рядом с проектом, закрывают правами и добавляют в исключения репозитория:

chmod 600 /var/www/project/.env

Так один и тот же код работает и на тестовом стенде, и в бою, отличаясь только содержимым файла. И при передаче проекта другому человеку вы не отдаёте вместе с ним боевые пароли.

Шаг 4. Запустить как службу

Главный шаг, без которого всё предыдущее бессмысленно. Приложение, запущенное руками в терминале, живёт до закрытия сессии.

Правильный способ - оформить его службой systemd: она поднимает процесс при старте сервера, перезапускает после падения и пишет логи в общий журнал. Подробный разбор с готовым файлом службы есть в статье про VPS для Telegram-бота - механика там ровно та же, меняется только команда запуска.

Короткий чек-лист по службе:

  • запускать не от root, а от отдельного пользователя;
  • прописать рабочий каталог и путь к файлу с переменными;
  • включить автозапуск и перезапуск при сбое;
  • проверить, что после reboot сервис поднялся сам.

Альтернатива - упаковать приложение в контейнер, тогда роль службы берёт на себя Docker с политикой перезапуска, см. Docker на VPS.

Шаг 5. Открыть наружу

Приложение обычно слушает локальный порт вида 127.0.0.1:8000, и напрямую из интернета к нему не достучаться. Это правильно: наружу его выставляют через веб-сервер, который принимает запросы, отдаёт статику и передаёт остальное приложению.

Настройка такого проксирования разобрана в статье про Nginx на VPS. После этого останется привязать домен и выпустить сертификат - именно в таком порядке, сертификат выдаётся только на домен, который уже указывает на сервер.

Само приложение при этом наружу не открывают: в правилах файрвола остаются только 80, 443 и порт SSH.

Обновление и откат

Первый деплой делают один раз, обновления - постоянно. Порядок, который экономит нервы:

1. Забрать новый код. 2. Доставить зависимости, если менялись. 3. Применить миграции базы, если они есть. 4. Перезапустить службу. 5. Проверить, что сервис отвечает и в журнале нет ошибок.

Откат должен существовать до того, как понадобится. Минимально достаточно двух вещей: знать коммит, на котором всё работало, и иметь свежую копию базы перед миграциями, см. резервное копирование. Более зрелый вариант - разворачивать новую версию в отдельный каталог и переключать символическую ссылку: тогда откат это переключение обратно, а не восстановление из архива.

Что автоматизировать потом

Первые деплои руками полезны: вы видите, из чего состоит процесс. Дальше это превращается в рутину и автоматизируется.

  • Скрипт деплоя - те же пять команд одним файлом, чтобы не забыть шаг.
  • Сборка на стороне разработки - на сервер приезжает готовый результат, а не исходники.
  • Автоматический деплой по коммиту - обновление запускается само после проверки тестами.

Начинать с последнего пункта не стоит: автоматизировать имеет смысл процесс, который вы уже понимаете вручную.

Частые ошибки

Запускают приложение в терминале и закрывают SSH. Процесс умирает вместе с сессией. Нужна служба, а не запущенная команда.

Ставят зависимости в систему. Пакеты приложения ломают системный интерпретатор, а обновление ОС ломает приложение. Для этого и существуют виртуальные окружения.

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

Открывают порт приложения наружу. Приложение слушает локально, снаружи стоит веб-сервер. Иначе вы выставляете в интернет отладочный сервер без защиты.

Обновляют базу без копии. Миграция, которая пошла не так, без бэкапа превращается в потерю данных.

Работают от root. Отдельный пользователь для приложения ограничивает ущерб при взломе, см. что такое root-доступ.

Частые вопросы

Что нужно, чтобы выложить приложение на сервер? Доставить код, поставить окружение и зависимости, вынести настройки в переменные окружения, оформить запуск службой и открыть приложение наружу через веб-сервер.

Почему приложение останавливается после выхода по SSH? Процесс, запущенный в терминале, привязан к сессии. Чтобы он жил постоянно и поднимался после перезагрузки, его оформляют службой systemd или запускают в контейнере.

Чем деплой Python отличается от Node.js и PHP? Только шагом установки зависимостей: виртуальное окружение и pip, npm по локфайлу или composer. Доставка кода, секреты, служба и проксирование одинаковы.

Где хранить пароли и ключи? В файле с переменными окружения рядом с проектом, с правами только для владельца и исключением из репозитория. В коде секретов быть не должно.

Нужен ли Docker для деплоя? Не обязательно. Для одного приложения хватает systemd. Контейнеры выигрывают, когда сервисов несколько или важна одинаковость окружения на разных машинах.

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

Какой сервер нужен под небольшое приложение? Обычно хватает младшего тарифа: 1-2 ГБ памяти достаточно для типового API или бота. Как считать ресурсы, разобрано в статье про конфигурацию сервера.


Нужен сервер под ваше приложение? VPS на NVMe с root-доступом - ставите любую версию Python, Node.js или PHP, площадки в Москве, Казани и Амстердаме. Активация за 3 минуты, первые 7 дней - тестовый период с возвратом средств.