gunicorn+supervisor+nginx+fabric部署Web应用

学习如何去部署一个Web应用。

1、首先选择一个WSGI服务器,之前看的是uWSGI,但uWSGI配置稍微麻烦些,因此这里考虑使用gunicorn。

2、使用supervisor控制进程;

3、选用静态服务器,选用nginx做负载均衡,以及处理静态文件;

4、使用fabric,ansible等工具批量部署。

下面记录我Django项目部署的过程(系统是Ubuntu14.04)。

1、gunicorn

基本用法: 在工程目录下执行:

gunicorn -w4 app.wsgi:application

-w 表示开启多少个 worker,-b 表示 gunicorn 开发的访问地址。

命令敲进去之后,会出现:
Listening at :http://127.0.0.1:8000
Using worker:sync 同步阻塞
4个worker

(感觉比uWSGI简单多了)

默认gunicorn 默认使用同步阻塞的网络模型(-k sync),对于大并发的访问可能表现不够好, 它还支持其它更好的模式,如gevent(协程)。gevent可以保证在IO请求时实现自动切换。

Gunicorn should only need 4-12 worker processes to handle hundreds or thousands of requests per second。gunicorn只需要4-12个进程处理每秒数以千计的请求。

服务模型(Server Model)

Gunicorn是基于 pre-fork 模型的。也就意味着有一个中心管理进程( master process )用来管理 worker 进程集合。Master从不知道任何关于客户端的信息。所有的请求和响应处理都是由 worker 进程来处理的。

Master(管理者)

主程序是一个简单的循环,监听各种信号以及相应的响应进程。master管理着正在运行的worker集合,通过监听各种信号比如TTIN, TTOU, and CHLD. TTIN and TTOU响应的增加和减少worker的数目。CHLD信号表明一个子进程已经结束了,在这种情况下master会自动的重启失败的worker。

gunicorn最好也将配置写到配置文件中,便于管理。在工程目录下, 建立一个gunicorn_conf.py文件。

bind = '127.0.0.1:8000'

workers=4

gunicorn可以充当一个WSGI服务器,但是单纯的使用它,还是有一定风险的。比如因为一些未知的元素,突然服务器挂了,但是你自己却不能及时发现。这时出现了一个利器,在gunicorn中称之为monitor,监控器。最好用的就是supervisor,他是一个有效的进程管理器,会保证在服务器down掉之后,重启应用。

2、supervisor

supervisor用于管理多进程,当进程莫名挂掉可以帮你restart,保证服务持久在线。

首先在工程目录下创建配置文件 echo_supervisord_conf > etc/supervisord.conf

supervisor的使用方法:

    [program:myapp]
    command=/home/haibo/study/myworks/venv/bin/gunicorn -w4 -b0.0.0.0:2170 myapp:app    ; supervisor启动命令
    directory=/home/haibo/study/myworks/dailyblog                                                 ; 项目的文件夹路径
    startsecs=0                                                                             ; 启动时间
    stopwaitsecs=0                                                                          ; 终止等待时间
    autostart=false                                                                         ; 是否自动启动
    autorestart=false                                                                       ; 是否自动重启
    stdout_logfile=/home/haibo/study/myworks/dailyblog/log/gunicorn.log                           ; log 日志
    stderr_logfile=/home/haibo/study/myworks/daiy=lyblog/log/gunicorn.err

command里的路径可以是相对,也可以是绝对的,但我一般就写成绝对的,这也利于别人维护嘛。

supervisord -c supervisor.conf                             通过配置文件启动supervisor
supervisorctl -c supervisor.conf status                    察看supervisor的状态
supervisorctl -c supervisor.conf reload                    重新载入 配置文件
supervisorctl -c supervisor.conf start [all]|[appname]     启动指定/所有 supervisor管理的程序进程
supervisorctl -c supervisor.conf stop [all]|[appname]      关闭指定/所有 supervisor管理的程序进程

可以通过supervisor.pid里的pid来kill掉supervisor进程。

当我们修改配置后,需要重新加载配置文件

supervisorctl -c supervisor.conf reload

此时可能出现: 如果出现如下异常:

error: <class 'socket.error'>, [Errno 111] Connection refused: file: /usr/lib64/python2.6/socket.py line: 567

解决办法如下:

先启动supervisor服务 
sudo supervisord -c /path/to/your/supervisord.conf
sudo supervisorctl -c /path/to/your/supervisord.conf

然后:

supervisorctl start myapp

记录另外一个问题:当启动supervisor服务时,总是提前unknown error making dispatchers for 'app_name' :EACCES。 这种问题是因为权限引起的,更改工程目录的权限即可。 chown -R haibo:haibo /srvdailyblog/www。

再介绍一下supervisor的组件

1、supervisord
是服务端程序,主要功能是启动supervisor程序自身,启动supervisor管理的子进程,响应来自clients的请求,重启闪退或异常退出的子进程,把子进程的stderr或stdout记录到日志文件中,生成和处理Event。

2、supervisorctl
是客户端程序,主要功能就是可以利用它来查看子进程状态, 启动/停止/重启子进程,获取running子进程的列表等等。。。最牛逼的一点是,supervisorctl不仅可以连接到本机上的 supervisord,还可以连接到远程的supervisord,当然在本机上面是通过UNIX socket连接的,远程是通过TCP socket连接的。supervisorctl和supervisord之间的通信,是通过xml_rpc完成的。 相应的配置在[supervisorctl]块里面

