我正在Heroku上用uWSGI、管理员和nginx运行Docker映像中的Django应用程序。
当应用程序缩小时,我经常收到H13(连接关闭,无响应)错误:
此问题会生成以下日志事件:
2022-10-12T09:35:13.231318+00:00 heroku web.3 - - State changed from up to down
2022-10-12T09:35:13.774228+00:00 heroku web.3 - - Stopping all processes with SIGTERM
2022-10-12T09:35:14.028602+00:00 heroku router - - at=error code=H13 desc="Connection closed without response" method=GET path="/comments/api/assets-uuidasset/xxxx-xxxx-xxxx-xxxx-xxxxx/count/?_=1665564563"
我认为问题在于套接字没有在SIGTERM信号上关闭,或者nginx使用SIGTERM信号不正常地关闭(它应该接收SIGQUIT以正常关闭),或者类似的问题。
第一个案例在本文中描述,是关于Puma和Ruby的:https://www.schneems.com/2019/07/12/puma-4-hammering-out-h13sa-debugging-story/
第二种情况描述如下:https://canonical.com/blog/avoiding-dropped-connections-in-nginx-containers-with-stopsignal-sigquit
1条答案
按热度按时间dldeef671#
经过三个星期的工作,我终于能够解决这个问题。
简短答案:
尽量避免使用Heroku运行Docker映像。
Heroku将
SIGTERM
发送到dyno中的所有进程,这是非常难以处理的事情。您将需要修补Docker容器中的几乎每个进程,以便使用SIGTERM
计数并顺利终止。终止Docker容器的标准方法是使用
docker stop
命令,该命令仅将SIGTERM
发送到根进程(入口点),在那里可以处理它。Heroku有一个非常任意的过程来终止与现有应用程序以及现有Docker映像部署不兼容的示例。根据我与Heroku的沟通,他们在未来无法改变这一点。
长答案:
不是一个问题,而是5个不同的问题。要成功终止示例,需要满足以下条件:
docker-entrypoint.sh
需要捕捉SIGTERM
信号。当我在本地测试时,这并没有出现。为了实现这一点,我不得不单独处理每个应用程序:
Nginx:
我不得不修补Nginx来切换
SIGTERM
和SIGQUIT
信号,所以我在我的Dockerfile中运行以下命令:Issue I created
uWSGI/独角兽:
我放弃了uWSGI并切换到Gunicorn(它在
SIGTERM
上优雅地终止),但最后我不得不修补它,因为它需要比Nginx晚终止。我禁用了SIGTERM
信号并将其功能Map到SIGUSR1
上我的修补版本在这里:https://github.com/PetrDlouhy/gunicorn/commit/1414112358f445ce714c5d4f572d78172b993b79我安装了:
Issue I created
PG弹跳机:
我还部署了PGBouncer,我必须修改它,使其在
SIGTERM
上不起作用:它仍然可以用
SIGINT
优雅地关闭。Issue I created
第1001章:我的docker-entrypoint.sh
我不得不在我的
docker-entrypoint.sh
中捕获SIGTERM
,其中包含:主管
为了不接收
R12
错误,所有进程都需要在30秒Heroku宽限期之前终止。我通过以下supervisord.conf
实现了这一点:测试溶液:
为了测试正在发生的事情,我必须开发一些测试技术,这些技术在不同但相似的情况下可能会很方便。
我创建了一个视图,它等待10秒后才回答,并将其绑定到
/slow_view
url上。然后,我在Docker示例中启动服务器,使用
curl -I "http://localhost:8080/slow_view"
查询慢视图,并与Docker示例建立第二个连接,使用pkill -SIGTERM .
或pkill -SIGTERM gunicorn
执行kill命令。我还可以在测试Heroku dyno时运行kill命令,其中我连接了
heroku ps:exec --dyno web.1 --app my_app
。