两年前我开始使用 Docker 容器。您可以在这里找到我的 dockerfile 示例:https: //github.com/komljen/dockerfile-examples
这是我的测试场地,但在我开始使用 Docker 进行企业级应用程序部署后,我发现我做错了很多事情。其中之一是我如何在 Docker 中启动应用程序。
几乎我所有的 dockerfiles 在最后都有一些 bash 脚本来在容器内做一些小的改变并最终启动应用程序。我通常将此脚本添加到 Dockerfile 中的 CMD 指令中。我认为这没有任何问题,只是 Docker 停止了它应该的工作。
问题是,当您运行 bash 脚本时,它将获得 PID 1,而您的应用程序是一个 PPID 为 1 的子进程。当您运行 docker stop 时,Bash 不会将 SIGTERM 信号转发给您的应用程序。相反,容器将在 10 秒超时后被终止,这是 docker stop 命令的默认设置。这个超时是可以调整的。
有一种简单的方法可以使用 bash 脚本中的“exec”命令来处理这个问题。它将在不创建新进程的情况下替换 shell,并且您的应用程序将获得 PID 1。让我们先测试这两种情况。
出于测试目的,我将使用这个简单的 redis dockerfile:
FROM ubuntu:trusty
ENV DEBIAN_FRONTEND noninteractive
RUN
apt-get update &&
apt-get -y install
software-properties-common &&
add-apt-repository -y ppa:chris-lea/redis-server &&
apt-get update &&
apt-get -y install
redis-server &&
rm -rf /var/lib/apt/lists/*
COPY start.sh start.sh
EXPOSE 6379
RUN rm /usr/sbin/policy-rc.d
CMD ["/start.sh"]
这是 start.sh 脚本,它将更改一些推荐的 redis 容器内核设置(docker 必须在特权设置为 true 的情况下启动):
FROM ubuntu:trusty
ENV DEBIAN_FRONTEND noninteractive
RUN
apt-get update &&
apt-get -y install
software-properties-common &&
add-apt-repository -y ppa:chris-lea/redis-server &&
apt-get update &&
apt-get -y install
redis-server &&
rm -rf /var/lib/apt/lists/*
COPY start.sh start.sh
EXPOSE 6379
RUN rm /usr/sbin/policy-rc.d
CMD ["/start.sh"]
现在让我们构建并运行这个容器:
FROM ubuntu:trusty
ENV DEBIAN_FRONTEND noninteractive
RUN
apt-get update &&
apt-get -y install
software-properties-common &&
add-apt-repository -y ppa:chris-lea/redis-server &&
apt-get update &&
apt-get -y install
redis-server &&
rm -rf /var/lib/apt/lists/*
COPY start.sh start.sh
EXPOSE 6379
RUN rm /usr/sbin/policy-rc.d
CMD ["/start.sh"]
然后查看redis容器里面运行的是什么:
FROM ubuntu:trusty
ENV DEBIAN_FRONTEND noninteractive
RUN
apt-get update &&
apt-get -y install
software-properties-common &&
add-apt-repository -y ppa:chris-lea/redis-server &&
apt-get update &&
apt-get -y install
redis-server &&
rm -rf /var/lib/apt/lists/*
COPY start.sh start.sh
EXPOSE 6379
RUN rm /usr/sbin/policy-rc.d
CMD ["/start.sh"]
好的,所以这是一个问题。现在让我们尝试停止这个 docker 容器:
FROM ubuntu:trusty
ENV DEBIAN_FRONTEND noninteractive
RUN
apt-get update &&
apt-get -y install
software-properties-common &&
add-apt-repository -y ppa:chris-lea/redis-server &&
apt-get update &&
apt-get -y install
redis-server &&
rm -rf /var/lib/apt/lists/*
COPY start.sh start.sh
EXPOSE 6379
RUN rm /usr/sbin/policy-rc.d
CMD ["/start.sh"]
10 秒后容器被杀死,没有正常停止。如果我们检查日志,我们可以看到这一点。最后一条消息是 redis 已准备好接受连接:
FROM ubuntu:trusty
ENV DEBIAN_FRONTEND noninteractive
RUN
apt-get update &&
apt-get -y install
software-properties-common &&
add-apt-repository -y ppa:chris-lea/redis-server &&
apt-get update &&
apt-get -y install
redis-server &&
rm -rf /var/lib/apt/lists/*
COPY start.sh start.sh
EXPOSE 6379
RUN rm /usr/sbin/policy-rc.d
CMD ["/start.sh"]
为了让它工作,我们需要更改 start.sh 脚本中的最后一行并重建图像。我们在 /usr/bin/redis-server 命令之前添加 exec:
FROM ubuntu:trusty
ENV DEBIAN_FRONTEND noninteractive
RUN
apt-get update &&
apt-get -y install
software-properties-common &&
add-apt-repository -y ppa:chris-lea/redis-server &&
apt-get update &&
apt-get -y install
redis-server &&
rm -rf /var/lib/apt/lists/*
COPY start.sh start.sh
EXPOSE 6379
RUN rm /usr/sbin/policy-rc.d
CMD ["/start.sh"]
再次构建、启动并运行 ps -ef 命令:
FROM ubuntu:trusty
ENV DEBIAN_FRONTEND noninteractive
RUN
apt-get update &&
apt-get -y install
software-properties-common &&
add-apt-repository -y ppa:chris-lea/redis-server &&
apt-get update &&
apt-get -y install
redis-server &&
rm -rf /var/lib/apt/lists/*
COPY start.sh start.sh
EXPOSE 6379
RUN rm /usr/sbin/policy-rc.d
CMD ["/start.sh"]
如您所见,redis 现在以 PID 1 运行,并且 docker stop 可以正常工作。我们先试试看,再查看docker日志。您应该在 redis 日志中看到此消息:
FROM ubuntu:trusty
ENV DEBIAN_FRONTEND noninteractive
RUN
apt-get update &&
apt-get -y install
software-properties-common &&
add-apt-repository -y ppa:chris-lea/redis-server &&
apt-get update &&
apt-get -y install
redis-server &&
rm -rf /var/lib/apt/lists/*
COPY start.sh start.sh
EXPOSE 6379
RUN rm /usr/sbin/policy-rc.d
CMD ["/start.sh"]
这就是我将 exec 与 postgres 和 tomcat 容器一起使用的方式,其中进程不以 root 用户运行:
FROM ubuntu:trusty
ENV DEBIAN_FRONTEND noninteractive
RUN
apt-get update &&
apt-get -y install
software-properties-common &&
add-apt-repository -y ppa:chris-lea/redis-server &&
apt-get update &&
apt-get -y install
redis-server &&
rm -rf /var/lib/apt/lists/*
COPY start.sh start.sh
EXPOSE 6379
RUN rm /usr/sbin/policy-rc.d
CMD ["/start.sh"]
由于 sudo 或 su,这里的进程不会以 PID 1 运行,但是 Docker stop 在这两种情况下都能完美运行。这样做的原因是 sudo 和 su 命令会将 SIGTERM 信号传递给子进程。