Пишем web на Python3, разворачиваем приложение на сервер

newbie

Опубликован:  2019-04-19T08:25:25.577288Z
Отредактирован:  2019-04-19T08:23:41.494324Z
Продолжаем работу над проектом selfish. В этом выпуске я займусь отработкой первого тестового развёртывания web-приложения на сервер. На текущем этапе стоит две цели: 1) проанализировать и отработать ручной процесс развёртывания приложения; 2) подтвердить текущие функциональные возможности приложения и функцию отсылки электронной почты с реального адреса в штатом режиме production.

1. В предыдущих сериях

Selfish - web-приложение на базе Python3 и Flask, начальная конфигурация и разработка которого детально описаны в предыдущих выпусках этого блога. Все посвященные разработке этого приложения выпуски можно отфильтровать по метке selfish.

Эта демонстрация последняя из запланированных и завершает цикл статей, посвящённый разработке selfish.

2. Подробности о целях этого перформанса

В предыдущем выпуске блога наше web-приложение обзавелось очередью задач, которая является, на мой взгляд, достаточно капризной вещью, а её конфигурация и встраивание в приложение Flask слегка запутаны и неоднозначны. Отсюда следует необходимость подтвердить работоспособность текущей конфигурации приложения в целом в режиме production на сервере, дабы избежать неприятных сюрпризов в будущем.

Кроме этого, в наборе функциональных возможностей selfish появилась функция рассылки электронной почтой регистрационных писем по заявкам желающих зарегистрироваться пользователей. По вполне обоснованным причинам в режиме отладки и тестирования отсылка писем подавлена, а вместо этого текст отсылаемого письма транслируется в выхлоп консоли отладочного сервера. Отсюда следует необходимость подтвердить работоспособность этой функции приложения и протестировать отсылку регистрационного письма с реального адреса электронной почты в составе автономного сервера.

Развёртывание боевого сервера в сети Интернет - процесс достаточно сложный и предполагает множество тонких деталей, более того, в перспективе этот процесс будет необходимо автоматизировать. Это ещё одна веская причина для первого тестового развёртывания приложения на сервер, которое позволит проанализировать весь процесс, отработать все его детали и увидеть работу приложения на сервере. Вполне возможно, что по результатам этого перформанса придётся внести какие-то изменения и доработать код приложения.

3. Установка и настройка базового сервера

Для осуществления задуманного мне понадобится базовый сервер. У меня есть два возможных варианта:

  1. Заказать у какого-нибудь хостинг провайдера VDS-сервер;
  2. Использовать программу VirtualBox или другую подобную и развернуть виртуальный сервер прямо на своём лэптопе.

Первый вариант на текущем этапе разработки не оправдан и предполагает некоторые специфические сложности, которых мне пока хотелось бы избежать, поэтому на данный момент от него лучше отказаться. А вот второй вариант сейчас будет весьма оправданным выбором и позволит подготовиться к первому варианту, потому что в будущем, рано или поздно, первым вариантом придётся воспользоваться.

Установка VirtualBox, подготовка виртуальной машины, установка на неё операционной системы, настройка сети сервера и настройка взаимодействия клиента с сервером детально описаны в соседнем блоге. Опираясь на предложенные инструкции я подготовил виртуальную машину и установил на неё Debian buster. Установочный образ netinst доступен для загрузки на официальном сайте Debian.

6Mbsp2TDv4.png

Я выбрал Debian buster, так как работу над проектом осуществлял на десктопе под управлением этой операционной системы, будет разумно для сервера использовать ту же самую пакетную базу. Итак мой сервер включен и слушает сеть на IP-адресе 192.168.56.102.

RTzQ3V0EX5.png

Отправляю окно сервера на второй рабочий стол своего десктопа, чтобы не мешало.

Чтобы со своего десктопа обращаться к своему виртуальному серверу по доменному имени, мне необходимо IP-адрес сервера связать с желаемым доменным именем. В рамках этой демонстрации я буду использовать домен auriz.net и затеню его IP-адресом своего виртуального сервера в файле /etc/hosts операционной системы своего десктопа.

