说我有以下Dockerfile:
FROM ubuntu
RUN apt-get update
RUN apt-get install -y apache2
RUN apt-get install -y mongod #pretend this exists
EXPOSE 80
ENTRYPOINT ["/usr/sbin/apache2"]
ENTRYPOINT
命令使得apache2
在容器启动时启动。我希望能够在容器以命令mongod
启动时启动service mongod start
。但是,根据documentation,Dockerfile中只能有一个ENTRYPOINT
。那么这样做的正确方法是什么?
答案 0 :(得分:58)
正如Jared Markell所说,如果您想在docker容器中启动多个进程,则必须使用supervisor。您必须配置主管告诉他启动您的不同流程。
我在这个blog post中写过这个,但你有一个真正的nice article here详细说明了如何以及为什么在Docker中使用supervisor。
基本上,您需要执行以下操作:
FROM ubuntu
RUN apt-get update
RUN apt-get install -y apache2
RUN apt-get install -y mongod #pretend this exists
RUN apt-get install -y supervisor # Installing supervisord
ADD supervisord.conf /etc/supervisor/conf.d/supervisord.conf
EXPOSE 80
ENTRYPOINT ["/usr/bin/supervisord"]
添加配置文件supervisord.conf
[supervisord]
nodaemon=true
[program:mongodb]
command=/etc/mongod/mongo #To adapt, I don't know how to launch your mongodb process
[program:apache2]
command=/usr/sbin/apache2 -DFOREGROUND
编辑:由于这个答案已经收到了很多赞成,我想要准确地警告使用主管不被视为最佳实践< / em>运行多个工作。相反,您可能有兴趣为不同的流程创建多个容器,并通过docker compose进行管理。 简而言之, Docker Compose 允许您在一个文件中定义应用程序所需的所有容器,并在一个命令中启动它们。
答案 1 :(得分:18)
我的解决方案是将单个脚本放入/opt/run/
并使用以下命令执行:
#!/bin/bash
LOG=/var/log/all
touch $LOG
for a in /opt/run/*
do
$a >> $LOG &
done
tail -f $LOG
我的入口点就是这个脚本的位置,比如它叫/opt/bin/run_all
:
ADD 00_sshd /opt/run/
ADD 01_nginx /opt/run/
ADD run_all /opt/bin/
ENTRYPOINT ["/opt/bin/run_all"]
答案 2 :(得分:6)
简单的答案是你不应该因为它打破了单一责任原则:一个容器,一个服务。想象一下,由于突然的工作量,你想要产生MongoDB的其他云图像 - 为什么还要以1:1的比例增加Apache2实例? 相反,您应该链接框并让它们通过TCP说话。有关详细信息,请参阅https://docs.docker.com/userguide/dockerlinks/。
答案 3 :(得分:3)
我无法使用&&
工作。我能够按照此处所述解决此问题:https://stackoverflow.com/a/19872810/2971199
所以在你的情况下你可以这样做:
RUN echo "/usr/sbin/apache2" >> /etc/bash.bashrc
RUN echo "/path/to/mongodb" >> /etc/bash.bashrc
ENTRYPOINT ["/bin/bash"]
您可能需要/想要编辑启动命令。
如果多次运行Dockerfile,请注意,您可能不希望将多个命令副本附加到bash.bashrc
文件中。您可以使用grep
和if
语句使RUN
命令具有幂等性。
答案 4 :(得分:2)
我可以想到几种方式:
ADD
),然后将其放入ENTRYPOINT
ENTRYPOINT
上添加任何shell命令,因此您可以执行service mongod start && /usr/sbin/apache2
答案 5 :(得分:1)
docker docs中有一个答案: https://docs.docker.com/config/containers/multi-service_container/
但总之
如果您需要在容器中运行多个服务,可以通过几种不同的方式实现此目的。
第一个是运行脚本来管理你的过程。
第二个是使用像 supervisord
这样的流程管理器答案 6 :(得分:0)
您无法在Dockerfile中指定多个入口点。要在同一个docker容器中运行多个服务器,您必须使用能够启动服务器的命令。 Supervisord已被引用,但我也可以推荐multirun,这是我的一个较轻的选择。
答案 7 :(得分:0)
如果您尝试运行多个并发npm脚本,例如监视脚本和构建脚本,请签出:
答案 8 :(得分:0)
通常,您不会这样做。这是一种反模式,因为:
因此,最好的选择是创建两个单独的映像,并使用处理共享专用网络的撰写文件启动两个容器。
如果您不能遵循最佳实践,那么最终会遇到以下情况。父图像包含一行:
ENTRYPOINT ["/entrypoint-parent.sh"]
,并且您要将以下内容添加到子图像中:
ENTRYPOINT ["/entrypoint-child.sh"]
然后将结果图像中的ENTRYPOINT
的值替换为/entrypoint-child.sh
,换句话说,ENTRYPOINT
只有一个值。 Docker只会调用一个进程来启动您的容器,尽管该进程会产生子进程。有两种扩展入口点的技术。
选项A :调用您的入口点,然后最后运行父入口点,例如/entrypoint-child.sh
看起来像:
#!/bin/sh
echo Running child entrypoint initialization steps here
/usr/bin/mongodb ... &
exec /entrypoint-parent.sh "$@"
exec
部分很重要,它用/entrypoint-parent.sh
shell或进程替换了当前shell,从而消除了信号处理方面的问题。结果是您在子入口点中运行初始化的第一位,然后委托给原始父入口点。这确实需要您跟踪父入口点的名称,这可能会在基本映像的版本之间发生变化。这也意味着您失去了在mongodb上执行错误处理和正常终止的权限,因为它在后台运行。这可能会导致错误的健康容器和数据丢失,我都不建议在生产环境中使用它们。
选项B :在后台运行父入口点。这是不理想的,因为除非您采取一些额外的步骤,否则您将不再对父进程进行错误处理。最简单地说,这类似于您/entrypoint-child.sh
中的以下内容:
#!/bin/sh
# other initialization steps
/entrypoint-parent.sh "$@" &
# potentially wait for parent to be running by polling
# run something new in the foreground, that may depend on parent processes
exec /usr/bin/mongodb ...
请注意,我一直使用的"$@"
表示法将CMD
的值作为参数传递给父入口点。
选项C :切换至supervisord之类的工具。我不是很喜欢这个,因为它仍然意味着在容器中运行多个守护程序,通常最好将其拆分为多个容器。您需要确定单个子进程不断失败时正确的响应是什么。
选项D :类似于选项A和B,我经常创建一个入口点脚本目录,该目录可以在映像构建的不同级别进行扩展。入口点本身是不变的,我只是将新文件添加到基于文件名依次调用的目录中。在我的方案中,这些脚本都在前台运行,最后我执行CMD
。您可以在我的base image repo中看到一个示例,尤其是entrypoint.d
目录和bin/entrypointd.sh
脚本,其中包括以下部分:
# ...
for ep in /etc/entrypoint.d/*; do
ext="${ep##*.}"
if [ "${ext}" = "env" -a -f "${ep}" ]; then
# source files ending in ".env"
echo "Sourcing: ${ep}"
set -a && . "${ep}" && set +a
elif [ "${ext}" = "sh" -a -x "${ep}" ]; then
# run scripts ending in ".sh"
echo "Running: ${ep}"
"${ep}"
fi
done
# ...
# run command with exec to pass control
echo "Running CMD: $@"
exec "$@"
但是,以上内容只是为了扩展初始化步骤,而不是为了在容器内运行多个守护程序。考虑到它们各自的错误选择和问题,我希望很清楚为什么在您的方案中最好运行两个容器。