英文:
Python Docker SIGTERM not being fired on container stop
问题
Here's the translation of your provided content:
- 我有一个使用asyncio和aiohttp运行的Python脚本,它在每次运行后都会睡眠一分钟。
- 我试图在Docker容器中使用poetry 1.5.1运行它,当我在本地按下Ctrl + C时,它通过处理SIGINT和SIGTERM信号正常工作。
- 当我在Docker文件中运行它并调用
docker container stop <container-name>
时,它不会正常终止。
Dockerfile
FROM python:3.10.11-slim as base
ENV PYTHONFAULTHANDLER=1 \
PYTHONHASHSEED=random \
PYTHONUNBUFFERED=1
RUN apt-get update \
&& apt-get install --no-install-recommends -y gcc libffi-dev g++ \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
FROM base as builder
ENV PIP_DEFAULT_TIMEOUT=100 \
PIP_DISABLE_PIP_VERSION_CHECK=1 \
PIP_NO_CACHE_DIR=1 \
POETRY_VERSION=1.5.1
RUN pip install "poetry==$POETRY_VERSION"
RUN python -m venv /venv
WORKDIR /home/ch_news
COPY --chown=10000:10000 pyproject.toml poetry.lock ./
RUN . /venv/bin/activate && poetry install --no-interaction --no-dev --no-ansi
COPY --chown=10000:10000 . .
RUN . /venv/bin/activate && poetry build
FROM base as final
WORKDIR /home/ch_news
RUN groupadd --gid 10000 ch_news \
&& useradd --uid 10000 --gid ch_news --shell /bin/bash --create-home ch_news
COPY --from=builder --chown=10000:10000 /venv /venv
COPY --from=builder --chown=10000:10000 /home/ch_news/dist .
COPY --chown=10000:10000 ./docker/production/python_server/docker-entrypoint.sh ./
RUN chmod +x docker-entrypoint.sh
RUN . /venv/bin/activate && pip install *.whl
USER ch_news
CMD ["./docker-entrypoint.sh"]
docker-entrypoint.sh
#!/bin/sh
set -e
. /venv/bin/activate
python -m news
我正在使用Docker Compose运行所有内容。
version: '3.9'
name: ch_news_prod
services:
ch_news_pro_python:
build:
context: ../../
dockerfile: ./docker/production/python_server/Dockerfile
container_name: ch_news_pro_python
depends_on:
ch_news_pro_postgres:
condition: service_healthy
env_file:
- .env
image: ch_news_pro_python_image
networks:
- network
restart: 'always'
ch_news_pro_postgres:
build:
context: ../..
dockerfile: ./docker/production/postgres_server/Dockerfile
container_name: ch_news_pro_postgres
env_file:
- .env
healthcheck:
test:
- 'CMD-SHELL'
- "pg_isready -d 'host=ch_news_pro_postgres user=ch_api_user port=47289 dbname=ch_api_db_pro'"
interval: 5s
timeout: 5s
retries: 3
start_period: 10s
image: ch_news_pro_postgres_image
networks:
- network
ports:
- '47289:47289'
restart: 'always'
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
network:
driver: bridge
volumes:
postgres_data:
driver: local
这是我的脚本大致内容:
import asyncio
import signal
def handle_sigterm(signum: int, frame: Any) -> None:
raise KeyboardInterrupt()
def app() -> None:
try:
asyncio.run(periodic_task())
except KeyboardInterrupt:
logger.info("因为手动退出而关闭...")
signal.signal(signal.SIGTERM, handle_sigterm)
signal.signal(signal.SIGINT, handle_sigterm)
有人能告诉我我哪里错了以及如何使SIGTERM处理程序在通过docker-compose运行时触发Keyboard Interrupt吗?
英文:
- I have a Python script that uses asyncio and aiohttp to run infinitely while sleeping for a minute after each run
- I am trying to run this inside Docker with poetry 1.5.1 and when I hit Ctrl + C locally, it works by handling the SIGINT, SIGTERM signal
When I run it inside docker file and call
docker container stop <container-name>
It does not terminate gracefully
Dockerfile
FROM python:3.10.11-slim as base
ENV PYTHONFAULTHANDLER=1 \
PYTHONHASHSEED=random \
# Turns off buffering for easier container logging
PYTHONUNBUFFERED=1
RUN apt-get update \
&& apt-get install --no-install-recommends -y gcc libffi-dev g++ \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
FROM base as builder
ENV PIP_DEFAULT_TIMEOUT=100 \
PIP_DISABLE_PIP_VERSION_CHECK=1 \
PIP_NO_CACHE_DIR=1 \
POETRY_VERSION=1.5.1
RUN pip install "poetry==$POETRY_VERSION"
RUN python -m venv /venv
WORKDIR /home/ch_news
COPY --chown=10000:10000 pyproject.toml poetry.lock ./
# --no-interaction not to ask any interactive questions
# --no-ansi flag to make your output more log friendly
RUN . /venv/bin/activate && poetry install --no-interaction --no-dev --no-ansi
COPY --chown=10000:10000 . .
RUN . /venv/bin/activate && poetry build
FROM base as final
WORKDIR /home/ch_news
RUN groupadd --gid 10000 ch_news \
&& useradd --uid 10000 --gid ch_news --shell /bin/bash --create-home ch_news
COPY --from=builder --chown=10000:10000 /venv /venv
COPY --from=builder --chown=10000:10000 /home/ch_news/dist .
COPY --chown=10000:10000 ./docker/production/python_server/docker-entrypoint.sh ./
RUN chmod +x docker-entrypoint.sh
RUN . /venv/bin/activate && pip install *.whl
USER ch_news
CMD ["./docker-entrypoint.sh"]
docker-entrypoint.sh
#!/bin/sh
set -e
. /venv/bin/activate
python -m news
I am running everything inside docker-compose if that helps
version: '3.9' # optional since v1.27.0
name: ch_news_prod
services:
ch_news_pro_python:
build:
context: ../../
dockerfile: ./docker/production/python_server/Dockerfile
container_name: ch_news_pro_python
depends_on:
ch_news_pro_postgres:
condition: service_healthy
env_file:
- .env
image: ch_news_pro_python_image
networks:
- network
restart: 'always'
ch_news_pro_postgres:
build:
context: ../..
dockerfile: ./docker/production/postgres_server/Dockerfile
container_name: ch_news_pro_postgres
env_file:
- .env
healthcheck:
test:
[
'CMD-SHELL',
"pg_isready -d 'host=ch_news_pro_postgres user=ch_api_user port=47289 dbname=ch_api_db_pro'",
]
interval: 5s
timeout: 5s
retries: 3
start_period: 10s
image: ch_news_pro_postgres_image
networks:
- network
ports:
- '47289:47289'
restart: 'always'
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
network:
driver: bridge
volumes:
postgres_data:
driver: local
This is what my script roughly looks like
import asyncio
import signal
def handle_sigterm(signum: int, frame: Any) -> None:
raise KeyboardInterrupt()
def app() -> None:
try:
asyncio.run(periodic_task())
except KeyboardInterrupt:
logger.info("Shutting down because you exited manually...")
signal.signal(signal.SIGTERM, handle_sigterm)
signal.signal(signal.SIGINT, handle_sigterm)
Can someone tell me where i am going wrong and how to make the SIGTERM handler fire the Keyboard Interrupt when running via docker-compose?
答案1
得分: 3
Your docker-entrypoint.sh
脚本将主应用程序作为普通子进程运行。这意味着 Shell 脚本将在 Python 应用程序运行时保持运行。如果你运行 docker-compose exec ch_news_pro_python ps
,我预期你将看到进程 1 是 docker-entrypoint.sh
脚本而不是 Python 应用程序。
docker stop
只向容器中的进程 1 发送信号。这意味着 Shell 脚本会接收到 SIGTERM 信号,但不会将其传递给应用程序。
最简单的解决方法是将脚本的最后一行更改为使用 exec
启动 Python 应用程序。这会导致 Shell 脚本用 Python 解释器替换自身;在 exec
命令之后无法在脚本中运行任何内容,但相应地,现在 python
将成为进程 1 并接收 Docker 信号。
exec python -m news
#^^^
或者,通常可以直接在虚拟环境中运行命令,而无需先显式激活它。最简单的 Docker 设置可能是使用 Poetry 的 scripts
设置,在 /venv/bin/news
中创建一个脚本来运行你的应用程序;通过 poetry install
将整个应用程序安装到虚拟环境中;将 /venv/bin
目录放入 $PATH
;然后将镜像的 CMD
设置为运行包装脚本。
FROM base as final
COPY --from=builder /venv /venv
ENV PATH /venv/bin:$PATH
CMD ["news"]
英文:
Your docker-entrypoint.sh
script runs the main application as an ordinary subprocess. This means the shell script will stay running while your Python application runs. If you docker-compose exec ch_news_pro_python ps
, I expect you will see that process 1 is the docker-entrypoint.sh
script and not the Python application.
docker stop
sends its signal only to process 1 in the container. That means the shell script receives SIGTERM, and it doesn't forward it on to the application.
The easiest way to work around this is to change the last line of the script to exec
the Python application. This causes the shell script to replace itself with the Python interpreter; you can't run anything in the script after the exec
command, but conversely, now python
will be process 1 and will receive Docker signals.
exec python -m news
#^^^
Alternatively, you can usually run commands directly out of a virtual environment without specifically activating it first. The simplest Docker setup might be to use the Poetry scripts
setting to create a script in /venv/bin/news
that runs your application; have poetry install
install the entire application into the virtual environment; put the /venv/bin
directory in the $PATH
; and then set the image CMD
to run the wrapper script.
FROM base as final
COPY --from=builder /venv /venv
ENV PATH /venv/bin:$PATH
CMD ["news"]
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论