大道至简,知行合一。

手摸手教你nginx + uWSGI +Django部署项目

之前我们分析了Django不同的部署方式之间的差别,包括自带的调试runserver、uwsgi和nginx等,今天我们讲一下常规的Django项目部署的流程。

这里演示所用环境为:

  • CentOS 7.6
  • Python 2.7.5
  • Django 1.11.5
  • uWSGI 2.0.18
  • nginx 1.6.0

准备Django项目

首先要准备你的项目,这里假设没有现成的,从头创建一个简单的helloworld项目做演示。

安装Django

创建Django项目首先需要安装django模块,具体的版本随项目需求变化,一般为1.11到2.0之间,尽量不要用最新的2.2版本,会出现很多系统依赖的问题(比较恶心的如ssl)。安装的方法为:

pip install django    #如果需指定版本:pip install django==1.11.5

创建Django项目

安装完django,就可以使用django-admin来创建项目,如果环境变量中未设置django-admin,可以查找django-admin.py的路径,使用绝对路径执行:

#环境变量中有django-admin
django-admin startproject helloworld    

#环境变量中没有django-admin
find / -name django-admin
/usr/local/python/bin/django-admin.py startprocject helloworld
#当然为了方便下次使用,可以及时将django-admin添加到环境变量中
ln -s /usr/local/python/bin/django-admin.py  /usr/bin/django-admin

调试项目是否正确

创建项目后,我们就可以进行开发了。这里就不做展开了,假设一切正常,开发完毕准备部署,我们可以在调试环境检查最终检查一下项目是否有其他问题:

cd helloworld
python manage.py runserver 0.0.0.0:80
#如果是在别的机器上查看,记得settings中的ALLOWED_HOSTS做修改

OK,项目一切正常,当然这里Django只对一些语法、简单配置和连接等信息做了检查,如果你的项目比较复杂,很多异常是无法在这个调试中发现的。

使用uWSGI部署Django项目

OK,项目上线了,我们需要使用uWSGI来部署项目,提高项目的并发能力。

区分uwsgi、WSGI、uWSGI

  • WSGI 是一个Web服务器(如nginx)与应用服务器(如uWSGI)通信的一种规范(协议)。官方定义是,the Python Web Server Gateway Interface。从名字就可以看出来,这东西是一个Gateway,也就是网关。网关的作用就是在协议之间进行转换。在生产环境中使用WSGI作为python web的服务器。Python Web服务器网关接口,是Python应用程序或框架和Web服务器之间的一种接口,被广泛接受。WSGI没有官方的实现, 因为WSGI更像一个协议,只要遵照这些协议,WSGI应用(Application)都可以在任何服务器(Server)上运行;
  • uWSGI 实现了WSGI的所有接口,是一个快速、自我修复、开发人员和系统管理员友好的服务器。把 HTTP 协议转化成语言支持的网络协议。uWSGI代码完全用C编写,效率高、性能稳定;
  • uwsgi是一种线路协议,不是通信协议,常用于在uWSGI服务器与其他网络服务器的数据通信。uwsgi协议是一个uWSGI服务器自有的协议,它用于定义传输信息的类型;

简单来说,WSGI是网关、接口,uWSGI是一种服务,uwsgi是一种线路协议。下文和网上提到的uwsgi均为uWSGI。

安装uWSGI

首先要安装uWSGI,和django的安装类似:

pip install uwsgi    #如果指定版本,pip install uwsgi==2.0.18

默认uWSGI会添加到环境变量中,可以直接在命令行运行“uwsgi”测试一下:

如果报错,可以查找uWSGI的路径,并建立软连接到环境变量中:

#举例,如果uWSGI无问题,该步骤可以略过
ln -s /usr/local/python/bin/uwsgi /usr/bin/uwsgi

检查uWSGI是否可用

在当前目录下创建临时测试文件(这里命名为test.py),编辑写入内容如下:

def application(env, start_response):
    start_response('200 OK', [('Content-Type','text/html')])
    return [b"Hello World"]

然后运行命令,查看是否成功:

uwsgi --http :8002 --wsgi-file test.py

浏览器中打开web界面查看是否成功:

OK,uWSGI安装成功。

uWSGI部署Django

uWSGI部署Django有两种方式,一种是直接使用命令提供参数来部署,另一种是使用配置文件部署,后者是前者的简化。

uWSGI使用命令部署Django的形式如下:

uwsgi --http :8008 --chdir /home/helloworld --wsgi-file hello/wsgi.py --master --processes 4 --threads 2 --stats 127.0.0.1:9191
#chdir:指定运行目录,后面提供项目的绝对路径
#wsgi-file: 载入wsgi-file,提供相对路径

当然也有很多其他的参数可以使用,详细内容可以参见官方文档,这里展示一些常见参数:

  • http:协议类型和端口号;
  • processes:开启的进程数量;
  • workers:开启的进程数量,等同于processes(官网的说法是spawn the specified number ofworkers / processes);
  • stats:在指定的地址上,开启状态服务;
  • threads:运行线程。由于Python GIL的存在,在部署Django项目时比较鸡肋;
  • master:允许主进程存在;
  • daemonize:使进程在后台运行,并将日志打到指定的日志文件(常用)或者udp服务器;
  • pidfile:指定pid文件的位置,记录主进程的pid号;
  • vacuum:服务退出时自动清理环境,删除unix socket和pid文件;