3、web server
Web Server主要可以在界面上管理进程,Web Server其实是通过XML_RPC来实现的,可以向supervisor请求数据,也可以控制supervisor及子进程。配置在[inet_http_server]块里面

4、XML_RPC
远程调用的,上面的supervisorctl和Web Server就它实现

3、Nginx

现在已经完成了Web sever的部署,但还需要一个代理服务器处理静态资源,以及负载均衡。
这里使用的是著名的nginx。

在配置Nginx之前,需要收集admin下的static文件,执行下面命令:

    python manage.py collectstatic

nginx配置:

我在工程目录下新建了一个dailyblog.conf配置文件,并进行如下基本配置:

server {
    listen      80;
    server_name localhost;
    charset     utf-8;

    error_log /tmp/ninx_error.log;
    access_log /tmp/ninx_access.log;

    location /media {
        alias /home/haibo/study/myworks/dailyblog/media;
    }

    location /static {
        alias /home/haibo/study/myworks/dailyblog/static;
        }

    location / {
        proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_pass http://127.0.0.1:8000; #gunicorn监听的端口
    }

}

配置好之后,将配置文件软连接到nginx配置根目录下 :

ln -s /path/to/your/nginx_conf  /etc/nginx/site-enabled/yournginx.conf

然后执行sudo /etc/init.d/nginx reload重新启动,就可访问http://localhost了。访问这个会跳转到http://127.0.0.1:8000/了。

上面是基本的配置,可以配置的选项还有很多。我第一次是按照官网那么配置的,就是有http,然后在http里面配置server等等信息,但没有成功,不知道为啥,这是应该解决的问题。

1、什么是Nginx?

Nginx是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP服务器.Nginx是一款轻量级的Web服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器.
目前使用的最多的web服务器或者代理服务器,像淘宝、新浪、网易、迅雷等都在使用

Nginx更多是用于作为静态资源的处理和负载均衡。

静态资源

当我们访问一个博客文章详情页面时,服务器会接收到下面两种请求:

  1. 显示文章的详情信息,这些信息通常保存在数据库里,因此需要调用数据库获取数据。
  2. 图片、css、js 等存在服务器某个文件夹下的静态文件。

对于前一种请求,博客文章的数据需要借助 Django 从数据库中获取,Nginx 处理不了,它就会把这个请求转发给 Django,让 Django 去处理。而对于后一种静态文件的请求,只需要去这些静态文件所在的文件夹获取,Nginx 就会代为处理直接处理。

用 Django 去获取静态文件是很耗时的,但 Nginx 可以很高效地处理,这就是我们要使用 Nginx 的原因(当然其功能远不止这些)。

负载均衡

负载均衡即是代理服务器将接收的请求均衡的分发到各服务器中 负载均衡主要解决网络拥塞问题,提高服务器响应速度,服务就近提供,达到更好的访问质量,减少后台服务器大并发压力。

4.fabric

利用fabric是一个自动化管理工具,利用fabric可以避免我们重复敲一些代码。fabric通过ssh进行远程连接,非常方便。

下面是我项目的fabfile文件代码。

_TAR_FILE = 'dailyblog.www.tar.gz'


def pack():
    includes = ['blog','dailyblog','media','static','utils','templates','*.conf','*.py','requirements.txt','start_local.sh']
    excludes = ['*.pyc','*.pyw']
    local('rm -f dist/%s' % _TAR_FILE)
    with lcd(os.path.join(os.path.abspath('.'),'www')):
        cmd = ['tar','--dereference','-zcvf','../dist/%s' % _TAR_FILE]
        cmd.extend(['--exclude=\'%s\'' % ex for ex in excludes])
        cmd.extend(includes)
        local(' '.join(cmd))



_REMOTE_TMP_TARFILE = '/tmp/%s' % _TAR_FILE
_REMOTE_BASE_DIR = '/srv/dailyblog'

def deploy(first=False):
    new_dir = 'www-%s' % datetime.now().strftime('%y-%m-%d_%H-%M-%S')
    #upload tar file
    put('dist/%s' % _TAR_FILE,_REMOTE_TMP_TARFILE)
    with cd(_REMOTE_BASE_DIR):
        sudo('mkdir %s' % new_dir)
    #unpack tar file
    with cd('%s/%s' % (_REMOTE_BASE_DIR,new_dir)):
        sudo('tar -zxvf %s' % _REMOTE_TMP_TARFILE)
    #create sot link for app project'
    with cd(_REMOTE_BASE_DIR):
        sudo('rm -rf www')
        sudo('ln -s %s www' % new_dir)
        sudo('chown haibo:haibo www')
        sudo('chown -R haibo:haibo %s' % new_dir)

    if first:
        #create vitural enviroment
        witdh cd('%s' % _REMOTE_BASE_DIR):
            run('pip install virtualenv')
            run('virtualenv env')

        run('source %s/env/bin/activate' % _REMOTE_BASE_DIR)
        with cd('%s/%s' % (_REMOTE_BASE_DIR,'www')):
            run('pip install -r requirements.txt')
            sudo('ln -s dailyblog.conf /etc/nginx/site-enabled/dailyblog.conf')
            run('supervisord -c supervisor.conf')
            sudo('/etc/init.d/nginx reload')
--------EOF---------
微信分享/微信扫码阅读