[Из песочницы] Развёртывание Django 1.9 на IIS 7+
Предисловие
Всем привет!
Не так давно ко мне обратились с просьбой о создании сайта. Интересный крупный проект с множеством »хочу вот это». Среди пожеланий были два главных, определивших web framework для написания, это интернационализация и панель администратора. Как уже понятно из заголовка статьи, таким framework’ом стала Django.
На старте, и почти всё время разработки, у нас не было одной вещи — сервера. Было доменное имя, бодрым темпом разрабатывался сайт, к проекту присоединился дизайнер, но сервер нам так выделить не могли. Все показы сайта проходили на моём ноутбуке, не давая возможности заказчику сесть вечером с кружкой кофе, расслабиться и насладиться тем, что мы уже для него сделали. А также, отсутствие возможности показать нашу работу людям лишало нас получения обратной связи.
И вот, можно сказать на днях, свершилось чудо — у нас появился сервер. И как следствие этого — эта статья.
Проблема
Радость моя была не долгой. Сайт на Django, а сервер на Windows Server 2012. Первая мысль:
python manage.py runserver 80
Открываю сайт в браузер на сервере — всё работает. Отлично. Открываю сайт на домашней машине и вижу, что сайт не доступен. Грусть.
Следующая идея, это использовать IIS. Но как?»Ok, Google» выдал мне кучу статей на разных языках с кучей сложностей. Так же я не обошел вниманием cтатью с Хабра от Microsoft и с официального сайта Djnago, но все эти решения были довольно громоздкие, а мне хотелось некой простоты и элегантности.
Что мы имеем
- Python 3.5
- Django 1.9.7
- Windows Server 2012 R2
Структура сайта (часть дерева):
project/
manage.py
project/
__init__.py
settings.py
urls.py
wsgi.py
Приступим к решению проблемы развёртывания Django на IIS.
Решение проблемы
Python
Тут всё просто, устанавливаем wfastcgi и готово.
pip install wfastcgi
Я бы предложил установить в глобальное окружение, дабы один раз настроить IIS для всех Python framewok’ов.
IIS
Теперь настраиваем IIS. В Windows Server этот компонент уже установлен по умолчанию, но не в полном объёме. Я имею в ввиду важный для нашего сайта компонент — CGI.
Компоненты Windows → Службы IIS → Службы Интернета → Компоненты разработки приложений → CGI.
Устанавливаем его. Теперь в Диспетчере IIS у нашего сервера появился пункт Настройки FastCGI.
Далее заходим в него. Возможно, если у вас уже был установлен CGI до того как вы установите wfastcgi, у вас уже будет такая строка.
В противном случае нажимаем на Добавить Приложение… и заполняем поля:
- Полный путь — С:\Python\python.exe
- Аргументы — C:\Python\Lib\site-packages\wfastcgi.py
Всё OK. Основная часть сделана. Далее создаём новый сайт. И у сайта выбираем Сопоставления обработчиков.
Сопоставления обработчиков → Добавление сопоставления модуля
- Путь запроса — *
- Модуль — FastCgiModule
- Исполняемый файл — C:\Python\python.exe|C:\Python\Lib\site-packages\wfastcgi.py
- Имя — Python FastCGI
- Ограничения запроса — нажать и убрать галочку
Далее ОК. IIS настроен.
Django App
Копируем проект в папку сайта. Создаём в ней виртуальное окружение. В этой же папке создаём файл web.config (или открываем для редактирования, если он уже создан).
А также создаём ptvs_virtualenv_proxy.py и вставляем в него код ниже
# ############################################################################
#
# Copyright (c) Microsoft Corporation.
#
# This source code is subject to terms and conditions of the Apache License, Version 2.0. A
# copy of the license can be found in the License.html file at the root of this distribution. If
# you cannot locate the Apache License, Version 2.0, please send an email to
# vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
# by the terms of the Apache License, Version 2.0.
#
# You must not remove this notice, or any other, from this software.
#
# ###########################################################################
import datetime
import os
import sys
if sys.version_info[0] == 3:
def to_str(value):
return value.decode(sys.getfilesystemencoding())
def execfile(path, global_dict):
"""Execute a file"""
with open(path, 'r') as f:
code = f.read()
code = code.replace('\r\n', '\n') + '\n'
exec(code, global_dict)
else:
def to_str(value):
return value.encode(sys.getfilesystemencoding())
def log(txt):
"""Logs fatal errors to a log file if WSGI_LOG env var is defined"""
log_file = os.environ.get('WSGI_LOG')
if log_file:
f = open(log_file, 'a+')
try:
f.write('%s: %s' % (datetime.datetime.now(), txt))
finally:
f.close()
ptvsd_secret = os.getenv('WSGI_PTVSD_SECRET')
if ptvsd_secret:
log('Enabling ptvsd ...\n')
try:
import ptvsd
try:
ptvsd.enable_attach(ptvsd_secret)
log('ptvsd enabled.\n')
except:
log('ptvsd.enable_attach failed\n')
except ImportError:
log('error importing ptvsd.\n')
def get_wsgi_handler(handler_name):
if not handler_name:
raise Exception('WSGI_HANDLER env var must be set')
if not isinstance(handler_name, str):
handler_name = to_str(handler_name)
module_name, _, callable_name = handler_name.rpartition('.')
should_call = callable_name.endswith('()')
callable_name = callable_name[:-2] if should_call else callable_name
name_list = [(callable_name, should_call)]
handler = None
while module_name:
try:
handler = __import__(module_name, fromlist=[name_list[0][0]])
for name, should_call in name_list:
handler = getattr(handler, name)
if should_call:
handler = handler()
break
except ImportError:
module_name, _, callable_name = module_name.rpartition('.')
should_call = callable_name.endswith('()')
callable_name = callable_name[:-2] if should_call else callable_name
name_list.insert(0, (callable_name, should_call))
handler = None
if handler is None:
raise ValueError('"%s" could not be imported' % handler_name)
return handler
activate_this = os.getenv('WSGI_ALT_VIRTUALENV_ACTIVATE_THIS')
if not activate_this:
raise Exception('WSGI_ALT_VIRTUALENV_ACTIVATE_THIS is not set')
def get_virtualenv_handler():
log('Activating virtualenv with %s\n' % activate_this)
execfile(activate_this, dict(__file__=activate_this))
log('Getting handler %s\n' % os.getenv('WSGI_ALT_VIRTUALENV_HANDLER'))
handler = get_wsgi_handler(os.getenv('WSGI_ALT_VIRTUALENV_HANDLER'))
log('Got handler: %r\n' % handler)
return handler
def get_venv_handler():
log('Activating venv with executable at %s\n' % activate_this)
import site
sys.executable = activate_this
old_sys_path, sys.path = sys.path, []
site.main()
sys.path.insert(0, '')
for item in old_sys_path:
if item not in sys.path:
sys.path.append(item)
log('Getting handler %s\n' % os.getenv('WSGI_ALT_VIRTUALENV_HANDLER'))
handler = get_wsgi_handler(os.getenv('WSGI_ALT_VIRTUALENV_HANDLER'))
log('Got handler: %r\n' % handler)
return handler
Теперь открываем браузер, вводим адрес нашего сайта и он работает.
Ещё немного
Если вы не планируете использовать виртуальное окружение в своём проекте, то ptvs_virtualenv_proxy.py добавлять не надо, а web.config будет иметь следующий вид:
Так же, теперь на вашем IIS вы можете разворачивать проекты не только на Django, но и других Python framework’ах. Главное не забыть подредактировать web.config.
Послесловие
Теперь у нас есть рабочий сайт, небольшая группа людей, которая помогает нам с развитием и улучшением, за что им большое спасибо. А у меня появился некий опыт по дружбе Python проектов и IIS которым я поделился с вами. Надеюсь, что пригодится.
Комментарии (5)
13 июля 2016 в 12:09
0↑
↓
Вспомнил я свой деплой джанги на IIS несколько лет назад, это было ужасно. Да, там в итоге работало как-то, но потом всё время что-то вылезало, блочилось, текло, глючило понемногу. Но делал вроде не так, через какие-то полу-родные тулзы. Не знаю может сейчас лучше с этим. В итоге сейчас там на апаче+mod_wsgi всё это стоит, даже это лучше оказалось.13 июля 2016 в 12:10 (комментарий был изменён)
0↑
↓
Два вопроса которые выползли из головы от начала статьи:Все показы сайта проходили на моём ноутбуке, не давая возможности заказчику сесть вечером с кружкой кофе, расслабиться и насладиться тем, что мы уже для него сделали.
Почему не использовали heroku.com — dynos? Ведь для тестов и прототипов за глаза хватает бесплатного аккаунта. Ну или аналоги.Радость моя была не долгой. Сайт на Django, а сервер на Windows Server 2012.
Почему не обговорили с клиентом тип сервера (особенно после выбора языка и фреймворка), а пустили всё на самотёк? Ведь клиент мог с тем же успехом и шаред хостинг взять за 30р в месяц, с поддержкой только статики.13 июля 2016 в 12:20
0↑
↓
python manage.py runserver 80
Открываю сайт в браузер на сервере — всё работает. Отлично. Открываю сайт на домашней машине и вижу, что сайт не доступен. Грусть.
Порою ошибки позволяют развить вполне себе интересные направления, как, например, Ваша статья.python manage.py runserver 0.0.0.0:8000
Так было бы проще, но тогда Вы не освоили бы реализацию IIS+Django.13 июля 2016 в 12:34
0↑
↓
Спасибо, буду знать. Я перепробовал все комбинации, но не подумал о порте 8000.13 июля 2016 в 12:38
0↑
↓
Дело же не в порте, а в хосте.