当删除一个pod或部署一个新版本的pod时,理论上kubernetes应该向正在运行的进程发送一个SIGTERM
,然后等待gracePeriodSeconds
(默认为30秒),然后再发送一个SIGKILL
。
我遇到了这个问题,这第一个SIGTERM
似乎从来没有被发送。我的集群中的默认设置从未改变(kill在30秒后按预期发送),所以我的假设是可能有什么问题,权限或类似的,与我的Dockerfile(见下文)。
我已经排除了在优雅的关闭逻辑中有一个错误,通过kubectl exec
-ing进入pod并在进程中使用kill -15
来捕获可执行文件中的SIGTERM
。
Dockerfile如下所示:
FROM debian:bullseye-slim AS app
ARG USERNAME=app
ARG USER_UID=1000
ARG USER_GID=$USER_UID
RUN apt update && apt install -y libssl-dev zstd ca-certificates pkg-config
RUN groupadd --gid $USER_GID $USERNAME
&& useradd --uid $USER_UID --gid $USER_GID -m $USERNAME
WORKDIR /home/$USERNAME
ARG RELEASE_DIR
ARG SERVICE
USER $USERNAME
COPY $RELEASE_DIR .
EXPOSE 8080
ENV CMD=./${SERVICE}
CMD ${CMD}
这里有什么明显的错误吗?或者kubernetes需要一些额外的配置来实际发送预期的终止信号?
要使终止正常工作,您需要确保您的应用程序是主容器进程。对于shell形式CMD
,您的容器运行的命令是/bin/sh -c '${CMD}'
,并且根据该环境变量中的内容和/bin/sh
的实际内容,该shell包装器可能会继续作为主容器进程运行并拦截终止信号。
同样的机制适用于普通Docker和Kubernetes,如果您在本地docker stop
容器,您应该会看到类似的问题。这可能更容易调试和迭代。
解决这个问题最简单的方法是使用CMD
的exec形式,看起来像一个JSON数组。由于这不会运行shell,因此它也不能进行变量展开,并且您必须拼写出您希望命令的实际内容
CMD ["./service"]
这仍然很容易在运行时重写,实际上您根本不需要CMD
:
# instead of `docker run -e CMD='...'`
docker run --rm my-image
ls -l /home/app
# or in a Kubernetes pod spec
command:
- /home/app/another_app
args:
- --option
你可以类似地删除Dockerfile中几乎所有的ARG
声明(容器用户的名称或数字uid不重要,例如,编译的应用程序文件名和主机构建路径通常是固定的),这将简化设置。