Продвинутым
October 14, 2021

Деплой Python программ на Linux

1package & Let's Node

⠀Для беспрерывной работы Python программы необходимо задеплоить её на сервер. На Linux это можно сделать двумя способами, описанными в данной статье.


Содержание


Windows или Linux?

К содержанию

⠀У обоих систем есть как плюсы, так и минусы.

Windows

⠀Плюсы:

  • Для большинства привычная система;
  • Программы могут работать с графическим интерфейсом (приложения с GUI, кликер, работа с браузером и т. п.);
  • Возможность запуска программ без установки Python (при помощи EXE файла, созданного библиотекой на подобии PyInstaller).

⠀Минусы

  • Высокая стоимость аренды сервера;
  • Низкое соотношение цена-производительность;
  • Скудный выбор хостингов с адекватными ценами;
  • Нестабильность (зависит от хостинга);
  • Через профиль одного пользователя можно заходить одновременно только с одного устройства.

Linux

⠀Плюсы:

  • Возможность запуска программ без установки Python (в Docker контейнере).
  • Низкая стоимость аренды сервера;
  • Высокое соотношение цена-производительность;
  • Большой выбор хостингов с адекватными ценами;
  • Более стабильная работа (зависит от хостинга);
  • Через профиль одного пользователя можно заходить одновременно с нескольких устройств.

⠀Минусы

  • Для многих непривычная система.

Способы деплоя

К содержанию

⠀В обоих системах существует по 2 основных способа.

⠀В Windows:

  • Скомпилировать исполняющий EXE файл, перенести его на сервер и запускать программу (лёгкий, быстрый и предпочтительный);
  • Установить Python, перенести проект на сервер, установить все зависимости и запустить исполняющий файл (более сложный, медленный и не наполненный смыслом).

⠀В Linux:

  • Создать Docker образ с программой и запустить в качестве контейнера (более сложный, быстрый и предпочтительный);
  • Установить Python и запустить при помощи сервисного файла (лёгкий, долгий, но не предпочтительный).

⠀Способы деплоя на Linux подробно расписаны в следующем разделе.


Деплой

К содержанию

Все названия из раздела менять на свои

Подготовка

⠀У себя на ПК в виртуальном окружении проекта Python создать файл с используемыми библиотеками

pip freeze > requirements.txt

⠀Подключиться к серверу.

⠀При необходимости создать папку, в которую будут помещёны все программы, например

mkdir python_programs; cd python_programs

⠀Создать папку для запускаемой программы и перейти в неё

mkdir program_name; cd program_name

⠀Загрузить необходимые файлы из проекта на сервер в созданную ранее папку с виртуальным окружением.

В MobaXterm это можно сделать двумя способами:

  1. ПКМ в браузере файлов слева — «Upload to current folder» — выбрать, что загружать;
  2. Перетащить файлы из папки на своём ПК в браузер файлов слева (также, как в Windows перетаскивать курсором из папки в папку).

Docker

⠀Прочитать статью про создание Docker образа.

⠀Установить Docker

