简体   繁体   English

Python如何在Docker中接收SIGINT以停止服务?

[英]Python how to recieve SIGINT in Docker to stop service?

I'm writing a monitor service in Python that monitors another service and while the monitor & scheduling part works fine, I have a hard time figuring out how to do a proper shutdown of the service using a SIGINT signal send to the Docker container.我正在用 Python 编写监控另一个服务的监控服务,虽然监控和调度部分工作正常,但我很难弄清楚如何使用发送到 Docker 容器的SIGINT信号正确关闭服务。 Specifically, the service should catch the SIGINT from either a docker stop or a Kubernetes stop signal, but so far it doesn't.具体来说,服务应该从 docker stop 或 Kubernetes stop 信号中捕获SIGINT ,但到目前为止还没有。 I have reduced the issue to a minimal test case which is easy to replicate in Docker:我已将问题简化为一个易于在 Docker 中复制的最小测试用例:

import signal
import sys
import time

class MainApp:

    def __init__(self):
        self.shutdown = False
        signal.signal(signal.SIGINT, self.exit_gracefully)
        signal.signal(signal.SIGTERM, self.exit_gracefully)

    def exit_gracefully(self, signum, frame):
        print('Received:', signum)
        self.shutdown = True

    def start(self):
        print("Start app")

    def run(self):
        print("Running app")
        time.sleep(1)

    def stop(self):
        print("Stop app")

if __name__ == '__main__':

    app = MainApp()

    app.start()

    # This boolean flag should flip to false when a SIGINT or SIGTERM comes in...
    while not app.shutdown:
        app.run()

    else: # However, this code gets never executed ...
        app.stop()
        sys.exit(0)

And the corresponding Dockerfile, again minimalistic:以及相应的 Dockerfile,同样是简约的:

FROM python:3.8-slim-buster
COPY test/TestGS.py .
STOPSIGNAL SIGINT
CMD [ "python", "TestGS.py" ]

I opted for Docker because the Docker stop command is documented to issue a SIGINT signal, waits a bit, and then issues a SIGKILL.我选择了 Docker,因为Docker stop 命令被记录为发出 SIGINT 信号,稍等片刻,然后发出 SIGKILL。 This should be an ideal test case.这应该是一个理想的测试用例。

However, when starting the docker container with an interactive shell attached, and stopping the container from a second shell, the stop() code never gets executed.但是,当启动带有交互式 shell 的 docker 容器并从第二个 shell 停止容器时,stop() 代码永远不会被执行。 Verifying the issue, a simple:验证问题,一个简单的:

$ docker inspect -f '{{.State.ExitCode}}' 64d39c3b

Shows exit code 137 instead of exit code 0.显示退出代码 137 而不是退出代码 0。

Apparently, one of two things is happening.显然,发生了两件事之一。 Either the SIGTERM signal isn't propagated into the container or Python runtime and this might be true because the exit_gracefully function isn't called apparently otherwise we would see the printout of the signal. SIGTERM信号没有传播到容器或 Python 运行时,这可能是真的,因为 exit_gracefully 函数没有被明显调用,否则我们会看到信号的打印输出。 I know that you have to be careful about how to start your code from within Docker to actually get a SIGINT , but when adding the stop signal line to the Dockerfile, a global SIGINT should be issued to the container, at least to my humble understanding reading the docs.我知道你必须小心如何从Docker 中启动你的代码来实际获得SIGINT ,但是当将停止信号线添加到 Dockerfile 时,应该向容器发出全局SIGINT ,至少以我的拙见阅读文档。

Or, the Python code I wrote isn't catching any signal at all.或者,我编写的 Python 代码根本没有捕捉到任何信号。 Either way, I simply cannot figure out why the stop code never gets called.无论哪种方式,我都无法弄清楚为什么停止代码永远不会被调用。 I spent a fair amount of time researching the web, but at this point, I feel I'm running circles, Any idea how to solve the issue of correctly ending a python script running inside docker using a SIGINT signal?我花了很多时间研究网络,但在这一点上,我觉得我在绕圈子,知道如何解决使用SIGINT信号正确结束在 docker 内运行的 python 脚本的问题吗?

Thank you谢谢

Marvin马文

Solution:解决方案:

The app must run as PID 1 inside docker to receive a SIGINT.该应用程序必须在 docker 中作为 PID 1 运行才能接收 SIGINT。 To do so, one must use ENTRYPOINT instead of CMD.为此,必须使用 ENTRYPOINT 而不是 CMD。 The fixed Dockerfile:固定的 Dockerfile:

FROM python:3.8-slim-buster
COPY test/TestGS.py .
ENTRYPOINT ["python", "TestGS.py"]

Build the image:构建图像:

docker build . -t python-signals

Run the image:运行图像:

docker run -it --rm --name="python-signals" python-signals

And from a second terminal, stop the container:然后从第二个终端停止容器:

 docker stop python-signals

Then you get the expected output:然后你得到预期的输出:

Received SIGTERM signal
Stop app

It seems a bit odd to me that Docker only emits SIGTERMS to PID 1, but thankfully that's relatively easy to fix. Docker 只向 PID 1 发出 SIGTERMS 对我来说似乎有点奇怪,但幸运的是,这相对容易修复。 The article below was most helpful to solve this issue.下面的文章对解决这个问题最有帮助。

https://itnext.io/containers-terminating-with-grace-d19e0ce34290 https://itnext.io/containers-terminating-with-grace-d19e0ce34290

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM