我可以在一个 Docker 容器中运行多个程序吗?

我试图从部署一个应用程序的角度来了解 Docker,这个应用程序应该在桌面上的用户上运行。我的应用程序只是一个烧瓶 Web 应用程序和 mongo 数据库。通常情况下,我会同时安装在虚拟机中,并将主机端口转发到访客 Web 应用程序。我想给 Docker 一个尝试,但我不知道我应该如何使用多个程序。文档说只能有 ENTRYPOINT,所以我怎么能有 Mongo 和我的烧瓶应用程序。或者它们需要在不同的容器中,在这种情况下,它们如何相互交流,以及如何使发布应用程序变得容易?

150152 次浏览

它们可以位于单独的容器中,实际上,如果应用程序也打算在更大的环境中运行,那么它们很可能位于不同的容器中。

一个多容器系统将需要更多的编排来提供所有需要的依赖关系,尽管在 Docker v0.6.5 + 中有一个新的工具来帮助解决 Docker 本身内置的依赖关系—— 连接中。但是,对于多机解决方案,仍然需要从 Docker 环境外部进行安排。

对于两个不同的容器,这两个部分仍然通过 TCP/IP 进行通信,但是除非这两个端口被特别锁定(不推荐这样做,因为您无法运行多个副本) ,否则您必须传递数据库向应用程序公开的新端口,以便它可以与 Mongo 进行通信。这又是,链接可以帮助解决的问题。

对于一个更简单的小型安装,其中所有依赖项都放在同一个容器中,也可以通过最初称为 ENTRYPOINT 的程序启动数据库和 Python 运行时。这可以像 shell 脚本或其他进程控制器一样简单—— 主管非常流行,公共 Dockerfiles 中有许多示例。

虽然只能有一个 ENTRYPOINT,但是该目标通常是一个启动所需程序的脚本。您还可以使用例如 主管或类似的方法在单个容器中启动多个服务。这是一个在单个容器中运行 mysql、 apache 和 wordpress 的 docker 容器的示例.

假设,您有一个由单个 Web 应用程序使用的数据库。那么在一个容器中同时运行这两个命令可能更容易。

如果您拥有一个由多个应用程序使用的共享数据库,那么最好在其自己的容器中运行该数据库,并在各自的容器中运行每个应用程序。

当应用程序在不同的容器中运行时,它们之间至少有两种可能的通信方式:

  1. 使用公开的 IP 端口并通过它们进行连接。
  2. 最近的码头版本 支持连接

我同意使用两个容器更好的其他答案,但是如果您决心将多个服务绑定到一个容器中,那么您可以使用类似 monitor ord 的东西。

例如,在 希帕奇中,包含的 Dockerfile 运行 superord 文件,而 visidord.conf 文件指定同时运行 hipache 和 redis-server。

对于运行 LAMP 堆栈、 MongoDB 和我自己的服务,我也有类似的需求

Docker 是基于操作系统的虚拟化,这就是为什么它将其容器隔离在正在运行的进程周围,因此它需要至少一个在 FOREGROund 中运行的进程。

因此,您可以提供自己的启动脚本作为入口点,这样,您的启动脚本就变成了扩展的 Docker 映像脚本,在这个脚本中,您可以堆叠任意数量的服务,最远可以堆叠到 至少有一个前台服务已经开始,但是也快结束了

所以我的 Docker 图像文件在最后有两行:

COPY myStartupScript.sh /usr/local/myscripts/myStartupScript.sh
CMD ["/bin/bash", "/usr/local/myscripts/myStartupScript.sh"]

在我的脚本中,我运行所有的 MySQL、 MongoDB、 Tomcat 等。最后,我将 Apache 作为前台线程运行。

source /etc/apache2/envvars
/usr/sbin/apache2 -DFOREGROUND

这使我能够启动所有服务,并使容器保持活动状态,最后一个服务开始显示在前台

希望能有帮助

更新 : 自从我上次回答这个问题以来,出现了像 码头组合这样的新东西,它可以帮助你在自己的容器上运行每个服务,然后把它们作为这些服务之间的依赖关系绑定在一起,尝试更多地了解 码头作曲并使用它,除非你的需求与它不匹配,否则这是一种更优雅的方式。

Docker 提供了一个关于如何做到这一点的 举几个例子。轻量级选项是:

将所有命令放在一个包装脚本中,并完成测试 和调试信息。将包装器脚本作为 CMD运行 一个非常天真的例子。首先,包装器脚本:

#!/bin/bash


# Start the first process
./my_first_process -D
status=$?
if [ $status -ne 0 ]; then
echo "Failed to start my_first_process: $status"
exit $status
fi


# Start the second process
./my_second_process -D
status=$?
if [ $status -ne 0 ]; then
echo "Failed to start my_second_process: $status"
exit $status
fi


# Naive check runs checks once a minute to see if either of the processes exited.
# This illustrates part of the heavy lifting you need to do if you want to run
# more than one service in a container. The container will exit with an error
# if it detects that either of the processes has exited.
# Otherwise it will loop forever, waking up every 60 seconds


