在这里讲述部署的方法和常见的问题,并给出了在BAE,JAE,SAE等上面部署的实例。

Django + nginx + Gunicorn/uwsgi部署方式,参见另一篇:Django部署(nginx)

如果是自己的服务器(比如阿里云的或者自己搭建的虚拟机)请看下文:

如果你是新手,个人推荐你用Ubuntu,除非你对Linux非常熟悉,Ubuntu服务器的优点:

一、开机apache2等都自动启动,不需要额外设置

二、安装软件非常方便apt-get搞定

三、安装ssh,git等也非常容易,几乎是傻瓜化

可以先用用Linux Mint,它用起来更简单,和ubuntu兼容。

下面是Ubuntu上部署详细步骤:

1、安装apache2和mod_wsgi



wulaoer@wulaoer:~$sudo apt-get install apache2
 
# Python 2
wulaoer@wulaoer:~$ sudo apt-get install libapache2-mod-wsgi
 
# Python 3
wulaoer@wulaoer:~$sudo apt-get install libapache2-mod-wsgi-py3



2、确认安装的apache2版本号



wulaoer@wulaoer:~$ apache2 -v
Server version: Apache/2.4.12 (Ubuntu)
Server built:   Jul 24 2015 15:59:00



3、准备一个新网站



wulaoer@wulaoer:~$ sudo vi /etc/apache2/sites-available/sitename.conf
sudo: 无法解析主机:wulaoer
<VirtualHost *:80>
    ServerName www.yourdomain.com
    ServerAlias otherdomain.com
    ServerAdmin wulaoer@163.com

    Alias /media/ /home/tu/blog/media/
    Alias /static/ /home/tu/blog/static/

    <Directory /home/tu/blog/media>
        Order deny,allow

        Allow from all
</Directory>

    <Directory /home/tu/blog/static>
        Require all granted
    </Directory>

    WSGIScriptAlias / /home/tu/blog/blog/wsgi.py
    # WSGIDaemonProcess ziqiangxuetang.com python-path=/home/tu/blog:/home/tu/.virtualenvs/blog/lib/python2.7/site-packages
    # WSGIProcessGroup ziqiangxuetang.com

    <Directory /home/tu/blog/blog>
    <Files wsgi.py>
        Require all granted
    </Files>
    </Directory>
</VirtualHost>



如果你的apache的版本号是 2.2.x

用下面的代替 Require all granted



Order deny,allow
Allow from all



备注:把上面配置的文件中这两句的备注去掉,可以使用virtualenv来部署网站,当然也可以只写一个/home/tu/blog



# WSGIDaemonProcess ziqiangxuetang.com python-path=/home/tu/blog:/home/tu/.virtualenvs/blog/lib/python2.7/site-packages
    # WSGIProcessGroup ziqiangxuetang.com



4、修改wsgi.py文件

注意:上面如果写了WSGIDaemonProcess 的话,这一步可以跳过,即可以不修改 wsgi.py 文件。 

上面的配置中写的WSGIScriptAlias / /home/tu/blog/blog/wsgi.py

就是把apache2和你的网站project联系起来了



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"] = "blog.settings" # 7
 
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()



