前言
本人是首次部署后端,很不幸也是第一次接触后端,正好入门也是Python的flask
而且我的工程用了下面这么内容: flask,celery,redis,mysq,migirate等
错误的尝试
- pip install uwgsi 显示编译 #include "Python.h"的时候报错了,放弃了
- 通过pip install virtualenv ,virtualenvwrapper创建虚拟环境,安装requirements,然后跑了python main.py runserver发现是一个开发环境(development),并不是生产环境(production),也就是说属于flask自带的,用于调试的,不推荐作为部署使用,而且还有一个问题就是部署的时候跑了这个指令整个命令行都是监控命令,意思是你关了然后你的部署也算是走到了尽头
- 偶然发现了gunicorn部署,发现大家都在用这个,也就是绿色独角兽,如果直接跑gunicorn -w 4 -b 0.0.0.0:5000 他也能跑,但是问题是跟你2一摸一样的问题
- 后来发现nohup可以创建一个进程让gunicorn在后台跑,然后也挺好,但是呢你每次部署需要grep到进程,然后kill掉他,然后重新跑,麻烦事也是一堆
- 后来发现网上大家更多的是通过supervisor来管理,同时也能管理gunicorn,celery,毕竟celery也要单独跑指令,也是麻烦的很
所以最终的方案是选择: supervisor gunicorn flask celery
创建虚拟环境
- linux的建议先去把python python3 pip pip3 弄明白了再来
- 该安装的安装,该升级的升级
- 把代码上传到git,然后通过git拉取到你的linux上
- cd到你仓库的根目录下
- 创建虚拟环境
python -m venv venv
(虚拟环境创建到哪自己决定,建议跟项目创一块去) - 激活虚拟环境
source venv/bin/activate
- 然后会发现命令行前多了
(venv) #
这说明已经进入了虚拟环境 - 在虚拟环境下(venv)
pip install -r requirements.txt
- 安装
pip install gunicorn
本文重点讲解supervisor的踩坑
重点是supervisor,当然如何创建虚拟环境,如何避免掉坑也会讲解,对于falsk,gunicorn,celery的指令,本文会有正确的例子,但是并不会深度讲解为什么要用这个指令,本文重点是讲部署
安装supervisor
网上说的是supervisor不支持python3(2019的帖子了,后面尝试的时候我会再回来备注的)
pip install supervisor
深渊巨坑
网上很多教程告诉你下面这个例子
但是你在linux下面cat /etc/supervisord.conf 试试,
压根没这个文件,所以下面踩坑日记告诉你这个文件要安装了以后自己去跑命令生成的
创建目录,初始化配置文件
echo_supervisord_conf > /etc/supervisord.conf
创建的配置文件如下:
[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
接下来我们才可以安心的在项目下面创建supervisor_app.conf
这个配置文件
[include]
files=/etc/supervisord.conf
[program:flask_app]
directory=/home/toolsmtodo/mtodoback
command=gunicorn -w 4 -b 0.0.0.0:5000 main:app
files=/etc/supervisord.conf
就是上面我们初始化执行命令生成的directory=/home/toolsmtodo/mtodoback
这个路径就是我们从git下面clone下来,然后放在linux下面的位置command=gunicorn -w 4 main:app
如果你研究过gunicorn单独跑命令的话你应该知道这个是什么意思,main是py的名字,app是flask的实例化名字 ,-w
是线程,这里还可以添加-b 0.0.0.0:5000
设置host和端口- [program:flask_app] 这里
flask_app
是你的项目名,可以自行设置,后面跑指令的话会用到这个词
启动supervisord:(建议复制,打错了可真是找不到指令,半天摸不到方向)
supervisord -c supervisor_app.conf
启动我们的应用:(满怀期待启动我们的应用,此处国粹)
supervisorctl start flask_app
修改配置
[include]
files=/etc/supervisord.conf
[program:flask_app]
directory=/home/toolsmtodo/mtodoback #项目目录
command=gunicorn -w 4 -b 0.0.0.0:5000 main:app #gunicorn的指令
startsecs=5 #启动5秒后没有异常退出,视作正常启动
autostart=true #在supervisord启动时自动启动
autorestart=true #程序异常退出后重启
redirect_stderr=true #将错误信息重定向至stdout日志
继续执行配置 supervisord -c supervisor_app.conf
报错Error: Another program is already listening on a port that one of our HTTP servers is configured to use. Shut this program down first before starting supervisord.
关掉配置 supervisorctl shutdown
重新配置 supervisord -c supervisor_app.conf
没问题
继续启动 supervisorctl start all
报错 flask_app: ERROR (spawn error)
非常奈斯,继续国粹!
继续CSDN,百度(再三确认,我是production模式,debug早删除了)
supervisor错误:ERROR (spawn error)大佬说使用supervisorctl tail program_name stderr
命令查看错误信息,对于我来说也就是supervisorctl tail flask_app stderr
错误: flask_app: ERROR (no log file)
惊不惊喜,意不意外~
继续修改配置文件
[include]
files=/etc/supervisord.conf
[program:flask_app]
directory=/home/toolsmtodo/mtodoback #项目目录
command=gunicorn -w 4 -b 0.0.0.0:5000 main:app #gunicorn的指令
startsecs=5 #启动5秒后没有异常退出,视作正常启动
autostart=true #在supervisord启动时自动启动
autorestart=true #程序异常退出后重启
redirect_stderr=true #将错误信息重定向至stdout日志
stdout_logfile=/home/toolsmtodo/mtodoback/logs/gunicorn.log # 进程日志
老流程了. 关闭 | 配置 | 启动 | 查询错误
算球,另谋生路吧
输入 supervisorctl tail flask_app stdout
原因终于出来了,原来是找不到模块,结合我目前的例子,我们是直接启动gunicorn,并没有优先去开启虚拟环境,没有让gunicorn指令在虚拟环境中奔跑,所以下一步应该是加上虚拟环境应该会好点.
当我再次修改配置文件后,可以看出(我把command的命令引申到了我的虚拟环境路径) (重点啊同志们)
[include]
files=/etc/supervisord.conf
[program:flask_app]
directory=/home/toolsmtodo/mtodoback #项目目录
command=/home/toolsmtodo/mtodoback/venv/bin/gunicorn -w 4 -b 0.0.0.0:5000 main:app #gunicorn的指令
startsecs=5 #启动5秒后没有异常退出,视作正常启动
autostart=true #在supervisord启动时自动启动
autorestart=true #程序异常退出后重启
redirect_stderr=true #将错误信息重定向至stdout日志
stdout_logfile=/home/toolsmtodo/mtodoback/logs/gunicorn.log # 进程日志
组合拳又来了一波
哎呀 国粹
附上各种指令,说不定你跟我的还不一样
supervisor的常用命令
supervisorctl status # 获取所有进程状态
supervisorctl stop gunicorn # 停止进程
supervisorctl start gunicorn # 启动进程
supervisorctl restart gunicorn # 重启进程,不会重新加载配置文件
supervisorctl reread # 重新加载配置文件,不会新增和删除进程
supervisorctl update # 加载配置文件,会删除和新增进程,并重启受影响的程序
supervisorctl shutdown # 停止supervisord
supervisorctl all # 停止全部进程
面对这种危机,我觉得先执行个supervisorctl status
看看啥情况
那?打开网站试试?
哎哟,国粹,竟然开了
那我们再关关开开试试?
在一声国粹中,完成了工作! 卧槽,已经凌晨1点多了.
接下来是不是得开始搞celery了
那么必然也是配置文件 + 组合拳!
索性就这么配置把,报错了再说报错的事
[include]
files=/etc/supervisord.conf
[program:flask_app]
directory=/home/toolsmtodo/mtodoback #项目目录
command=/home/toolsmtodo/mtodoback/venv/bin/gunicorn -w 4 -b 0.0.0.0:5000 main:app #gunicorn的指令
startsecs=5 #启动5秒后没有异常退出,视作正常启动
autostart=true #在supervisord启动时自动启动
autorestart=true #程序异常退出后重启
redirect_stderr=true #将错误信息重定向至stdout日志
stdout_logfile=/home/toolsmtodo/mtodoback/logs/gunicorn.log # 进程日志
[program:celery_app]
directory=/home/toolsmtodo/mtodoback #项目目录
command=/home/toolsmtodo/mtodoback/venv/bin/celery -A main.celery worker loglevel=info #celery指令
startsecs=5 #启动5秒后没有异常退出,视作正常启动
autostart=true #在supervisord启动时自动启动
autorestart=true #程序异常退出后重启
redirect_stderr=true #将错误信息重定向至stdout日志
stdout_logfile=/home/toolsmtodo/mtodoback/logs/celery.log # 进程日志
这么搞组合拳也太累了,搞个脚本来代替 我这套组合拳得了
vim supervisor_changeconfig.sh
#!/bin/bash
supervisorctl stop all
supervisorctl shutdown
supervisord -c supervisor_app.conf
supervisorctl start flask_app
supervisorctl status
chmod u+x supervisor_changeconfig.sh
然后执行就完了
果不其然,出错了
在执行下脚本
果然快多了哈
信息果然多了点
意外发现
国粹,这脚本也白写了 (巨坑,希望各位伙伴谨记教训)
国粹.不知道怎么看, 想起来我们不是设置log了嘛,去试试,真有惊喜
多么低级的错误啊
重新加载配置文件,重新启动正在运行的服务:
$sudo supervisorctl reload
重新加载修改过的配置并重启该服务:
$sudo supervisorctl reread
$sudo supervisorctl update
非常的奈斯 ,一套部署这不就下来了