dfK9YIgNHA.png

В результате такой настройки по доменному имени будет откликаться мой виртуальный сервер.

INyPjufr4E.png

Поскольку по инструкции debianeach я предварительно настроил взаимодействие между десктопом и сервером по протоколу SSH, теперь я могу из консоли десктопа подключиться к серверу по настроенному только что доменному имени.

kvw3NtA9lS.png

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

4. Установка на сервер дополнительного программного обеспечения

Чтобы развернуть selfish на виртуальном сервере, необходимо установить следующие дополнительные пакеты:

  • postgresql;
  • nginx;
  • git;
  • redis-server;
  • libpq-dev;
  • python3-dev;
  • python3-venv;
  • gcc.

Все перечисленные пакеты есть в официальном репозитории Debian и их можно установить одной командой.

sudo apt install postgresql nginx git redis-server libpq-dev python3-dev python3-venv gcc

gPRQNwDZiC.png

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

cXjgPBN0Hl.png

Соглашаюсь, ввожу Y и дожидаюсь завершения процесса установки.

5. Настройка PostgreSQL

Приложению необходима база данных, чтобы её создать мне понадобится пользователь базы данных. Вхожу на сервере пользователем postgres.

sudo -i -u postgres

ps0KSEIy9h.png

Следует обратить внимание, что приглашение командной строки терминала изменило свой вид. Создаю пользователя.

createuser newbie -P -d --interactive

1ovaGrzApT.png

Пользователь postgres завершил необходимые действия, можно выйти.

exit

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

createdb selfish

nfS38ylvxN.png

Чуть позже я подключу эту базу данных в свой проект. В настоящий момент я делаю тестовый деплоймент на виртуальную машину, и пока тонкая настройка PostgreSQL не требуется, оставляю службу в текущем состоянии.

6. Восстановление проекта на сервере

Настало время получить с git-сервера код приложения и воссоздать конфигурацию проекта. Создаю на сервере в домашнем каталоге пользователя newbie новый каталог с именем workspace и вхожу в него.

mkdir workspace
cd workspace

Клонирую git-репозиторий selfish.

git clone https://gitlab.com/newbie_/selfish.git

Вхожу в базовый каталог selfish.

cd selfish/

Xkxo59c9zz.png

Все последующие действия я буду совершать с учётом, что нахожусь в этом базовом каталоге проекта. Теперь мне нужно виртуальное окружение, создаю его и активирую.

python3 -m venv ../venv
source ../venv/bin/activate

IbU1gcD5Lv.png

Обновляю wheel и восстанавливаю состав виртуального окружения в заданной последовательности.

pip install --upgrade wheel
pip install -r requirements-first.txt
pip install -r requirements.txt

M0ghFYq0rI.png

jEf4ZAuU0c.png

Устанавливаю unzip.

sudo apt install unzip

Восстанавливаю из архива клиентские сторонние библиотеки.

unzip -d selfish/static/ deployment/static-vendor.zip

AJiXnxl9Tf.png

На сервере приложение будет развёрнуто в режиме production, а это значит, что статические файлы (стили и сценарии) подвергнутся минимизации и будут транслироваться сервером из каталога generic. Создаю в каталоге generic ссылку на каталог шрифтов.

mkdir selfish/static/generic
ln -s -T /home/newbie/workspace/selfish/selfish/static/vendor/bootstrap-3.4.0/fonts/ /home/newbie/workspace/selfish/selfish/static/generic/fonts

KIMBKZJhH7.png

Восстанавливаю файл конфигурации config.py.

cp config.template.py config.py

Открываю файл config.py в текстовом редакторе nano и привожу его код к следующему виду.

import os

from datetime import timedelta


