如何在 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
信号。
我有一个由以下 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
信号。