第3、4、5行为新加内容,作用是让脚本找到django项目的位置,也可以在sitename.conf中做,用WSGIPythonPath,想连接的自行搜索,第7行如果一台服务器有多个django project时一定要修改成上面那样,否则访问的时候会发生网站互相串的情况,即访问A网站到了B网站,一会正常,一会不正常(当然可以使用mod_wsgi daemon模式,点击这里

5、设置目录和文件权限

一般目录权限设置为755,文件权限设置为644

假如项目位置在/home/tu/wulaoer(在wulaoer下面有一个manage.py,wulaoer是项目名称)



cd /home/tu/
sudo chmod -R 644 wulaoer
sudo find wulaoer -type d -exec chmod 755 \{\} \;



上传文件夹权限

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用户的写权限,下面的方法比较好,不影响原来的用户编辑。

假如上传目录为wulaoer/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/tu/wulaoer哪就进入/home/tu执行下面的命令(和修改上传文件类似)



sudo chgrp www-data blog
sudo chmod g+w blog
sudo chgrp www-data blog/db.sqlite3  # 更改为你的数据库名称
sudo chmod g+w blog/db.sqlite3



备注:上面的不要加-R,-R是更改包括所有的子文件夹和文件,这样不安全。个人建议可以专门弄一个文件夹,用它来放sqlite3数据库,给该文件夹www-data写权限,而不是整个项目给写权限,有些文件只要读懂权限就够了,给写权限会造成不安全。

6、激活新网站



sudo a2ensite sitename 或 sudo a2ensite sitename.conf



如果顺利,这样网站就搭建成功了,访问你的网址试试看,如果发现问题就接着看下面的。

7、错误排查

一、没有静态文件,网站打开很乱,没有布局,多半是静态文件没有生效。

1、确保你的配置文件中的路径是正确的

2、确保你的settings.py中的文件设置正确

3、收集静态文件



python manage.py collectstatic



二、网站打开后报错

这时你可以把settings.py更改



DEBUG = True



重启服务器



sudo service apache2 restart



再访问网站来查看具体的出错信息。

如果这样做还看不到出错信息,只是显示一个服务器错误,你可以查看apache2的错误日志



cat /var/log/apache2/error.log



根据错误日志里面的内容进行修改!

总结:

部署时文件对应关系:


sitename.conf ---> wsgi.py---> settings.py----> urls.py ----> views.py

扩展

明白了上面的关系,一个Django project使用多个域名或让app使用子域名很简单,只要新建wsgi.py文件,更改里面对应的settings文件,新的settings文件可以对应新的urls.py,从而做到访问与原来不同的地址!

Django 部署(Nginx)

1、运行开发服务器测试



cd wulaoer # 进入项目 wulaoer 目录
python manage.py runserver



2、安装nginx和需要的包

2.1 安装nginx等软件

ubuntu/Linux Mint等,下面简写为(ubuntu):



sudo apt-get install python-dev nginx



centos/Fedora/redhat等,下面简写为(centos)



sudo yum install epel-release
sudo yum install python-devel nginx



2.2安装supervisor,一个专门用来管理进程的工具,我们用它来管理gunicorn/uwsgi



sudo pip install supervisor



Ubuntu用户,请直接看3,一下是Centos注意事项:

Centos下,如果不是非常懂SELinux和iptables的话,为了方便调试,可以先临时关闭它们,如果发现部署了之后出不来结果,可以临时关闭测试一下,这样就知道是不是SELinux和iptables的问题

CentOS 7 iptables如何使用http://stackoverflow.com/questions/24756240/

将SELinux设置为宽容模式,方便调试:



sudo setenforce 0



防火墙相关的设置:



可以选择临时关闭防火墙
sudo service iptables stop
 
或者开放一些需要的端口,比如 80
sudo iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT



上面的两条命令,如果是Centos 7 用



临时关闭防火墙
sudo systemctl stop firewalld
 
或者 开放需要的端口
sudo firewall-cmd --zone=public --add-port=80/tcp --permanent
sudo firewall-cmd --reload



备注:由于我们还没有用最新版的Fedora, 新版Fedora需要用dnf来安装包,需求的同学自测,可以参考这里

3、使用gunicorn/uwsgi来部署(二选一)

注意:以下为二选一,不需要两个都用

3.1使用gunicorn(纯Python实现的包):



sudo pip install gunicorn



在项目目录下运行下面的命令进行测试:



gunicorn -w4 -b0.0.0.0:8001 wulaoer.wsgi



-w表示开启多少个worker,-b表示要使用的ip和port,我们这里用的是8001,0.0.0.0代表监控电脑的所有ip。

如果使用了virtualenv可以这样



/path/to/env/bin/gunicorn --chdir /path/to/project --pythonpath /path/to/env/ -w4 -b0.0.0.0:8017 project.wsgi:application



用--pythonpath指定依赖包路径,多个点时候用逗号,隔开,如:'/path/to/lib,/home/tu/lib'

3.2使用uwsgi(纯C 语音实现的包):

安装uwsgi



sudo pip install uwsgi



使用uwsgi运行项目



uwsgi --http :8001 --chdir /path/to/project --home=/path/to/env --module project.wsgi



这样就可以跑了,  --home指定virtualenv路径,如果没有可以去掉。project.wsgi指project/wsgi.py文件

如果端口提示已经被占用:



probably another instance of uWSGI is running on the same address (:8002).
bind(): Address already in use [core/socket.c line 764]



这时可用把相关的进程kill掉:

按照端口进行查询:



lsof -i :8002



可用查出:



COMMAND  PID USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
uwsgi   2208   tu    4u  IPv4 0x53492abadb5c9659      0t0  TCP *:teradataordbms (LISTEN)
uwsgi   2209   tu    4u  IPv4 0x53492abadb5c9659      0t0  TCP *:teradataordbms (LISTEN)



这时根据PID可以用下面的命令kill掉相关的程序:



sudo kill -9 2208 2209



按照程序名称查询:



ps aux | grep uwsgi



4、使用supervisor来管理进程

安装supervisor软件包



(sudo) pip install supervisor



生成supervisor默认配置文件,比如我们放在/etc/supervisord.conf路径中:



(sudo) echo_supervisord_conf > /etc/supervisord.conf



打开supervisor.conf在最底部添加(每行前面不要有空格,防止报错):



[program:wulaoer]
command=/path/to/uwsgi --http :8003 --chdir /path/to/wulaoer --module wulaoer.wsgi
directory=/path/to/wulaoer
startsecs=0
stopwaitsecs=0
autostart=true
autorestart=true



command中写上对应的命令,这样,就可以用supervisor来管理来。

启动supervisor



(sudo) supervisord -c /etc/supervisord.conf



重启wulaoer程序(项目):



(sudo) supervisorctl -c /etc/supervisord.conf restart wulaoer



启动,停止,或重启supervisor管理的某个程序或所有程序:



(sudo) supervisorctl -c /etc/supervisord.conf [start|stop|restart] [program-name|all]



以uwsgi为例,上面这样使用一行命令太长了,我们是一ini配置文件来搞定,比如项目在/home/tu/wulaoer这个位置。

在其中新建一个uwsgi.ini全路径为/home/tu/wulaoer/uwsgi.ini



[uwsgi]
socket = /tmp/wulaoer.sock
chdir=/home/tu/wulaoer
wsgi-file = zqxt/wsgi.py
touch-reload=/home/tu/wulaoer/reload
 
processes = 2
threads = 4
 
chmod-socket = 664
chown-socket=tu:www-data



注意上面的/tmp/wulaoer.sock,一会我们把它和nginx关联起来。

在项目上新建一个空白的reload文件,只要touch一下这个文件(touch reload)项目就会重启。

修改supervisor配置文件中的command一行:



[program:wulaoer]
command=/path/to/uwsgi --ini /home/tu/wulaoer/uwsgi.ini
directory=/path/to/wulaoer
startsecs=0



然后重启一下supervisor:



(sudo) supervisorctl -c /etc/supervisord.conf restart wulaoer
或者
(sudo) supervisorctl -c /etc/supervisord.conf restart all



5、配置nginx

新建一个网站wulaoer



sudo vim /etc/nginx/sites-available/wulaoer.conf



写入一下内容



server {
    listen      80;
    server_name www.wulaoer.org;
    charset     utf-8;
 
    client_max_body_size 75M;
 
    location /media  {
        alias /path/to/project/media;
    }
 
    location /static {
        alias /path/to/project/static;
    }
 
    location / {
        uwsgi_pass  unix:///tmp/wulaoer..sock;
        include     /etc/nginx/uwsgi_params;
    }
}



激活网站:



sudo ln -s /etc/nginx/sites-available/zqxt.conf /etc/nginx/sites-enabled/wulaoer.conf



测试配置语法问题



sudo service nginx configtest



重启nginx服务器:



sudo service nginx reload 或者 sudo service nginx restart



一些有用的参考教程:

Django 官网部署教程:

https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/gunicorn/

https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/uwsgi/

 

一些博客相关教程:

http://www.ituring.com.cn/article/201045

http://www.jianshu.com/p/be9dd421fb8d


 

nginx 与 socket

http://uwsgi-docs.readthedocs.org/en/latest/tutorials/Django_and_nginx.html#configure-nginx-for-your-site

 

防火墙:

iptables: https://www.digitalocean.com/community/tutorials/how-to-setup-a-basic-ip-tables-configuration-on-centos-6

centos 7 FireWalld: http://stackoverflow.com/questions/24756240/how-can-i-use-iptables-on-centos-7

ubuntu ufw 防火墙:http://wiki.ubuntu.org.cn/Ufw%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97

 

uwsgi ini 配置文件:http://uwsgi-docs.readthedocs.org/en/latest/tutorials/Django_and_nginx.html#configuring-uwsgi-to-run-with-a-ini-file

Django 发送邮件

我们常常会用到一些发送邮件的功能,比如有人提交了应聘的表单,可以向HR的邮箱发送邮件,这样HR不看网站就可以知道有人在网站上提交了应聘信息。

1、配置相关参数

如果用的是阿里云的企业邮箱,则类似于下面:

在settings.py的最后面加上这些



EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
  
EMAIL_USE_TLS = False
EMAIL_HOST = 'smtp.wulaoer.org'
EMAIL_PORT = 25
EMAIL_HOST_USER = 'mail@wulaoer.org'
EMAIL_HOST_PASSWORD = 'xxxx'
DEFAULT_FROM_EMAIL = 'mail@wulaoer.org'



DEFAULT_FROM_EMAIL 还可以写成这样:



DEFAULT_FROM_EMAIL = 'wulaoer <wulaoer@163.com>'



这样别人收到的邮件就会有你设定的名称,如下:



发件人:wulaoer <wulaoer@163.com>



其他邮箱参数可能登陆邮箱看帮助信息,也可以尝试在搜索引擎中搜索:“SMTP 邮箱名称”,比如:“163 SMTP” 进行查找。

2、发送邮件

2.1官网的一个例子:



from django.core.mail import send_mail
 
send_mail('Subject here', 'Here is the message.', 'from@example.com',
    ['to@example.com'], fail_silently=False)



2.2 一次性发送多个邮件:



from django.core.mail import send_mass_mail
 
 
message1 = ('Subject here', 'Here is the message', 'from@example.com', ['first@example.com', 'other@example.com'])
message2 = ('Another Subject', 'Here is another message', 'from@example.com', ['second@test.com'])



备注:send_mail每次发送邮件都会建立一个连接,发送多封邮件时建立多个连接。而send_mass_mail是建立单个连接发送多封邮件,所以一次性发送多封邮件时send_mass_mail要优与send_mail。

2.3 如果我们想在邮件中添加附件,发送html格式的内容



from django.conf import settings
from django.core.mail import EmailMultiAlternatives
 
 
from_email = settings.DEFAULT_FROM_EMAIL
# subject 主题 content 内容 to_addr 是一个列表,发送给哪些人
msg = EmailMultiAlternatives(subject, content, from_email, [to_addr])
 
msg.content_subtype = "html"
 
# 添加附件(可选)
msg.attach_file('./twz.pdf')
 
# 发送
msg.send()



上面的做法可能有一些风险,除非你确定你的接收者都可以阅读html格式的邮件。

为了安全起见,你可以弄两个版本,一个纯文本(text/plain)的为默认的,另外在提供一个html版本的(好像好多国外发送的邮件都是纯文本的)



from __future__ import unicode_literals
 
from django.conf import settings
from django.core.mail import EmailMultiAlternatives
 
subject = '来自吴老二的问候'
 
text_content = '这是一封重要的邮件.'
 
html_content = '<p>这是一封<strong>重要的</strong>邮件.</p>'
 
msg = EmailMultiAlternatives(subject, text_content, from_email, [to@youemail.com])
 
msg.attach_alternative(html_content, "text/html")
 
msg.send()