class Config:
    SECRET_KEY = 'NPKEPTaVFEOSF*xC5*ee)iX5BT**g6oiru6H9RGBE%vdfg!1of'
    SITE_NAME = 'Selfish'
    TOKEN_LENGTH = 12
    REQUEST_INTERVAL = 24
    SQLALCHEMY_COMMIT_ON_TEARDOWN = True
    SQLALCHEMY_TRACK_MODIFICATIONS = True
    PERMANENT_SESSION_LIFETIME = timedelta(hours=2)
    CELERY_RESULT_BACKEND = 'redis://localhost:6379/0'
    CELERY_BROCKER_URL = 'redis://localhost:6379/0'
    CELERY_IMPORTS = ('selfish.tasks',)
    CELERY_TASK_RESULT_EXPIRES = timedelta(hours=1)
    MAIL_SERVER = 'smtp.yandex.ru'
    MAIL_PORT = 465
    MAIL_USE_SSL = True
    MAIL_USERNAME = 'noreply@auriz.ru'
    MAIL_PASSWORD = '##########'
    SELFISH_SUBJECT_PREFIX = '[Selfish] '
    SELFISH_SENDER = 'robot <noreply@auriz.ru>'

    @staticmethod
    def init_app(app):
        pass


class Development(Config):
    DEBUG = True
    SEND_FILE_MAX_AGE_DEFAULT = 0
    ASSETS_DEBUG = True
    TOKEN_LENGTH = 0.1
    REQUEST_INTERVAL = 0.12
    SESSION_PROTECTION = None
    MAIL_DEBUG = True
    MAIL_SUPPRESS_SEND = True
    SQLALCHEMY_DATABASE_URI = \
        'postgresql+psycopg2://{0}:{1}@192.168.56.101/selfish_d'.format(
            os.getenv('SDBU', 'newbie'),
            os.getenv('SDBP', 'aa'))


class Testing(Config):
    TESTING = True
    SQLALCHEMY_DATABASE_URI = \
        'postgresql+psycopg2://{0}:{1}@192.168.56.101/selfish_t'.format(
            os.getenv('SDBU', 'newbie'),
            os.getenv('SDBP', 'aa'))


class Production(Config):
    SEND_FILE_MAX_AGE_DEFAULT = int(timedelta(days=28).total_seconds())
    USERS_PER_PAGE = 30
    SQLALCHEMY_DATABASE_URI = \
        'postgresql+psycopg2://{0}:{1}@localhost/selfish'.format(
            os.getenv('SDBU', 'newbie'),
            os.getenv('SDBP', 'aa'))


config = {'development': Development,
          'testing': Testing,
          'production': Production,
          'default': Production}

Особое внимание следует обратить на настройки электронной почты в классе Config, на класс Production и адрес базы данных в нём, а так же на словарь config и его значение в ключе default. Для отсылки почты я буду использовать Яндекс-почту - все настройки соответствуют базовым параметрам этого оператора.

Теперь с помощью менеджера проекта можно попытаться сделать апгрейд базы данных.

python manage.py db upgrade

nbry21RYt8.png

Успех. Приложение получило базу данных.

Попытаюсь зарегистрировать в базе данных суперпользователя.

python manage.py create_root

enpBI56SY1.png

Успех. Проект восстановлен, мне не хватает одной единственной ссылки, создаю её.

ln -s -T /home/newbie/workspace/selfish/selfish/static/ /home/newbie/workspace/static

Приложение selfish готово к запуску. Для запуска приложения на сервере придётся прибегнуть к особой магии. Об этом далее...

7. Автоматическая загрузка web-приложения

Пришло время озадачиться запуском selfish на сервере, и эта задача делится на две части:

  1. Для запуска приложения Flask потребуется wsgi-сервер, желательно асинхронный;
  2. Запуск приложения на сервере должен производиться автоматически при старте и загрузке самого сервера.

В режиме отладки и тестирования я запускал selfish с помощью специального отладочного сервера. Для режима production этот сервер не годится. Вариантов можно придумать несколько, но на этом этапе проще всего завести проект с помощью Gunicorn. Чтобы Gunicorn был асинхронным, ему необходима асинхронная библиотека, я буду использовать gevent. Устанавливаю оба пакета в виртуальное окружение selfish.

