【问题标题】:Can I run multiple programs in a Docker container?我可以在 Docker 容器中运行多个程序吗?
【发布时间】:2022-01-03 04:59:19
【问题描述】:

我试图从部署一个旨在在桌面用户上运行的应用程序的角度开始围绕 Docker 进行思考。我的应用程序只是一个烧瓶 Web 应用程序和 mongo 数据库。通常我会在虚拟机中安装两者,并将主机端口转发到来宾 Web 应用程序。我想尝试一下 Docker,但我不确定我打算如何使用多个程序。文档说只能有 ENTRYPOINT 所以我怎么能有 Mongo 和我的烧瓶应用程序。或者它们是否需要位于单独的容器中,在这种情况下它们如何相互通信以及这如何使应用程序的分发变得容易?

【问题讨论】:

  • 发现:让我想知道为什么 docker 如此受欢迎..(单进程..?) - 但让我们看看答案告诉我们什么..

标签: docker


【解决方案1】:

它们可以在单独的容器中,实际上,如果应用程序还打算在更大的环境中运行,它们可能会。

多容器系统需要更多的编排才能调出所有必需的依赖项,尽管在 Docker v0.6.5+ 中,Docker 本身内置了一个新工具来帮助实现这一点 - Linking。然而,对于多机解决方案,它仍然必须从 Docker 环境之外进行安排。

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

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

【讨论】:

    【解决方案2】:

    只能有一个 ENTRYPOINT,但该目标通常是一个脚本,可以启动尽可能多的程序。您还可以使用例如Supervisord 或类似方法来负责在单个容器内启动多个服务。 This is an example of a docker container running mysql, apache and wordpress within a single container.

    假设,您有一个供单个 Web 应用程序使用的数据库。那么在一个容器中运行两者可能会更容易。

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

    当应用程序在不同的容器中运行时,至少有两种可能性可以相互通信:

    1. 使用暴露的 IP 端口并通过它们进行连接。
    2. 最近的 docker 版本support linking

    【讨论】:

    • 看起来新版本的 Docker 现在支持Docker container networks
    • Docker 现在支持运行 Supervisor,允许您为每个进程指定行为,例如 autorestart=true、stdout_logfile、stderr_logfile 等。看看docs.docker.com/engine/admin/using_supervisord
    • 我绝对不建议在这个例子中尝试在同一个容器中运行 Web 应用程序和 mongodb。 Docker中有很好的supervisord或类似init进程的用例,但这不是其中的一部分。使用 docker-compose 在单独的容器中运行这两个服务更简单。
    • @nicolas-van 为什么它更简单?是不是因为如果数据库死了,我可以重新启动数据库的容器,而不必重新启动整个事情?
    • 同一台机器上的应用程序也可以通过Unix domain sockets进行通信。保证最高性能。
    【解决方案3】:

    我同意使用两个容器更可取的其他答案,但如果您决心将多个服务捆绑在一个容器中,您可以使用类似 supervisord 的东西。

    Hipache 为例,包含的 Dockerfile 运行 supervisord,文件 supervisord.conf 指定运行 hipache 和 redis-server。

    【讨论】:

      【解决方案4】:

      我对运行 LAMP 堆栈、Mongo DB 和我自己的服务有类似的要求

      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 compose 这样的新东西,它可以帮助您在自己的容器上运行每个服务,并将它们作为依赖关系绑定在一起这些服务,尝试了解更多docker-compose 并使用它,除非您的需求与它不匹配,否则它会更优雅。

      【讨论】:

        【解决方案5】:

        Docker 提供了一个couple of examples 来说明如何做到这一点。轻量级选项是:

        将所有命令放入包装脚本中,并完成测试 和调试信息。以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
        

        【讨论】:

          【解决方案6】:

          我非常不同意以前的一些解决方案,这些解决方案建议在同一个容器中运行这两个服务。 It's clearly stated in the documentation that it's not a recommended:

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

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

          您绝对应该使用docker-compose 来执行此操作并编排具有不同职责的多个容器。

          【讨论】:

          • 这是评论,不是答案。请考虑添加解释和/或链接以支持此立场。否则无济于事。
          • 这是一个答案,因为我可以在这种用例中给出的最佳建议是使用 docker-compose。无论如何,你是对的,我可以提供更多官方建议的链接。我会更新的。
          • 问题是关于在一个容器中运行 2 个进程,因此不关心最佳实践。我会给你一个例子:我必须在基于 PhotonOS 的图像和一个 java 进程中运行 rabbitmq ......所以我使用了一个入口脚本并用作 ENTRYPOINT :)
          • 最初的问题不是关于在 Docker 容器中运行两个进程的技术可行性的通用问题。它说明了一个特定的用例,即 Python 应用程序和 MongoDB 数据库的部署。而且,对于那个用例,最好的建议是不鼓励使用单个容器并推荐使用 docker-compose。
          • 这应该是答案。 I'm trying to wrap my head around Docker。显然,这是一个不了解容器概念并试图将容器视为标准 Web 服务器的新 Docker 用户,许多刚接触 Docker 的人曾经尝试过这样做(包括我自己) )。将用户指向文档并解释最佳实践是一个很好的答案。
          【解决方案7】:

          虽然不建议您使用 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 集群。您可以通过为应用提供简单的 Kubernetes 清单来“分发”应用。

          【讨论】:

          • 清晰易懂。希望没有缺点。不知道为什么docker官网没有提到这个方法。
          • 唯一适用于我的解决方案。谢谢
          【解决方案8】:

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

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

          【讨论】:

            最近更新 更多