Docker多个入口点

docker

64215 观看

9回复

17307 作者的声誉

假设我有以下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。但是根据文档ENTRYPOINTDockerfile中必须只有一个。那么这样做的正确方法是什么?

作者: Snowman 的来源 发布者: 2013 年 9 月 14 日

回应 (9)


2

10353 作者的声誉

我可以想到几种方法:

  • 你可以编写一个脚本来放置容器(ADD)来执行所有启动命令,然后将其放入ENTRYPOINT
  • 我认为你可以把任何shell命令放在上面ENTRYPOINT,所以你可以这样做service mongod start && /usr/sbin/apache2
作者: BraveNewCurrency 发布者: 15.09.2013 07:02

3

794 作者的声誉

我无法使用它&&。我能够解决这个问题,如下所述: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文件中。您可以使用grepif声明来使RUN命令具有幂等性。

作者: damick 发布者: 09.11.2013 06:17

18

21434 作者的声誉

我的解决方案是将单个脚本放入/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"]
作者: gak 发布者: 15.11.2013 09:03

61

1155 作者的声誉

正如Jared Markell所说,如果你想在docker容器中启动多个进程,你必须使用supervisor。您必须配置主管告诉他启动您的不同流程。

我在这篇博文中写过这篇文章,但你在这里有一篇非常好的文章,详细介绍了如何以及为什么在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

编辑:由于这个答案得到了很多赞成,我想要精确地警告说,使用Supervisor 被视为运行多个工作的最佳实践。相反,您可能有兴趣为不同的进程创建多个容器,并通过docker compose管理它们。简而言之,Docker Compose允许您在一个文件中定义应用程序所需的所有容器,并在一个命令中启动它们。

作者: Pierre-Jean 发布者: 31.05.2014 09:06

6

1740 作者的声誉

简单的答案是你不应该因为它违反单一责任原则:一个容器,一个服务。想象一下,由于突然的工作量,你想要产生MongoDB的其他云图像 - 为什么还要以1:1的比例增加Apache2实例?相反,您应该链接框并让它们通过TCP说话。有关详细信息,请参阅https://docs.docker.com/userguide/dockerlinks/

作者: Alessandro Santini 发布者: 18.12.2014 10:07

0

268 作者的声誉

您无法在Dockerfile中指定多个入口点。要在同一个docker容器中运行多个服务器,您必须使用能够启动服务器的命令。Supervisord已被引用,但我也可以推荐multirun,这是一个更轻的替代品。

作者: nicolas-van 发布者: 16.12.2016 12:52

1

156 作者的声誉

docker docs中有一个答案:https//docs.docker.com/config/containers/multi-service_container/

但总之

如果您需要在容器中运行多个服务,则可以通过几种不同的方式完成此操作。

第一个是运行脚本来管理你的进程。

第二个是使用像supervisord这样的流程管理器

作者: Mike Yakovets 发布者: 28.05.2018 10:24

0

1694 作者的声誉

如果您尝试运行多个并发npm脚本(例如监视脚本和构建脚本),请查看:

如何并行运行多个npm脚本?

作者: dipole_moment 发布者: 24.06.2019 08:39

0

78205 作者的声誉

通常情况下,你不会这样做。这是一种反模式,因为:

  1. 通常,这两个进程的更新周期不同
  2. 您可能希望为每个进程更改基本文件系统
  3. 您希望每个进程的日志记录和错误处理彼此独立
  4. 在共享网络或卷之外,这两个进程可能没有其他硬依赖关系

因此,最好的选择是创建两个单独的映像,并使用处理共享专用网络的撰写文件启动这两个容器。


如果你不能遵循这个最佳实践,那么你最终会遇到如下情况。父图像包含一行:

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.shshell或进程替换当前的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 ...

注意,"$@"我一直使用的表示法是将CMDas作为参数的值传递给父入口点。

选项C:切换到像supervisord这样的工具。我不是很喜欢这个,因为它仍然意味着在你的容器中运行多个守护进程,你通常最好将它分成多个容器。当单个子进程一直出现故障时,您需要确定正确的响应。

选项D:与选项A和B类似,我经常创建一个入口点脚本目录,可以在图像构建的不同级别进行扩展。入口点本身没有变化,我只是将新文件添加到一个目录中,该目录根据文件名顺序调用。在我的场景中,这些脚本都在前台运行,我CMD在最后执行。您可以在我的基本映像仓库中查看此示例,特别是包含以下部分的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 "$@"

但是,以上内容更多的是用于扩展初始化步骤,而不是用于在容器内运行多个守护进程。鉴于他们每个人都有不好的选择和问题,我希望很清楚为什么在你的场景中首选运行两个容器。

作者: BMitch 发布者: 17.07.2019 02:30
32x32