pip install gunicorn gevent

8wmmwMtkww.png

Кроме этого, для правильной работы приложения необходим рабочий процесс очереди задач Celery, и его тоже необходимо запускать автоматически при старте сервера. Оба процесса (celery и gunicorn) будут иметь временные файлы, поэтому для временных файлов этих процессов нужны отдельные каталоги в системных каталогах сервера. Создать такие каталоги можно при помощи одного конфигурационного файла. Создаю его.

sudo nano /etc/tmpfiles.d/workspace.conf

AMOOM9d162.png

Сохраняю файл (ctrl+o) и покидаю текстовый редактор (ctrl+x).

Для автоматического запуска рабочего процесса очереди задач создаю новый unit для systemd операционной системы сервера.

sudo nano /etc/systemd/system/selfish-celery.service

aKBp3ROA02.png

Здесь следует обратить внимание на параметр ExecStart секции Service, в этом параметре для запуска рабочего процесса я использовал временные каталоги, которые должны создаться автоматически в соответствии с конфигурационным файлом в каталоге tmpfiles.d. Сохраняю файл и покидаю текстовый редактор.

Второй unit я создам для автоматического запуска gunicorn.

sudo nano /etc/systemd/system/selfish-gunicorn.service

1Kkn8LMZeO.png

Здесь опять следует обратить внимание на параметр ExecStart, а именно на аргумент командной строки --bind unix:/run/selfish/selfish.socket - этот сокет будет храниться в каталоге с временными файлами, созданном конфигурационным файлом в tmpfiles.d, и этот сокет чуть позже будет использован при настройке nginx. Сохраняю файл и покидаю текстовый редактор.

Оба созданных юнита необходимо добавить в автозагрузку.

sudo systemctl enable selfish-celery.service
sudo systemctl enable selfish-gunicorn.service

MsGUi8vV8p.png

Так как запущенные процессы автоматически создаю логи, нужно бы предусмотреть ещё и ротацию логов, но на текущем этапе этим можно пренебречь.

8. Настройка Nginx

Последний штрих - конфигурация Nginx... Создаю новый файл конфигурации.

sudo nano /etc/nginx/sites-available/autiz.net

5QpS3IE9YJ.png

Сохраняю файл и покидаю текстовый редактор. Создаю системную ссылку для этого файла конфигурации.

sudo ln -s -T /etc/nginx/sites-available/autiz.net /etc/nginx/sites-enabled/auriz.net

Перезагружаю сервер.

sudo systemctl reboot

TKB5BUTZQo.png

Так как сервер ушёл в перезагрузку, меня автоматически выкинуло из SSH-сессии и я опять оказался в терминале десктопа. Дожидаюсь полной загрузки сервера, запускаю на десктопе браузер и вбиваю в адресную строку доменное имя auriz.net. Момент истины...

KBSNQd1IFb.png

Welcome home, dear!

9. Тестирование в браузере

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

Поскольку у selfish на текущем этапе нет автоматизированных тестов, стоит протестировать функционал приложения в ручном режиме в браузере. Необходимо уделить внимание статическим файлам и их минимизации, проверить страницу входа, войти администратором и создать пару тройку новых пользователей, поиграть с их разрешениями на страницах профилей. Обязательно нужно попробовать зарегистрировать аккаунт самостоятельно от имени анонимного пользователя.

Я не буду демонстрировать все объекты тестирования, кроме одного. Продемонстрирую самостоятельную регистрация аккаунта анонимным пользователем, комментарии не требуются...

qrXCBVHRMq.png

KvhwA8pL3f.png

Wfocpxkcsk.png

XqML4rZQFa.png

7urnWd9yeY.png

ov51dPCDyM.png

HYxgKODLWD.png

UzVEiYrJCI.png

GAU95rLJdo.png

Комментарии: