宝塔面板 Python 升级失败复盘
背景
服务器宝塔面板一直用 Python 3.7.8(2018年发布,2023年已 EOL),运行一段时间后偶发 segfault 崩溃。dmesg 日志确认:python3[pid]: segfault at a9 ... error 4 in python3.7。
虽然挂了有守护脚本自动拉起来(最长 60s 恢复),但决定从源头解决——把面板的 pyenv 从 3.7 升级到 3.10。
过程:看似顺利,实则死胡同
第一步:装包(成功)
用系统自带的 Python 3.10 创建 venv,从旧环境 pip freeze 导出 156 个包逐个安装。遇到两个小坑,都解决了:
- PyYAML 编译失败: 新版 setuptools 不兼容旧版 Cython,编译 PyYAML 源码时报错。解决:单独装 wheel 版
pip install pyyaml --only-binary=:all: - gevent 导入报错:
ModuleNotFoundError: pkg_resources——新版 setuptools 去掉了这个模块,但 gevent 还依赖它。解决:setuptools 降级到 69.5.1
最终 161 个包装完,仅 PyYAML 版本差异(5.4→6.0.3,向下兼容)。
第二步:启动测试(成功了一半)
面板顺利启动,HTTPS 返回 200。所有 Python 文件正常运行,Flask + gevent + pyinotify 全部工作正常。
第三步:用户访问报错(失败)
打开面板首页就报了 ImportError: PluginLoader.so: undefined symbol: _Py_CheckRecursionLimit
PluginLoader.so 是宝塔面板的一个闭源 Cython 二进制扩展,负责插件加载、授权验证等功能。它是在 Python 3.7 下编译的,硬编码了 Python 3.7 C API 的内部结构体偏移量。Python 3.10 移除了 _Py_CheckRecursionLimit 这个内部变量。
第四步:尝试抢救(失败)
- bt 16 修复面板: 重新下载了 11.6.0 完整包——但包里的 PluginLoader 依然只有 Python 3.7 版本
- LD_PRELOAD 补符号: 写了一个 C 共享库提供
_Py_CheckRecursionLimit符号——符号是补上了,但 Python 3.7 和 3.10 的 PyThreadState 结构体布局不一致,直接段错误
根因
PluginLoader.so 是闭源的 Cython 扩展,编译时绑定了 Python 3.7 的 C API 结构体。没有源码就无法重编译。而宝塔官方并不提供 Python 3.10 版本的 PluginLoader。
即使把 Python 3.7→3.9(用户问过),结果也一样——3.8 开始 CPython 内部结构体就变了。
最终方案
- 回滚到 Python 3.7.8(秒级恢复,备份一直在)
- 守护脚本兜底:crontab 每分钟检测
pidof -x BT-Panel,挂了自动拉 - 可选增强:每天凌晨 4 点定时重启面板,不等它自然 segfault
教训
宝塔面板的闭源二进制组件(PluginLoader.so)是升级 Python 版本的死锁。面板代码本身是纯 Python 完全兼容 3.10,但一个闭源的 .so 卡死了全局。要么等官方出 Python 3.10 版本,要么不动它。
— 随趣科技 · 2026-05-22