因为参数非常多,所以我们常常会使用配置文件来代替命令部署项目,uWSGI支持的配置文件类型有很多种,包括xml、ini、json、yaml等,可以参考官方文档

常用的uWSGI配置文件类型是ini,在项目路径下新建文件,这里举例为“uwgsi.ini”:

# uwsgi.ini
[uwsgi]
chdir= /home/hellworld
daemonize=/home/helloworld/app.log
module= helloworld.wsgi
http= :8008
harakiri= 100    #设置超时时间
master= true
processes= 32
vacuum=true

其中harakiri这个参数比较重要,它是用来设置超时时间的,开发中提供操作接口,接口在后台调用了带外命令,可能耗时很长,uWSGI默认超时30秒,超时即断开连接,对接口调度者非常不友好,所以可以通过这个参数将超时时间适当延长。

有了配置文件,就不需要像刚刚调用命令启动服务一样有非常多的参数信息了,只需要指定配置文件就可以启动:

uwsgi --ini uwsgi.ini  #如果后台运行失败,可以指明-d参数:uwsgi -d --ini uwsgi.ini

#uwsgi通过配置文件启动会在同一路径下生产.pid文件,如果配置了其他路径另说,内容是uWSGI的主进程进程号
#uwsgi重启:uwsgi --reload xxx.pid
#uwsgi停止:uwsgi --stop xxx.pid

然后访问浏览器查看是否可以正常访问,有问题可以修改一下配置。

使用nginx部署Django项目

常规情况下使用uWSGI部署Django已经足够,但是上线的话我们还是要使用nginx反向代理,所以这里介绍一下进一步使用nginx部署Django项目的流程。

安装nginx

nginx需要直接下载编译安装即可,具体的版本可以根据自己的需求选择,在官网下载即可,这里以1.6.0版本为例:

cd /usr/local/
wget http://nginx.org/download/nginx-1.6.0.tar.gz
tar xf nginx-1.6.0.tar.gz
cd nginx-1.6.0
./configure 
make && make install
#测试nginx:
nginx   #直接在命令行输入nginx,如果提示找不到,可以将nginx链接到系统路径下
#ln -s /usr/local/nginx/sbin/nginx /usr/bin/nginx
#测试完成后记得清除nginx,防止下次启动报错:killall -9 nginx

测试nginx是否安装成功

修改nginx的配置文件(“/usr/local/nginx/conf/nginx.conf”),内容如下:

server {
        listen       8888;   #修改默认80为8888或其他非常用端口,防止冲突
        server_name  localhost;

然后启动nginx服务,查看浏览器界面是否正常,如可以打开如下界面即OK:

另外,这里贴一下经验:

  • 如果是在虚拟机上测试,本地打不开,可能需要查看一下防火墙规则;
  • nginx路径下有多个配置文件时,可以通过-c参数指明配置,如“nginx -c /usr/local/nginx/conf/nginx190806.conf”;
  • nginx重启报错“nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)”,说明端口被占用,可能是还有同样配置的nginx进程在后台运行,可以使用命令杀掉“ killall -9 nginx ”;

nginx + uWSGI + django 部署项目

整合nginx和uWSGI来部署django,只需要继续配置nginx的配置即可,主要调整再location中添加“ include uwsgi_params ”和“uwsgi_pass”,最终参考如下:

#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    server {
        listen       8092;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            include uwsgi_params;
            uwsgi_pass 127.0.0.1:8008
            uwsgi_read_timeout 2;
            root   html;
            index  index.html index.htm;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }


    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}


    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}

}

配置完成后,运行命令启动项目:

uwsgi --ini /home/helloworld/uwsgi.ini & nginx

在浏览器中检查页面是否可以正常打开,注意配置的端口号保持一致。

认识nginx + uwsgi + django 部署的原理

这里不展开讲了,后续可能单独出一篇文章介绍,大家简单了解一下架构。

OK,完成。

其他问题

这里简单记录一下你可能遇到的问题或者疑问,并提供一下解决方案:

使用virtualenv搭配uWSGI怎么解决版本问题

有些同学使用virtualenv做版本控制,这里另外吐槽一下,推荐尽快切换到Python3,虽然virtualenv可以控制Python版本,但是uWSGI默认使用Python2,如果Python版本不匹配,即你的项目可能是Python3写的,启动时就可能报错“no modula name ‘site’”的问题,解决方法是使用uwsgi-plugin,然后在uWSGI的配置文件中声明Python的版本即可:

  • Python2的plugin:apt-get install uwsgi-plugin-python
  • Python3的plugin:apt-get install uwsgi-plugin-python3
[uwsgi]
#将helloworld项目和全局uwsgi解释器指定为Python2
plugin=python  
virtualenv=/home/helloworld
#将helloworld项目和全局uwsgi解释器指定为Python3
plugin=python3  
virtualenv=/home/helloworld

如何结合Django对静态文件进行加速

首先还是使用Django本身的静态文件收集:

python manage.py collectstatic

然后在nginx中配置好访问路径即可,在配置文件中修改:

http {
    server{
       location /static {
          alias /home/helloworld/static;
            }
       }
}

赞(0)
未经允许不得转载:北凉柿子 » 手摸手教你nginx + uWSGI +Django部署项目
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址