如何在 Docker 中正确捕获 SIGTERM?

How to catch SIGTERM properly in Docker?

我有一个由以下 Dockerfile 创建的 docker 容器:

ARG TAG=latest
FROM continuumio/miniconda3:${TAG}
ARG GROUP_ID=1000
ARG USER_ID=1000
ARG ORG=my-org
ARG USERNAME=user
ARG REPO=none
ARG COMMIT=none
ARG BRANCH=none
ARG MAKEAPI=True
RUN addgroup --gid $GROUP_ID $USERNAME
RUN adduser --uid $USER_ID --disabled-password --gecos "" $USERNAME --ingroup $USERNAME
COPY . /api_maker
RUN /opt/conda/bin/pip install pyyaml psutil packaging
RUN apt install -y openssh-client git

RUN mkdir -p -m 0700 ~/.ssh && ssh-keyscan github.com >> ~/.ssh/known_hosts
ENV GIT_SSH_COMMAND="ssh -i /run/secrets/thekey"
RUN --mount=type=secret,id=thekey git clone git@github.com:$ORG/$REPO.git /repo
RUN /opt/conda/bin/python3 /api_maker/repo_setup.py $BRANCH $COMMIT
RUN /repo/root_script.sh
RUN chown -R $USERNAME:$USERNAME /api_maker
RUN chown -R $USERNAME:$USERNAME /repo
RUN mkdir -p /data
RUN chown -R $USERNAME:$USERNAME /data
RUN mkdir -p /working
RUN chown -R $USERNAME:$USERNAME /working
RUN mkdir -p /opt/conda/pkgs
RUN mkdir -p /opt/conda/envs
RUN chmod -R 777 /opt/conda
RUN touch /opt/conda/pkgs/urls.txt
USER $USERNAME
RUN /api_maker/user_env_setup.sh $MAKEAPI
CMD /repo/run_api.sh $@;

使用以下 run_api.sh 脚本:

#!/bin/bash
cd /repo
PROCESSES=${1:-9}
LOCAL_DOCKER_PORT=${2:-7001}
exec /opt/conda/envs/environment/bin/gunicorn --bind 0.0.0.0:$LOCAL_DOCKER_PORT --workers=$PROCESSES restful_api:app

我的应用包含一些信号处理。如果我从容器内部手动将 SIGTERM 发送到 gunicorn(工作程序或父进程),我的信号处理工作正常。但是,当我在容器上 运行 docker stop 时,它无法正常工作。我怎样才能使我的 shell 脚本正确转发它应该接收的 SIGTERM?

您需要确保主容器进程是您的实际应用程序,而不是 shell 包装器。

由于您目前有 CMD,shell 会调用它。参数列表 $@ 将始终为空。 shell 解析 /repo/run_api.sh 并发现它后面跟着一个分号,因此它可能需要做其他事情。因此,即使您的脚本正确地以 exec gunicorn ... 结尾以将控制权直接移交给另一个进程,它仍然 运行 在 shell 之下,并且当您 docker stop 容器时,它转到 shell 包装器。

避免这种情况的最简单方法 shell 是使用 exec form CMD:

CMD ["/repo/run_api.sh"]

这将导致您的脚本直接 运行,而没有 /bin/sh -c 包装器调用它,并且当脚本最终 exec 另一个进程时,该进程成为主进程并且将收到 docker stop 信号。