在这里讲述部署的方法和常见的问题,并给出了在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
防火墙:
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()