Vikky avatar

Виктор Шостак
   

Разворачиваем Flask приложение на обычном шаред хостинге


Небольшой дисклеймер: если перед вами действительно стоит задача «развернуть %любой из веб-фреймворков на Python% в продакшен на обычном (шаред) хостинге» — значит у вас что-то пошло не так.

Как временная мера при разработке — why not?! Но как основная площадка для работы приложения — это не самая лучшая идея. Почему? Всё просто.

  • Если не сразу, то при развитии проекта — вам всё равно потребуется тонкая настройка сервера и окружения.
  • По-умолчанию, хостеры оставляют версию SQL и интерпретатора Python, которые идут со стабильными сборками ОС. Обычно это какой-нибудь MySQL 5.1.x и Python 2.7.6. И да, это ещё не самый плохой вариант! Я видел и более консервативных хостеров.
  • (исходя из вышесказанного) Вы сами себя загоняете в рамки устаревшего стека технологий, вместо того, чтобы получать удовольствие от разработки :D

И самое главное — в 2017 году не найти дешёвый облачный VDS по цене шареда? Серьёзно?! О_о

Начальная структура проекта

└─ my-project.com
     └── public_html
             ├─ static           <-- папка для статики
             ├─ templates        <-- папка шаблонов
             └─ application.py   <-- Flask приложение

Подготовка виртуального окружения

Прежде всего, спросите у тех. поддержки вашего хостинга: открыт ли для вашего тарифа доступ к аккаунту по SSH? Обычно — да, но лучше уточнить заранее.

Загружаем файлы на хостинг в папку проекта и копируем её полный путь. В качестве примера, я буду использовать /home/u/user/my-project.com/public_html/.

Далее, подключаемся по SSH, переходим в эту папку, скачиваем и устанавливаем virtualenv (виртуальное окружение):

Так как на большинстве шаред хостингов установлен Python 3.4, то буду использовать его для демонстрации.

$ cd /home/u/user/my-project.com/public_html/
$ curl -O https://pypi.python.org/packages/source/v/virtualenv/virtualenv-<версия>.tar.gz
$ tar xvfz virtualenv-<версия>.tar.gz
$ python3 virtualenv-<версия>/virtualenv.py venv

По большому счёту, установщик virtualenv — больше нам не пригодится. Поэтому можете смело удалить его папку:

$ rm -rf virtualenv-<версия>

Вот и всё. Наше виртуальное окружение готово к использованию. Установим все необходимые пакеты и зависимости через pip install и мы можем продолжать!

$ source venv/bin/activate
(venv) $ pip install flask ...
(venv) $ deactivate

Готовим Apache для нашего приложения

Хорошо. Теперь, создадим файл deploy.cgi, который будет использоваться веб-сервером для запуска Flask:

#!/home/u/user/my-project.com/public_html/venv/bin/python3.4
# -*- coding: utf-8 -*- 

from wsgiref.handlers import CGIHandler
from application import app

CGIHandler().run(app)

И последний штрих. Файл .htaccess со следующим содержанием:

Options +ExecCGI
AddHandler cgi-script .cgi .py
DirectoryIndex deploy.cgi

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f 
RewriteRule ^(.*)$ deploy.cgi/$1 [QSA,L]

Profit! Ваше Flask приложение работает на шаред хостинге без регистрации и смс.

Финальная структура проекта

└─ my-project.com
     └── public_html
             ├─ static           <-- папка для статики
             ├─ templates        <-- папка шаблонов
             ├─ venv             <-- папка виртуального окружения
             ├─ .htaccess
             ├─ application.py   <-- Flask приложение
             └─ deploy.cgi       <-- CGI скрипт для запуска приложения

Подводный камень с url_for()

Если вы планируете делать ссылки на роуты в шаблонах по фен-шую, через url_for(), то готовьтесь к неожиданному прибавлению в URL имени CGI скрипта.

То есть, при переходе по ссылке, сгенерированной через url_for('about'), вы попадаете не на my-project.com/about, а на my-project.com/deploy.cgi/about. При этом, страница корректно открывается что по одному адресу, что по другому!

Данный баг можно исправить фиксом CGIRootFix из пакета Werkzeug, но у меня он так и не захотел работать на Flask версии 0.12.1 — вылезали 404 ошибки на всех урлах.

Поэтому в шаблонах — я использую вот такую конструкцию:

<a href="{{ url_for('about') | replace('/deploy.cgi/', '/') }}">Link</a>

Надеюсь, что данная статья сэкономит кому-нибудь уйму времени. Если есть, что дополнить, то милости прошу в комментарии ;)