因为工作需要做全栈开发,所以必须要搭建一个web服务器和vue进行联调,但是如果采用django自带的服务器,静态资源的处理很麻烦,前端开发还是需要处理静态资源的,所以我还是需要一个专门的web服务器。而我习惯在windows上开发,linux搭建开发环境虽然方便,不过单就浏览器不能多开这一点就让我十分不爽,windows下可以下载好几个浏览器,然后每个浏览器用于不同的任务(调试程序的死机了,不能耽误我看文档的浏览器),甚至同一个浏览器也可以通过绿色版多运行几个配置环境。windows下没有uwsgi,所以nginx配合django不行,所以还是把目光转回了我多年前用过的apache。
很多网站上都有配置apache支持django的教程,不过这里还是要把我实践到的配置方法总结一下,和其他人的略有不同。既然是总结,那就把windows和linux都总结一下。
官方文档非常全面
https://docs.djangoproject.com/zh-hans/2.2/howto/deployment/wsgi/modwsgi/
不过它没有提到apache支持虚拟主机,通过一台apache服务器为多个域名提供服务的设置。另外,这篇文章
https://www.ziqiangxuetang.com/django/django-deploy.html
介绍的也很完整,不过没有提到python的配置,也没有提到windows平台的配置。我这里再补充一些。
第1步是给apache增加mod_wsgi支持
如果是windows平台,到
https://www.apachelounge.com/download/
下载apache的编译好的包
解压缩到安装目录后,进入apache/bin目录,然后运行
httpd -k install
就可以把apache安装为系统服务,然后通过命令行控制
httpd -k start //启动apache 服务
httpd -k stop //关闭apache 服务
httpd -k uninstall //删除apache服务
也可以运行ApacheMonitor通过图形界面来启动和关闭apache服务
然后下载mod_wsgi
mod_wsgi安装方法1
最方便的是到
https://www.lfd.uci.edu/~gohlke/pythonlibs/
下载mod_wsgi的二进制包,因为编译这个包时的编译器版本和编译apache的编译器版本必须要一致,所以各大python发行版都没有自带这个包,而是由apache用户自行添加。这个编译器版本不需要和python.exe可执行程序的编译器版本一致,但是要和python.exe的python版本保持一致。
根据自己的apache对应的编译器版本,和自己的python版本选择合适的mod_wsgi下载到本地。然后用pip安装
pip3 install mod_wsgi-4.6.7+ap24vc15-cp37-cp37m-win_amd64
如果你的python发行版是python3,那么pip也对应的是3,如果你使用virtualenv配置了多个虚拟python环境,那么你需要切换到对应的环境再执行,不过此时就必须要注意pip的版本了。python2在2020年就终止支持了,还是推荐用python3
然后在python安装目录下找到mod_wsgi-express.exe,执行
mod_wsgi-express module-config
过了一段时间,命令行会输出3行内容
LoadFile "D:/Programdata/Anaconda3/python37.dll"
LoadModule wsgi_module "D:/Programdata/Anaconda3/Lib/site-packages/mod_wsgi/server/mod_wsgi.cp37-win_amd64.pyd"
WSGIPythonHome "D:/Programdata/Anaconda3"
把它们复制到apache的httpd.conf中,不要放在任何一个尖括号对应的节点单元内,而是要放在根节点下。因为有LoadModule,所以我一般都是放在LoadModule段的末尾。我这里的目录名文件名都有大写,是我出于严谨的考虑编辑的,默认输出是全小写的。
然后用
httpd.exe -t
测试一下配置文件是否正确,如果不正确,控制台会有提示问题出在哪里。
mod_wsgi安装方法2
我用的是anaconda发行版,所以我希望所有的包都用conda管理,不希望用pip安装python包,那么就把whl文件的扩展名改为zip,然后解压缩,也可以得到这个pyd文件。其实关键就是这个pyd文件,其他的文件都不需要,在httpd.conf中,还是那3行配置,只不过把对应的目录和文件修改一下即可。
mod_wsgi并不是python运行所必须的,有没有它python都能正常运行。它在性质上更像是apache的一个模块,用于连接python的模块
mod_wsgi安装方法3
不过毕竟这个二进制包是基于官方python包编译的,如果担心对于anaconda发行版不兼容,毕竟这个包需要同时匹配python版本、apache版本、还有编译器版本,如果实在没有合适的包,也可以手动编译,源码包在
https://github.com/GrahamDumpleton/mod_wsgi/releases
解压缩后进入win32目录,这里提供了VC9和VC10的Makefile,经过实践检验,VC15(VC14.1)也就是MSVC2017,可以用VC10的文件
把ap24py34-win64-VC10.mk复制一份改名,其中ap后面的数字是apache版本,目前最新版是2.4.x,py后面的文件对应的是python版本,目前最新是3.7.x,VC后面的是编译器版本
打开它,把里面的
APACHE_ROOTDIR = c:\Apache24-win64-VC10
PYTHON_ROOTDIR = c:\Python34-win64-VC10
PYTHON_VERSION = 34
修改为对应的值,这里面的目录内的内容是为了编译的,也就是说只要内容一致,其目录位置可以不是实际生产环境的那个目录。要注意,其中的目录名中不要有空格,如果有要用“\”转义,否则后面会出错。所以如果生产环境在“Program Files”目录,中间有空格会导致问题,那么可以复制一份到没有空格的目录来支持编译。
然后进入MSVC控制台
nmake -f ap24py37-win64-VC15.mk
就会在win32目录下生成mod_wsgi.so,用这个so替换前面那个whl解压缩出来的pyd,也就是在httpd.conf文件的LoadModule那行修改载入的文件名指向这个生成的so即可
实践检验我用的MSVC2017+Anaconda2019.03+Apache2.4.41自己编译出来的so文件比whl解压缩出来的pyd文件小不少,可能有助于改善运行速度。
如果是linux,就非常简单了,绝大多数linux发行版都会自带mod_wsgi的包,只要用发行版自带的包管理器安装,apache自己就会配置好。debian系用apt,redhat系用yum或者dnf,suse用zypper,arch系用pacman,gentoo用emerge,BSD用port……这么多发行版,一个个都不一样,好烦啊。
启用了mod_wsgi,就可以在访问特定地址时,apache就会通过wsgi调用python程序来动态生成页面了。
要注意的是,所有的WSGI指令只有在载入了wsgi_module后才能使用,所以在配置文件中,都必须在LoadModule wsgi_module后面才行,包括后文提到的虚拟主机设置中,如果涉及到WSGI指令,就必须要把整个虚拟主机设置的节点放置在LoadModule wsgi_module之后
第2步是apache上虚拟主机的设置
如果apache服务器需要支持很多站点,就需要使用VirtualHost虚拟主机,跟据你的域名创建VirtualHost配置文件
/etc/apache2/sites-available/sitename.conf
内容如下
Define SITEROOT "F:/web"
ServerName www.yourdomain.com
ServerAlias otherdomain.com
ServerAdmin [email protected]
DocumentRoot "${SITEROOT}/yoursite"
Options Indexes FollowSymLinksAllowOverride None
Require all granted
Alias /media/ /home/demo/yoursite/media/
Require all granted
Alias /static/ /home/demo/yoursite/static/
Require all granted
WSGIScriptAlias /sublevel/ /home/demo/yoursite/yoursite/wsgi.py/
# WSGIDaemonProcess www.yourdomain.com python-path=/home/demo/yoursite:/home/demo/.virtualenvs/yoursite/lib/python3.7/site-packages
# WSGIProcessGroup www.yourdomain.com
Require all granted
Errorlog F:/log/apache2/yoursite/error.log
# Customlog "|/usr/bin/rotatelogs /var/log/apache2/piperck_access_%Y_%m_%d.log 86400 480" combined
这里对里面的配置简单介绍一下
DocumentRoot就是网站的主页目录,index放在这里,其他的其实也可以放在这里
media目录是用来接收用户上传文件的
static目录是提供静态文件的,django不提供静态文件服务,静态文件由apache来提供,这样可以减少计算量
WSGIScriptAlias参数的官方解释在这里
https://modwsgi.readthedocs.io/en/develop/configuration-directives/WSGIScriptAlias.html
其中说
WSGIScriptAlias /wsgi-scripts/ /web/wsgi-scripts/
的意思就是当用户访问http://domain/wsgi-scripts/someparam/时,后台会通过mod_wsgi调用python来来执行/web/wsgi-scripts/somparam/也就是执行wsgi服务器(django),对于django来讲对应的路由其实是somparam。也就是说通过这里就把apache和django项目联系起来了
如果访问的不是http://domain/wsgi-scripts/下的某个路径,那么apache不会执行python和wsgi,而是会在DocumentRoot下寻找符合的文件然后发送给请求方,如果找不到,则会返回apache自己的404页面。
在这里特别提醒:后面的参数“/web/wsgi-scripts/”最末尾的斜杠“/”必须要带上,否则apache接收到的参数将会变成“/web/wsgi-scriptssomparam”,如果是django就是“/home/demo/yoursite/yoursite/wsgi.pysomparam”,这明显是不对的,实际执行也会出错。如果带上了“/”,那么就是在执行“/home/demo/yoursite/yoursite/wsgi.py/somparam”,这样django的路由跳转就对了
第一个参数“/wsgi-scripts/”用处在于:如果一个站点有多个下属频道,比如新闻、娱乐、视频、聊天不同的板块,那么不同的板块可以访问不同的django项目。当然用一个django项目把所有这些都整合,然后通过django路由来分配也不是不可以,只是不同的django项目可以由不同的团队开发,便于项目和组织的管理。
是网站的存放目录
如果是Linux,使用命令
sudo a2ensite sitename
或
sudo a2ensite sitename.conf
激活这个虚拟主机,其实就是在httpd.conf中增加了一个
Include /path/to/sitename.conf
也可以手动处理,因为命令默认是追加的,如果你希望这个虚拟主机比较靠前,那么可以手动编辑httpd.conf文件
如果不用虚拟主机,那么也可以直接把虚拟主机的内容放置到httpd.conf的根节点。
windows平台没有a2ensite命令,所以只能手动编辑。可以放在VirtualHost节点里,也可以放在根节点里
把上面配置文件中的两行的注释去掉,可以使用virtualenv和mod_wsgi daemon模式来部署网站,再利用VirtualHost甚至可以在一个apache服务器下配置多个站点,每个站点配置多个django项目,而每个django项目可以配置不同的virtualenv,实现巨大的灵活性。不过WSGIDaemonProcess和WSGIProcessGroup这两个mod_wsgi指令在windows下没有,只能在linux下使用。
小窍门
编辑httpd.conf时,可以定义临时变量
Define SITEROOT "F:/Develop/web"
然后用
${SITEROOT}
来引用,如果你希望当成字符串来引用,最好还要再包上双引号
第3步是让python找到django站点运行时所需的各种包
有2种方法。
一种是修改django的wsgi.py文件
import os
from os.path import join,dirname,abspath
PROJECT_DIR = dirname(dirname(abspath(__file__))) # 3
import sys # 4
sys.path.insert(0,PROJECT_DIR) # 5
os.environ["DJANGO_SETTINGS_MODULE"] = "yoursite.settings" # 7
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
其中#3、#4、#5是在python-path中增加django项目的文件,这样python运行时确定能找到django项目里面的包,否则可能会出现找不到的情况。如果是virtualhost,那么这是必须的,如果直接在apache根节点配置站点,则不需要这么做。
另外一种是在apache中配置,在httpd.conf或者sitename.conf中增加
WSGIPythonPath /path/to/mysite.com
按照django官方文档的说法
WSGIPythonPath 行确保你的项目包能从 Python path 导入;换句话说, import mysite 能正常工作。
如果是apache根节点配置,这是必须的;如果是virtualhost,则这个配置和virtualhost冲突,不能添加这一行配置,所以如果是嵌入模式运行,只能在wsgi.py文件中修改,要么切换为后台模式。
第#7行,如果一台服务器有多个django项目时一定要修改成上面那样,否则访问的时候会发生网站互相串的情况,即访问A网站到了B网站,一会儿正常,一会儿又不正常。
除非使用 mod_wsgi daemon 后台模式,可是在windows下,apache不支持这种模式。
django从1.4版本开始,自动生成的wsgi.py是这样的
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'yoursite.settings')
根据
http://blog.dscpl.com.au/2012/10/requests-running-in-wrong-django.html
所述,其实这种变动会导致问题,所以请按照#7来更改。
第4步是设置目录访问权限
对于windows而言,其实目录访问权限几乎不起作用,主要是linux系统要注意,所以这一段讲的都是linux下的
一般目录权限设置为 755,文件权限设置为 644
假如项目位置在 /home/demo/yoursite (在yoursite下面有一个 manage.py,yoursite是项目名称)
cd /home/demo/
sudo chmod -R 644 yoursite
sudo find yoursite -type d | xargs chmod 755
apache 服务器运行用户可以在 /etc/apache2/envvars 文件里面改,这里使用的是默认值,当然也可以更改成自己的当前用户,这样的话权限问题就简单很多,但在服务器上推荐有 www-data 用户,更安全。以下是默认设置:
# Since there is no sane way to get the parsed apache2 config in scripts, some
# settings are defined via environment variables and then used in apache2ctl,
# /etc/init.d/apache2, /etc/logrotate.d/apache2, etc.
export APACHE_RUN_USER=www-data
export APACHE_RUN_GROUP=www-data
media 文件夹一般用来存放用户上传文件,static 一般用来放自己网站的js,css,图片等,在settings.py中的相关设置
STATIC_URL 为静态文件的网址 STATIC_ROOT 为静态文件的根目录,
MEDIA_URL 为用户上传文件夹的根目录,MEDIA_URL为对应的访问网址
在settings.py中设置:
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/dev/howto/static-files/
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR,'static')
# upload folder
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR,'media')
在 Linux 服务器上,用户上传目录还要设置给 www-data 用户的写权限,下面的方法比较好,不影响原来的用户的编辑。
假如上传目录为 yoursite/media/uploads 文件夹,进入media文件夹,将 uploads 用户组改为www-data,并且赋予该组写权限:
cd media/ # 进入media文件夹
sudo chgrp -R www-data uploads
sudo chmod -R g+w uploads
这两条命令,比直接用sudo chown -R www-data:www-data uploads 好,因为下面的命令不影响文件原来所属用户编辑文件,fedora系统应该不用设置上面的权限,但是这里推荐用ubuntu,除非你对linux非常熟悉,你自己选择。
如果你使用的是sqlite3数据库,还会提示 Attempt to write a readonly database,同样要给www-data写数据库的权限
进入项目目录的上一级,比如project目录为 /home/demo/yoursite 那就进入 /home/demo 执行下面的命令(和修改上传文件夹类似)
sudo chgrp www-data yoursite
sudo chmod g+w yoursite
sudo chgrp www-data yoursite/db.sqlite3 # 更改为你的数据库名称
sudo chmod g+w yoursite/db.sqlite3
上面的不要加 -R ,-R是更改包括所有的子文件夹和文件,这样不安全。个人建议可以专门弄一个文件夹,用它来放sqlite3数据库,给该文件夹www-data写权限,而不是整个项目给写权限,有些文件只要读的权限就够了,给写权限会造成不安全。
不过大型程序恐怕用sqlite的人非常少,这个几乎用不上
第5步添加PostgreSQL支持
如果django后台使用的数据库是PostgreSQL,那么还需要在apache的httpd.conf根节点中添加
LoadFile "D:/Program Files/PostgreSQL/11/bin/libpq.dll"
否则会提示psycopg2 DLL载入错误
如果你不用PostgreSQL,而是MySQL或者其他数据库后端,那就不需要这个配置。
生产环境和开发环境的差异
嵌入模式把python作为apache进程内部的一部分来执行,这是从mod_php发展到mod_python时遗留的,简单方便,如果程序本身有什么问题,就会导致apache挂掉进而导致整个apache服务挂掉,如果apache上有多个虚拟主机,受影响的就是一大片,而且由于线程安全问题,线程间通信会有问题;daemon也就是后台模式,python的wsgi程序是单独运行的,apache和daemon程序通过socket通讯来交换信息,系统资源占用大一些,不过更安全,互不影响,一个程序挂了,不影响另外一个,服务器稳定性比较好。nginx和IIS对python的支持只有daemon模式,没有嵌入模式。php更适合嵌入式,而python更适合后台模式。
在生产环境建议使用daemon模式。考虑到各个web服务器程序的性能差异,生产环境最好是使用nginx,ngxin在windows下不能搭配django,所以生产环境最好是linux+nginx+gunicorn+django+数据库+前端库,也可以把gunicorn替换为uwsgi,不过目前看来gunicorn还是要更流行一些。如果是windows生产环境,web服务器最好用IIS,所以搭配的自然就是wfastcgi。
只不过我作为开发者,不想在自己的电脑上装IIS,但我还要用windows,所以才要这么费力的在windows上用apache+mod_wsgi+django