最近,州的先生在对 MrDoc 专业版(一个基于 Django 开发的在线文档系统)进行最后的上线测试工作。
原本在开发环境下测试得好好的「一键更新」功能,在宝塔面板上通过 uWSGI 部署之后,突然不能使用了。
期间各种搜索、改代码、测试,简直快要崩溃,幸运的是,最终问题得以解决,下面回顾一下这些问题及其解决方法,方便大家借鉴和避坑。
环境
开发环境:
- 操作系统:Windows 10
- Python版本:3.6
- 虚拟环境:否
- 运行方式:runserver
测试环境:
- 操作系统:CentOS 7
- Python 版本:3.7.8
- 虚拟环境:是
- 运行方式:宝塔面板 uWSGI Nginx
功能逻辑
在 Django 内通过 subprocess.Popen() 方法,调用系统命令,拉取最新的 Git 仓库代码,然后执行 Django 的数据迁移命令,最后执行 pip 命令更新项目依赖库。
在原代码中,已经对 subprocess.Popen() 方法进行了一定的优化和处理。
首先,通过cwd
参数指定命令运行的路径,通过stdin
、stderr
、stdout
参数获取命令执行的输出内容,通过communicate()
等待命令执行完成。
最终的脚本调用代码如下所示:
代码语言:javascript复制Popen(
[python_env, 'manage.py', 'makemigrations'],
stdin=PIPE,
stderr=PIPE,
stdout=PIPE,
cwd=settings.BASE_DIR
)
out_2, err_2 = mig_call.communicate()
Git 仓库认证问题
最开始出现的是 Git 认证问题。
仓库的代码通过 https 的方式进行拉取:
代码语言:javascript复制git clone https://git.mrdoc.pro/MrDoc/MrDocPro.git
正常情况下,在命令行终端输入 git 相关命令可以直接进行操作。
但是日志里面却报出了一个错误:
代码语言:javascript复制fatal: could not read Username for 'https://git.mrdoc.pro'
试过使用:
代码语言:javascript复制git config --global credential.helper store
这在命令行界面启动 uWSGI 时运行没问题:
代码语言:javascript复制uwsgi --ini uwsgi.ini
但是一旦使用宝塔面板的「Python 项目管理器」启动,就会继续报上述错误:
最后通过修改 git 仓库地址,在地址上附带用户名和密码得以解决:
代码语言:javascript复制git clone https://{username}:{password}@git.mrdoc.pro/MrDoc/MrDocPro.git
Python 解释器路径问题
因为需要执行 Django 的数据迁移命令,所以需要在脚本中调用Python
:
python manage.py migrate
这在单一的 Python 环境下,也是没有任何问题的。但是在 Linux 上,在虚拟环境中,这个问题就凸现出来了。
首先很多服务器使用的 Linux 还是自带了 Python2,且默认的python
命令指向的也是 Python 2,就算将其指向到 Python3。
其次,全局 Python 的第三方模块和虚拟环境内的第三方模块也不一样。
为了让代码里面执行的python
命令指向正确的 Python 解释器路径,州的先生使用了 Python 内置库 sys
提供的executable
属性,这个属性会返回 Python 解释器的可执行二进制文件的绝对路径。
例如:
虚拟环境中:
按理来说,用 sys.executable 代替python
是不会错的了,结果又报出个错误来:
unable to load configuration from manage.py
网上搜索不到合适的问题,只得自己调试,在代码中把 sys.executable 的值打印出来,才发现 sys.executable 指向了 虚拟环境内 uWSGI 的路径。
借着这一关键信息搜索,才发现这一问题在 2014年就已经存在:
而开发者的解释是:
uwsgi 就是当前的 python 解释器,因为它链接 libpython.so。获取 sys.executable 是不可能的,因为库本身没有硬编码的二进制路径。
好吧。知道原因就好办了。虚拟环境内的 uWSGI 一般都存在于虚拟环境python的同级目录,在调用 sys.executable 之前对其返回值进行一下判断。
如果值以uwsgi
结尾,就可以将其替换掉,如果是正常的Python解释器路径,就可以直接使用。
总结
当问题解决了,回想起来,好像解决方法也是很简单。
这关键在于,出现问题后,要及时定位到问题点所在。
如果不知道问题点所在,就只能盲目地找答案,而这,往往是找不到答案的。
解决问题的方法可能很简单,但是如果找不到问题点,再简单的解决方法可能也不会被自己注意到,最终就像电视剧里面的两个人,那么近又那么远,最终擦肩而过。