部署Django站点到Heroku时psycopg2初始化报错原因排查
错误日志
2023-05-05T08:42:38.330672+00:00 app[web.1]: from psycopg2._psycopg import ( # noqa
2023-05-05T08:42:38.330672+00:00 app[web.1]: SystemError: initialization of _psycopg raised unreported exception
2023-05-05T08:42:38.330718+00:00 app[web.1]: [2023-05-05 08:42:38 +0000] [8] [INFO] Worker exiting (pid: 8)
2023-05-05T08:42:38.367087+00:00 app[web.1]: [2023-05-05 08:42:38 +0000] [2] [WARNING] Worker with pid 8 was terminated due to signal 15
2023-05-05T08:42:38.465035+00:00 app[web.1]: [2023-05-05 08:42:38 +0000] [2] [INFO] Shutting down: Master
2023-05-05T08:42:38.465071+00:00 app[web.1]: [2023-05-05 08:42:38 +0000] [2] [INFO] Reason: Worker failed to boot.
2023-05-05T08:42:38.616316+00:00 heroku[web.1]: Process exited with status 3
2023-05-05T08:42:38.666808+00:00 heroku[web.1]: State changed from starting to crashed
2023-05-05T08:42:39.000000+00:00 app[api]: Build succeeded
2023-05-05T08:42:44.863150+00:00 heroku[router]: at=error code=H10 desc="App crashed" method=GET path="/" host=a-coders-journey.herokuapp.com request_id=d8e2f6b2-c4f5-461f-8914-a8e84dc13551 fwd="93.204.33.78" dyno= connect= service= status=503 bytes= protocol=https
当前配置与已尝试操作
requirements.txt内容
# # This file is autogenerated by pip-compile with Python 3.8 # by the following command: # # pip-compile --output-file=requirements.txt requirements.in # asgiref==3.3.4 # via # -r requirements.in # django certifi==2022.12.7 # via # cloudinary # requests cffi==1.15.1 # via cryptography charset-normalizer==3.1.0 # via requests cloudinary==1.25.0 # via # -r requirements.in # dj3-cloudinary-storage cryptography==3.4.8 # via # -r requirements.in # pyjwt defusedxml==0.7.1 # via python3-openid dj-database-url==0.5.0 # via -r requirements.in dj3-cloudinary-storage==0.0.5 # via -r requirements.in django==3.2.3 # via # -r requirements.in # django-allauth # django-summernote django-allauth==0.44.0 # via -r requirements.in django-crispy-forms==1.11.2 # via -r requirements.in django-summernote==0.8.11.6 # via -r requirements.in gunicorn==20.1.0 # via -r requirements.in idna==3.4 # via requests oauthlib==3.1.1 # via # -r requirements.in # requests-oauthlib pillow==9.5.0 # via -r requirements.in psycopg2-binary # via -r requirements.in pycparser==2.21 # via cffi pyjwt[crypto]==2.1.0 # via # -r requirements.in # django-allauth python3-openid==3.2.0 # via # -r requirements.in # django-allauth pytz==2021.1 # via # -r requirements.in # django requests==2.30.0 # via # dj3-cloudinary-storage # django-allauth # requests-oauthlib requests-oauthlib==1.3.0 # via # -r requirements.in # django-allauth six==1.16.0 # via cloudinary sqlparse==0.4.1 # via # -r requirements.in # django urllib3==2.0.2 # via # cloudinary # requests # The following packages are considered to be unsafe in a requirements file: # setuptools
settings.py相关配置
from pathlib import Path import os import psycopg2 import dj_database_url if os.path.isfile('env.py'): import env # ...其他代码... DATABASE_URL = os.environ['DATABASE_URL'] conn = psycopg2.connect(DATABASE_URL, sslmode='require') # 文件末尾 DATABASES['default'] = dj_database_url.config(conn_max_age=600, ssl_require=True)
已尝试操作
- 确认requirements.txt包含
psycopg2-binary - 按Heroku文档配置数据库
- 添加
runtime.txt尝试统一Python版本(本地3.8.12,Heroku原用3.11.3) - 验证env.py和Heroku配置变量指向elephantSQL数据库
- 联系Heroku支持但问题未解决
疑问
哪里操作有误?为何psycopg2初始化时会报错?
解决方案
移除settings.py中手动建立psycopg2连接的代码
Django通过dj_database_url.config()配置后会自动管理数据库连接,无需手动调用psycopg2.connect()。提前执行该代码会导致Django启动时过早加载psycopg2底层库,此时环境未完全初始化,引发未上报的初始化异常。删除以下两行:DATABASE_URL = os.environ['DATABASE_URL'] conn = psycopg2.connect(DATABASE_URL, sslmode='require')锁定psycopg2-binary的兼容版本
当前requirements.txt中psycopg2-binary未指定版本,pip会拉取最新版本,但最新版本可能与Python 3.8不兼容。将其修改为psycopg2-binary==2.9.6(该版本经验证可稳定运行在Python 3.8和Heroku环境),之后重新运行pip-compile生成新的requirements.txt。强制Heroku使用与本地一致的Python版本
确保runtime.txt内容为python-3.8.12,避免Heroku默认使用高版本Python(如3.11)导致依赖编译不兼容。清理Heroku缓存并重新部署
旧缓存可能残留不兼容的依赖编译文件,执行以下命令清理后重新推送代码:heroku apps:clear-cache -a a-coders-journey git add . git commit -m "Fix psycopg2 initialization error" git push heroku main
内容的提问来源于stack exchange,提问作者ENJ

