在一个开发环境中运行不同版本的Python(wsgi)应用程序
首先
我的开发用Linux环境中通过操作系统的包管理命令安装了Python 2.7和Django 1.4。由于我正在开发的应用程序在这个环境中可以正常运行,所以没有问题。
有一次,我决定尝试将即将发布的Django 1.7与Python 3.4结合起来,以探索到底会遇到什么样的困难。
我們希望在相同的環境下進行先行技術的調查,同時不破壞現有的開發環境。
然而,这个”一个操作系统,多个Python和Django”的概念其实相当困难。上次我尝试的时候(参考链接:http://qiita.com/amedama/items/79994598d9f4daa69d13),由于Apache2的mod_wsgi无法同时运行多个版本的Python,所以进展一直停滞不前。
本次我们尝试作为替代方案,在同一主机上独立搭建一个wsgi服务器,并额外从Apache2设置反向代理来解决这个问题。
详细来说
-
- aptで入れたPython2.7 + django 1.4 環境
- pyvenv と pip を駆使した Python3.4 + django 1.7 環境
我們將探索在一個開發環境中同時並行啟動兩個環境的方法。
此外,我还遇到了与Django 1.7不兼容的实际改动,我也会稍稍介绍一下。
请任意选择:在文章的末尾,我还附上了一个7分钟的视频,展示了本文内容的一部分在实际终端上的实践过程。您可以选择阅读文章,观看视频,或是两者兼顾,随您喜好。
以下是重要角色:
-
- Python2.7 … 安定のPython2系
-
- Python3.4 … Python3期待の星
-
- Apache2 … フロントエンド。mod_wsgi経由でwsgiサーバにもなるが、mod_wsgi1つは1つのPython環境しか提供出来ないため、上に乗っかるwsgiアプリはPython2系のどれかかPython3系のどれか一種類でしか動かなくなる。Linux1環境にApacheバイナリを2つ入れるというのは若干狂気に見えたのでやってない。
-
- django 1.4 … 古い子
-
- django 1.7 … 生まれてないベータ
-
- xbuild … Python3.4のビルドを最初に行うときに使う
-
- virtualenv … Pythonの環境を作るもの
-
- pyvenv … Python3系で導入されたvirtualenvみたいなの
-
- tornado … 単体動作するwsgiサーバ
-
- supervisor … デーモンプロセスを束ねるデーモン。今回はwsgiサーバを束ねる
- /opt … 今回Djangoプロジェクト等はここに打ち込む
django应用程序的views.py文件
由于这次,views.py的内容本身并不重要,所以我们将继续使用以下内容:
返回Python版本和Django版本的纯文本。
from django.http import HttpResponse
import django
import sys
def home(request):
version = ('Python: {}\nDjango: {}\n'
.format(sys.version.replace('\n', ' '),
django.get_version()))
return HttpResponse(version, content_type="text/plain")
运行manage.py中的runserver命令
我安装了apt上的东西(Python2.7 + Django 1.4)。
假设已经事先准备好了tornado和django(较旧版本)等工具。
我认为无论是使用sudo pip还是apt-get(如果是Debian系统)命令都可以。
蛇足是这样的:如果使用pip安装的话,默认会安装1.6版本的Django,如果没有指定版本。根据本文的结果来看,在Debian wheezy (7.5)中使用apt-get install python-django是最接近目标的。根据我的了解,Ubuntu 12.04LTS安装的是1.3版本的Django,而Ubuntu 14.04LTS则安装的是1.6版本。换句话说,不同的环境有不同的情况。重要的是,Django 1.7并不会被安装。至于Python 3.4,在Ubuntu 14.04LTS中可以通过apt-get来安装。
-
- django-admin startproject test1
-
- settings.pyのsqlite3に関する設定をする。
db/db.sqlite3 をファイル保存先とし、www-dataから書き込めるようにする)
views.pyとurls.pyを変更
python mangae.py syncdb
python manage.py runserver
ブラウザで Python 2.7.3, Django 1.4.5 の文字列を確認
使用pyvenv和pip将Python3.4和Django 1.7结合在一起。
我正在创建第二个虚拟环境,有没有更好的方法呢?
$ xbuild/python-install 3.4.0 /opt/python3.4.0
$ cd /opt
$ /opt/python3.4.0/bin/pyvenv /tmp/venv
$ source /tmp/venv/bin/activate
(venv)$ pip install https://www.djangoproject.com/download/1.7b3/tarball/
(rehash on zsh)
(venv)$ python --version
Python 3.4.0
(venv)$ django-admin.py --version
1.7b3
(venv)$which pip
/opt/test2/venv/bin/pip
(venv)$ django-admin.py startproject test2
(venv)$ cd test2
(venv)$ deactivate
(rehash on zsh)
$ /opt/python3.4.0/bin/pyvenv venv
$ source venv/bin/activate
(venv) $ pip install https://www.djangoproject.com/download/1.7b3/tarball/
(rehash on zsh)
-
- settings.pyのsqlite3のdb保存先をdb/db.sqlite3へ変える
-
- パーミッションを変更
-
- views.pyとurls.pyを変更
-
- python mangae.py syncdb
python manage.py runserver
ブラウザで Python 3.4.0, Django 1.7b3 の文字列を確認
使用tornado在命令行上启动WSGI服务器
顺便提及,我参考了以下这篇文章。
-
- http://www.tornadoweb.org/en/stable/index.html
-
- https://github.com/facebook/tornado
- https://github.com/bdarnell/django-tornado-demo
我们将在这次更新中,在项目的根目录下,通过将tornado_main.py文件放置在下一个文件中来模糊问题。
与 https://github.com/bdarnell/django-tornado-demo 内容几乎相同,只是增加了通过命令行修改端口号的功能。另外,这次与 HelloHandler 无关的代码我忘记删除了。
from tornado.options import options, define, parse_command_line
import django.core.handlers.wsgi
import tornado.httpserver
import tornado.ioloop
import tornado.web
import tornado.wsgi
define('port', type=int, default=18000)
class HelloHandler(tornado.web.RequestHandler):
def get(self):
self.write('Hello from tornado')
def main():
parse_command_line()
wsgi_app = tornado.wsgi.WSGIContainer(
django.core.handlers.wsgi.WSGIHandler())
tornado_app = tornado.web.Application(
[('/hello-tornado', HelloHandler),
('.*', tornado.web.FallbackHandler, dict(fallback=wsgi_app)),
])
server = tornado.httpserver.HTTPServer(tornado_app)
server.listen(options.port)
tornado.ioloop.IOLoop.instance().start()
if __name__ == '__main__':
main()
请注意,如下所述,在 Django 1.7 中,使用此 tornado_main.py 实现将无法正常运行。
使用apt-get准备的选择
$ DJANGO_SETTINGS_MODULE=test1.settings python tornado_main.py
确认Python和Django的版本
pyvenv和pip的方法
(venv)$ pip install tornado
(venv)$ PYTHONPATH=venv/lib/python3.4/site-packages DJANGO_SETTINGS_MODULE=test2.settings python tornado_main.py
请确认Python和Django的版本。
让主管以守护进程的方式启动两个WSGI应用程序。
这次,我会给主管安排在apt中。就像上面所提到的分开管理的好处一样,我觉得对于主管来说并没有什么优势。
补充一点,虽然在apt-get中直接使用3.0a8也可以,但是我选择从不稳定版本中获取源码(3.0r1.1)进行构建。这与本篇文章无关,只是提供参考。
在安装了apt的supervisor后,将以下配置文件放置在/etc/supervisor/conf.d/目录下。
[program:test1]
command=python tornado_main.py --port=18101
directory=/opt/test1
autostart=true
autorestart=true
user=www-data
environment = DJANGO_SETTINGS_MODULE="test1.settings"
[program:test2]
command=/opt/test2/venv/bin/python tornado_main.py --port=18102
directory=/opt/test2
autostart=true
autorestart=true
user=www-data
environment = PYTHONPATH="/opt/test2/venv/lib/python3.4/site-packages", DJANGO_SETTINGS_MODULE="test2.settings"
两者都进行了活动,令人欣喜。
应用程序注册表尚未准备好。
2.7的情况还可以,但3.4的话就不能正常运作了。
在查看/var/log/supervisor/supervisord.log文件的同时,我们会详细列举并分析异常情况。
造成原因是因为Django 1.7的规格变更。
If you’re using Django in a plain Python script — rather than a management command — and you rely on the DJANGO_SETTINGS_MODULE environment variable, you must now explicitly initialize Django at the beginning of your script with:
>>> import django
>>> django.setup()
哦,好的。
from tornado.options import options, define, parse_command_line
import django
import django.core.handlers.wsgi
import tornado.httpserver
import tornado.ioloop
import tornado.web
import tornado.wsgi
define('port', type=int, default=18000)
class HelloHandler(tornado.web.RequestHandler):
def get(self):
self.write('Hello from tornado')
def main():
// For django 1.7
django.setup()
parse_command_line()
wsgi_app = tornado.wsgi.WSGIContainer(
django.core.handlers.wsgi.WSGIHandler())
tornado_app = tornado.web.Application(
[('/hello-tornado', HelloHandler),
('.*', tornado.web.FallbackHandler, dict(fallback=wsgi_app)),
])
server = tornado.httpserver.HTTPServer(tornado_app)
server.listen(options.port)
tornado.ioloop.IOLoop.instance().start()
if __name__ == '__main__':
main()
现在两者都可以在相同的开发环境下运行。
在Apache2中使用反向代理
由于我甚至不记得端口号,因此提前设置一个反向代理可能是一个不错的主意。(只是,由于需要单独使用supervisord和Apache来管理端口号,所以可能会在以后变得麻烦。)
启用mod_proxy,设置ProxyPass和ProxyPassReverse。省略
-
- http://localhost/test1
- http://localhost/test2
我们通过进行类似的操作来确认Python和Django的版本是否不同。
请看以下信息
虽然有些微妙,但我已经在终端上尝试运行了。