. <(wget -qO- https://raw.githubusercontent.com/SecorD0/utils/main/installers/docker.sh)

⠀Создать у себя на ПК Dockerfile и прописать в нём инструкции создания образа, например

FROM python:3.9.7-slim

WORKDIR /program

COPY . .
RUN pip install --upgrade pip; \
    pip install -r requirements.txt

ENTRYPOINT ["python3"]

CMD ["main.py"]

⠀Образ python:3.9.7-slim имеет минимальный размер, около 140 Мб. Есть вероятность, что придётся доустанавливать что-то недостающее или указать другой образ из списка.

⠀Если программа взаимодействует c БД по типу MySQL, то необходимо установить её в соседний контейнер или на сервер. Информацию о том, как это сделать с Docker, необходимо найти в интернете. Статьи про MySQL на английском: первая, вторая.

⠀Загрузить созданный Dockerfile в папку с программой на сервере.

⠀Перейти в папку с программой

cd $HOME/python_programs/program_name

⠀Собрать образ

docker build -t program_name .

⠀Запустить программу

docker run -dit --restart always \
  -v $HOME/python_programs/program_name/:/program \
  --name program_name program_name

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

⠀Удостовериться в корректном запуске программы, просмотрев лог

docker logs program_name -fn 100

⠀⠀Добавить команду для просмотра лога программы в систему в виде переменной

. <(wget -qO - https://raw.githubusercontent.com/SecorD0/utils/main/miscellaneous/insert_variable.sh) \
-n "program_name_log" -v "docker logs program_name -fn 100" -a

  • program_name — название программы

⠀При обновлении исходного кода программы необходимо загрузить обновлённые файлы в папку с программой на сервере и перезапустить контейнер командой

docker restart program_name

Сервисный файл

⠀Прочитать статью про параметры сервисного файла.

⠀Обновить пакеты и систему

sudo apt update && sudo apt upgrade -y 

⠀Установить необходимые пакеты

sudo apt install wget -y

⠀Запустить скрипт установки Python 3.9.7

. <(wget -qO - https://raw.githubusercontent.com/SecorD0/utils/main/installers/python.sh)

⠀При желании можно поставить другую версию (все версии), указав её через опцию -v, например

. <(wget -qO - https://raw.githubusercontent.com/SecorD0/utils/main/installers/python.sh) \
-v "3.9.1"

⠀Дождаться окончания установки (10-20 минут)

⠀Python будет установлен в папку

/usr/local/python

⠀Обновить pip

python3 -m pip install --upgrade pip

⠀Установить виртуальное окружение

pip install virtualenv

⠀Добавить команду активации виртуального окружения в систему в виде переменной

. <(wget -qO - https://raw.githubusercontent.com/SecorD0/utils/main/miscellaneous/insert_variable.sh) -n "venv_py" -v ". venv/bin/activate" -a

⠀Перейти в папку с программой

cd $HOME/python_programs/program_name

⠀Создать виртуальное окружение

virtualenv venv

⠀Активировать виртуальное окружение

venv_py

⠀Установить необходимые зависимости

pip install -r requirements.txt

⠀Запустить программу и удостовериться, что она работает корректно

python3 main.py

⠀Остановить программу сочетанием клавиш Ctrl+C, если она циклична, и деактивировать окружение командой

deactivate

⠀В блокноте подставить в команду свои значения и запустить её из директории с программой, тем самым создать сервисный файл

printf "[Unit]
Description=Program Name
After=network-online.target

[Service]
User=$USER
EnvironmentFile=/etc/environment
Environment=PYTHONUNBUFFERED=1
WorkingDirectory=`pwd`
ExecStart=`pwd`/venv/bin/python3 main.py
Restart=on-failure
RestartSec=3

[Install]
WantedBy=multi-user.target" > /etc/systemd/system/program_name.service
  • Description — название программы;
  • Environment=PYTHONUNBUFFERED=1 — для вывода print(…) в лог программы;
  • WorkingDirectory — автоматически подставляется путь до папки с программой;
  • ExecStart — необходимо подставить название исполняемого файла, например main.py, app.py;

⠀Если программа работает нециклично, то можно настроить её периодический запуск, для этого нужно:

  • Поставить значение Restart=always;
  • Задать периодичность запуска, например:
    • RestartSec=10;
    • RestartSec=5m;
    • RestartSec="1min 10s".

⠀Запустить сервисный файл

sudo systemctl daemon-reload
sudo systemctl enable program_name
sudo systemctl restart program_name

⠀Удостовериться в корректном запуске программы, просмотрев лог

journalctl -n 100 -f -u program_name

⠀Добавить команду для просмотра лога программы в систему в виде переменной

  • program_name — название программы.
. <(wget -qO - https://raw.githubusercontent.com/SecorD0/utils/main/miscellaneous/insert_variable.sh) \
-n "program_name_log" -v "journalctl -n 100 -f -u program_name" -a

⠀При обновлении исходного кода программы необходимо загрузить обновлённые файлы в папку с программой на сервере и перезапустить сервисный файл командой

sudo systemctl restart program_name

MySQL (бонус)

К содержанию

⠀Рассматривается работа на Linux сервере, не в Docker.

Установка

⠀Установить MySQL скриптом

. <(wget -qO- https://raw.githubusercontent.com/SecorD0/utils/main/installers/mysql.sh)

⠀Войти в оболочку MySQL

sudo mysql

⠀Задать пароль root пользователя

ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password by 'PASSWORD';
  • PASSWORD - пароль.

⠀Выйти из оболочки

exit

⠀Выполнить предварительную настройку, запустив команду

sudo mysql_secure_installation

⠀Ввести заданный ранее пароль и ответить на дальнейшие вопросы y или n:

  • Использовать ли плагин для валидации пароля? (n);
n
  • Сменить ль пароль root пользователя? (n);
n
  • Удалить ли анонимных пользователей? (желательно y)
y
  • Запретить ли удалённое подключение через root пользователя? (по желанию)
n
  • Удалить ли тестовую базу данных? (желательно y)
y
  • Обновить ли таблицы привилегий? (желательно y)

Настройка

⠀Для подключения к MySQL можно использовать два вида пользователей:

1) non-root (рекомендуется)

⠀Для создания такого пользователя необходимо подключиться к MySQL

sudo mysql -u root

⠀Создать БД

CREATE DATABASE DATABASE_NAME;
  • DATABASE_NAME — название базы данных.

⠀Создать пользователя

CREATE USER 'YOUR_USERNAME'@'localhost' IDENTIFIED BY 'YOUR_PASSWORD';
  • YOUR_USERNAME — имя пользователя;
  • YOUR_PASSWORD — пароль.

⠀Дать пользователю все права в созданной БД

GRANT ALL PRIVILEGES ON DATABASE_NAME.* TO 'YOUR_USERNAME'@'localhost';
FLUSH PRIVILEGES;
  • DATABASE_NAME — название базы данных;
  • YOUR_USERNAME — имя пользователя.

2) root (не рекомендуется)

⠀При попытке подключения к MySQL через root пользователя появится ошибка

1698 (28000): Access denied for user 'root'@'localhost'

⠀Чтобы её исправить, необходимо подключиться к MySQL

sudo mysql -u root

⠀Изменить тип подключения root пользователя

USE mysql;
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 
'YOUR_PASSWORD';
FLUSH PRIVILEGES;
  • YOUR_PASSWORD — указанный при предварительной настройке пароль.

Удаление

Все БД со всеми таблицами будут удалены.

⠀Поэтому во избежании потери данных необходимо экспортировать все БД в файлы командой

sudo mysqldump -u root -p DATABASE_NAME > DATABASE_FILE_NAME.sql
  • DATABASE_NAME — название базы данных;
  • DATABASE_FILE_NAME — название файла с БД.

⠀MySQL полностью удаляется скриптом

. <(wget -qO- https://raw.githubusercontent.com/SecorD0/utils/main/installers/mysql.sh) -u


Полезные команды

К содержанию

Docker

⠀Лог программы

program_name_log
docker logs program_name -fn 100

⠀Список образов

docker images

⠀Список всех контейнеров

docker ps -a

⠀Удаление контейнера с программой

Удалит все файлы внутри контейнера

docker stop program_name
docker rm program_name

⠀Перезапуск программы (например при обновлении кода)

docker restart program_name

Сервисный файл

  • program_name — название сервисного файла программы

⠀Лог программы

program_name_log
journalctl -n 100 -f -u program_name

⠀Остановка программы

sudo systemctl stop program_name

⠀Перезапуск программы (например при обновлении кода)

sudo systemctl restart program_name

MySQL

  • -u root — имя пользователя;
  • DATABASE_NAME — название БД;
  • DATABASE_FILE_NAME — название файла с БД.

⠀Если был изменён тип подключения root пользователя, то в каждую команду необходимо добавлять опцию -p и вводить пароль после её исполнения

⠀Подключение к MySQL

sudo mysql -u root
sudo mysql -u root DATABASE_NAME

⠀Отключение от MySQL

Ctrl + D
exit

⠀Экспорт БД

sudo mysqldump -u root -p DATABASE_NAME > DATABASE_FILE_NAME.sql

⠀Импорт в существующую БД

sudo mysql -u root -p DATABASE_NAME < DATABASE_FILE_NAME.sql
  • DATABASE_NAME — название базы данных;
  • DATABASE_FILE_NAME — название файла с БД.

⠀SQL команды (выполнять после подключения к MySQL)

/* список пользователей */
SELECT user, host FROM mysql.user;
/* показать список БД */
SHOW DATABASES;
/* создать БД */
CREATE DATABASE DATABASE_NAME;
/* перейти в БД */
USE DATABASE_NAME;
/* показать список таблиц */
SHOW TABLES;
/* показать формат столбцов таблицы */
DESCRIBE TABLE_NAME;
/* показать содержимое таблицы */
SELECT * FROM TABLE_NAME;

Полезные ссылки

К содержанию

Контейнеризация Python приложения (EN) | Python и MySQL (EN)

Подключение Python приложения к MySQL в Docker (EN)


Благодарности

К содержанию

Команда 1package — написание статьи

Выразить благодарность