while /bin/true; do
ps aux |grep my_first_process |grep -q -v grep
PROCESS_1_STATUS=$?
ps aux |grep my_second_process |grep -q -v grep
PROCESS_2_STATUS=$?
# If the greps above find anything, they will exit with 0 status
# If they are not both 0, then something is wrong
if [ $PROCESS_1_STATUS -ne 0 -o $PROCESS_2_STATUS -ne 0 ]; then
echo "One of the processes has already exited."
exit -1
fi
sleep 60
done

接下来,Dockerfile:

FROM ubuntu:latest
COPY my_first_process my_first_process
COPY my_second_process my_second_process
COPY my_wrapper_script.sh my_wrapper_script.sh
CMD ./my_wrapper_script.sh

我强烈反对以前建议在同一个容器中运行两个服务的一些解决方案。 文件中明确指出,不推荐使用这种方法:

通常建议通过对每个容器使用一个服务来分离关注领域。该服务可能分叉成多个进程(例如,Apache HTTP Server 启动多个工作进程)。拥有多个进程是可以的,但是要从 Docker 中获得最大的好处,就要避免一个容器负责整个应用程序的多个方面。您可以使用用户定义的网络和共享卷连接多个容器。

有很好的用例来监督或类似的程序,但运行一个 Web 应用程序 + 数据库不是其中的一部分。

您肯定应该使用 码头作曲来实现这一点,并编排具有不同职责的多个容器。

虽然不推荐使用 wait在前台运行2个进程。只需创建一个 bash 脚本,其内容如下。例子 start.sh:

# runs 2 commands simultaneously:


mongod & # your first application
P1=$!
python script.py & # your second application
P2=$!
wait $P1 $P2

在 Dockerfile 中,从

CMD bash start.sh

如果您想同时运行多个进程,我建议您设置一个本地 Kubernetes 集群。你可以通过提供一个简单的库伯内特清单来“分发”这个应用程序。

如果一个专用的脚本似乎开销太大,您可以使用 sh -c显式地产生单独的进程。例如:

CMD sh -c 'mini_httpd -C /my/config -D &' \
&& ./content_computing_loop

在 docker 中,有两种方式可以运行程序

  1. CMD
  2. 入口

如果你想知道它们之间的区别,请参考 给你

在 CMD/ENTRYPOINT 中,运行命令有两种格式

  • SHELL 格式
  • EXEC 格式

SHELL 格式:

CMD executable_first arg1; executable_second arg1 arg2
ENTRYPOINT executable_first arg1; executable_second arg1 arg2

这个版本将创建一个 shell 并执行以上命令。这里可以使用任何 shell 语法,如“ ;”、“ &”、“ |”等。因此您可以在这里运行任意数量的命令。如果要运行一组复杂的命令,可以创建单独的 shell 脚本并使用它。

CMD my_script.sh arg1
ENTRYPOINT my_script.sh arg1

EXEC 格式:

CMD ["executable", "parameter 1", "parameter 2", …]
ENTRYPOINT ["executable", "parameter 1", "parameter 2", …]

这里您可以注意到,只有第一个参数是可执行文件。从第二个参数开始,所有内容都成为该可执行文件的参数/参数。

以 EXEC 格式运行多个命令

CMD ["/bin/sh", "-c", "executable_first arg1; executable_second"]
CMD ["/bin/sh", "-c", "executable_first arg1; executable_second"]

在上面的命令中,我们使用 shell 命令作为可执行命令来运行该命令。这是以 EXEC 格式运行多个命令的唯一方法。

以下内容是错误的

CMD ["executable_first parameter", "executable_second parameter"]
ENTRYPOINT ["executable_first parameter", "executable_second parameter"]
CMD ["executable_first", "parameter", ";", "executable_second",  "parameter"]
ENTRYPOINT ["executable_first", "parameter", ";", "executable_second", "parameter"]

我可以在一个 Docker 容器中运行多个程序吗?

是的,但风险很大。


下面是和上面一样的答案。但是有一些细节和推荐的解决方案。如果你对这些感兴趣的话。

不推荐

警告。不过,Docker 社区建议对多个服务使用相同的容器。Docker 文档中写道: “通常建议使用 每个集装箱使用一项服务分隔关注的区域。”资料来源:

https://archive.ph/3Roa6#selection-307.2-307.100

https://docs.docker.com/config/containers/multi-service_container/

如果你选择忽略上面的建议,你的集装箱风险就会随着安全性的减弱,日益不稳定,并在未来痛苦地增长。

如果您对上述风险没有意见,那么将一个容器用于多个服务的文档如下:

https://archive.ph/3Roa6#selection-335.0-691.1

https://docs.docker.com/config/containers/multi-service_container/

推荐

如果您需要一个安全性更强、稳定性更好的容器,并且将来需要更大的规模和更好的性能,那么 Docker 社区推荐这两个步骤:

  1. 每个 Docker 容器使用一个服务。最终结果是您将拥有多个容器。

  2. 使用这个 Docker“ 交际”特性可以连接到您喜欢的任何容器。