每次写好一个博客之类的项目,都想把它部署在我的云服务器上,这样就可以方便访问,但每次部署都容易掉了莫名其妙的坑里面,总是要谷歌各种问题才能彻底解决。所以这篇文章当做以后部署的笔记使用,免得每次都要查很多资料。
一、部署所需条件
首先你要部署项目的话,你得先有个服务器,一般来说只要你电脑装了Liunx系统都行,只要你不关机。但最好还是花钱买个云服务器使用,现在国内的阿里云、腾讯云都不错,而且是学生的话还有学生优惠。先贴出部署前提条件
- Liunx环境 (我使用的是Ubuntu系统)
- Python2.7
- Django1.8 (注版本不一样的可能需要改一些配置)
- Virtualenv (Virtualenv是一个Python虚拟环境管理的工具)
- Supervisor (Supervisor是一个客户端/服务器系统,允许其用户在类UNIX操作系统上控制多个进程)
- Nginx (Nginx 是一个高性能的 Web 和反向代理服务器)
我使用的是美团云服务器Ubuntu16.04来实现整个部署过程,如果对以上列举的工具不熟悉的话,请自己去谷歌文档看看,这里就简单概括下部署的流程,首先把以上环境搭建好,并根据需要配置好所需的环境。其次就可以Supervisor来管理进程,Nginx是用来反向代理的,把对域名的访问反向到Django的进程中,下面会具体地说。
二、 安装Python及Django依赖包
安装Python环境
- 我使用的Ubuntu系统已经预装了Python2.7和Python3.4。其它的Liunx系统如CentOS, Fedora, Red Hat也预装了Python2.7。如果没有的话,请谷歌找一下对应系统的安装教程,一般都是通过命令行就可以安装了。
安装Django框架
-
一般安装Django使用的是Python的包管理工具pip。如果没有pip的话,ubuntu系统可以通过:
$ sudo apt-get install python-pip
然后就可以愉快的使用pip安装python的各种扩展包了,安装Django可以使用
pip install Django==1.8
就这么简单搭建后安装了Django,如果没有指定版本号,默认就是最新的版本。不过这里我们先不需要使用这条语句安装Django,因为这里存在一个问题就是,pip安装的Django是全局有效的,如果你的服务器有其它项目依赖于Django的其它版本,这样安装的话就会导致其它项目直接崩盘了, 这就需要一个虚拟环境让你的Web应用的Python环境从系统的python环境直接隔开来。
安装virtualenv
-
virtualenv是一个Python虚拟环境管理的工具,作用是把你项目的python环境和系统全局的python环境隔开来,这样你就可以在不同的项目中安装不同版本的第三方库。在Ubuntu系统下安装virtualenv,可以使用pip安装或者apt-get安装:
$ pip install virtualenv $ sudo apt-get install python-virtualenv
三、virtualenv使用
在你项目根目录下执行下面语句:
$ cd /home/guoweikuang/django_web # cd到你项目目录下
$ virtualenv virtualenv #创建虚拟环境,会在当前的目录中创建一个文件夹,包含了Python可执行文件, 以及 pip 库的一份拷贝
$ source virtualenv/bin/activate # 激活虚拟环境
然后你会看到下面的命令行前面有虚拟环境的名字:
(virtualenv) ➜ guoweikuang
如果要退出虚拟环境,你可以使用:
$ deactivate
在虚拟环境下pip安装的和系统全局的python库是隔开的,如果你不想一条一条pip安装第三方库的话,你可以使用
$ pip install -r requirements.txt
其中requirements.txt里面内容是你项目所需的第三方库的名字和版本号,你可以使用
$ pip freeze > requirements.txt
来生成requirements.txt文件,前提是你之前要是没有requirements.txt文件的话,你需要一条一条的pip,但是有这个文件后你迁移到其它地方安装第三方库也只需要 pip install -r requirements.txt 就可以了。
四、Supervisor安装配置
Superviosr是一个进程管理工具,可以保证你的程序在服务器开机时自动启动以及程序意外终止时重新启动。当你的项目有多个并且需要同时启动或关闭,使用Supervisor一行命令都可以同时重启,而不是一个一个命令地重启。
supervisor 主要由两部分组成:
supervisord(server 部分):主要负责管理子进程,响应客户端命令以及日志的输出等;
supervisorctl(client 部分):命令行客户端,用户可以通过它与不同的 supervisord 进程联系,获取子进程的状态等。
首先,可以通过pip安装supervisor或者apt-get安装:
$ sudo pip install supervisor
$ sudo apt-get install supervisor
安装完 supervisor 之后,可以运行echo_supervisord_conf 命令输出默认的配置项,也可以重定向到一个配置文件里:
echo_supervisord_conf > /etc/supervisord.conf
Supervisor提供了很多配置给我们使用。首先来看 supervisord 的配置文件:
1、supervisord(这是 server 端,对应的有 client 端:supervisorctl)
2、应用程序(即我们要管理的程序)。
首先来看 supervisord 的配置文件。去除里面大部分注释和“不相关”的部分,我们可以先看这些配置:
[unix_http_server]
file=/tmp/supervisor.sock ; UNIX socket 文件,supervisorctl 会使用
;chmod=0700 ; socket 文件的 mode,默认是 0700
;chown=nobody:nogroup ; socket 文件的 owner,格式: uid:gid
;[inet_http_server] ; HTTP 服务器,提供 web 管理界面
;port=127.0.0.1:9001 ; Web 管理后台运行的 IP 和端口,如果开放到公网,需要注意安全性
;username=user ; 登录管理后台的用户名
;password=123 ; 登录管理后台的密码
[supervisord]
logfile=/tmp/supervisord.log ; 日志文件,默认是 $CWD/supervisord.log
logfile_maxbytes=50MB ; 日志文件大小,超出会 rotate,默认 50MB
logfile_backups=10 ; 日志文件保留备份数量默认 10
loglevel=info ; 日志级别,默认 info,其它: debug,warn,trace
pidfile=/tmp/supervisord.pid ; pid 文件
nodaemon=false ; 是否在前台启动,默认是 false,即以 daemon 的方式启动
minfds=1024 ; 可以打开的文件描述符的最小值,默认 1024
minprocs=200 ; 可以打开的进程数的最小值,默认 200
; the below section must remain in the config file for RPC
; (supervisorctl/web interface) to work, additional interfaces may be
; added by defining them in separate rpcinterface: sections
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
serverurl=unix:///tmp/supervisor.sock ; 通过 UNIX socket 连接 supervisord,路径与 unix_http_server 部分的 file 一致
;serverurl=http://127.0.0.1:9001 ; 通过 HTTP 的方式连接 supervisord
; 包含其他的配置文件
[include]
files = relative/directory/*.ini ; 可以是 *.conf 或 *.ini
里面需要注意的是配置
[include]
files= /etc/supervisor/conf.d/*.conf ; 你项目的配置文件所在地方
然后启动supervisord, 通过 -c选项指定配置文件路径:
supervisord -c /etc/supervisord.conf
查看 supervisord 是否在运行:
ps aux | grep supervisord
我们可以看到 supervisord 已经被启动了, 然后进入 supervisorctl 的 shell 界面:
$ supervisorctl
django_web RUNNING pid 58261, uptime 3:21:25
supervisor>status
可以看到如果有进程运行的后会显示状态信息之类,进程ID,启动时间等。
接下来就添加一个需要管理的进程:
按照官方文档的定义,一个 [program:x] 实际上是表示一组相同特征或同类的进程组,也就是说一个 [program:x] 可以启动多个进程。这组进程的成员是通过 numprocs 和 process_name 这两个参数来确定的.
下面以我部署的django_web进程配置为例:
[program:django_web]
directory = /home/guoweikuang/django_web ;程序放置目录
#command = gunicorn django_blog.wsgi:application -b 127.0.0.1:8090
command = runinenv /home/guoweikuang/virtualenv python manage.py runserver 127.0.0.1:8080
# command = sudo /home/guoweikuang/virtualenv/bin/gunicorn django_blog.wsgi:application -b 127.0.0.1:8080 ; 启动命令,和在命令行模式下都是一样的
#autostart = true ; 在supervisord启动时自动启动
user=www-data ; 使用哪个用户启动进程
startsecs = 5 ; 启动5秒后没有异常就相当于正常启动
startretries = 3 ; 启动失败后自动重试次数,默认为3
stopsignal = KILL ; 下面有解释
stopasgroup = true
redirect_stderr = true ; 把stderr重定向到stdout, 默认为false
stdout_logfile = /var/log/supervisor/django_web.log ; 日志文件,方便查看
stderr_logfile = /var/log/supervisor/django_web_error.log
Supervisorctl命令介绍
# 停止某一个进程,program_name 为 [program:x] 里的 x
supervisorctl stop program_name
# 启动某个进程
supervisorctl start program_name
# 重启某个进程
supervisorctl restart program_name
# 结束所有属于名为 groupworker 这个分组的进程 (start,restart 同理)
supervisorctl stop groupworker:
# 结束 groupworker:name1 这个进程 (start,restart 同理)
supervisorctl stop groupworker:name1
# 停止全部进程,注:start、restart、stop 都不会载入最新的配置文件
supervisorctl stop all
# 载入最新的配置文件,停止原有进程并按新的配置启动、管理所有进程
supervisorctl reload
# 根据最新的配置文件,启动新配置或有改动的进程,配置没有改动的进程不会受影响而重启
supervisorctl update
五、Supervisor容易出现的问题
一、管理的进程会产生新的进程时, 会遇到执行完stop命令后子进程依然在: 这是我使用supervisor启动了一个django, 可以看到有俩进程:
www-data 102180 2.2 3.1 80760 31312 ? S 08:38 0:00 python manage.py runserver 127.0.0.1:8080
www-data 102185 3.4 3.6 156932 36140 ? Sl 08:38 0:00 /home/guoweikuang/virtualenv/bin/python manage.py runserver 127.0.0.1:8080
当你stop进程时,使用ps aux | less 查看进程时你会看到还有下面这个进程没有停止:
www-data 102185 1.1 3.6 156932 36140 ? Sl 08:38 0:02 /home/guoweikuang/virtualenv/bin/python manage.py runserver 127.0.0.1:8080
为了关闭子进程, 这里需要介绍两个配置项 stopasgroup 和 killasgroup:
; 默认为 false,如果设置为 true,当进程收到 stop 信号时,会自动将该信号发给该进程的子进程。如果这个配置项为 true,那么也隐含 killasgroup 为 true。例如在 Debug 模式使用 Flask 时,Flask 不会将接收到的 stop 信号也传递给它的子进程,因此就需要设置这个配置项。
stopasgroup=false ; send stop signal to the UNIX process
; 默认为 false,如果设置为 true,当进程收到 kill 信号时,会自动将该信号发给该进程的子进程。如果这个程序使用了 python 的 multiprocessing 时,就能自动停止它的子线程。
killasgroup=false ; SIGKILL the UNIX process group (def false)
当增加 stopasgroup=true 配置后, 父进程关闭子进程也就关掉了.
二、需要注意的是权限问题,如果出现:
error: , [Errno 13] Permission denied: file: /usr/local/lib/python2.7/socket.py line: 228在[Permession denied error when use supervisorctl #173][1]
解决方法:就是在supervisod.conf文件中修改权限为0766:
[unix_http_server]
file=/tmp/supervisor.sock ; (the path to the socket file)
chmod=0766 ; socket file mode (default 0700)
最后别忘了重启supervisor使其生效:
sudo service supervisor restart