【问题标题】:Can we run multi-process program in docker?我们可以在 docker 中运行多进程程序吗?
【发布时间】:2016-11-25 22:20:45
【问题描述】:

我有一些像这样使用多进程的代码:

import multiprocessing
from multiprocessing import Pool

pool = Pool(processes=100)
result = []

for job in job_list:        
    result.append( 
        pool.apply_async(
            handle_job, (job)
            )
        )
pool.close()
pool.join()

这个程序正在对非常大的数据集进行大量计算。所以我们需要多进程同时处理工作以提高性能。

有人告诉我,对于托管系统,一个 docker 容器只是一个进程。所以我想知道我的多进程将如何在 Docker 中处理?

以下是我的担忧:

  1. 既然容器只是一个进程,那我的多进程代码会不会变成进程中的多线程?

  2. 性能会下降吗?因为我使用多进程的原因是为了同时完成工作以获得更好的性能。

【问题讨论】:

    标签: multithreading docker multiprocessing


    【解决方案1】:

    我怀疑大部分混淆来自于将容器视为轻量级 VM。相反,可以将 Linux 容器视为一种通过命名空间和 cgroup 设置运行进程的方式。

    其中一个命名空间是 pid 命名空间。配置它时,您会看到该命名空间中的第一个进程作为命名空间内的 pid 1。从另一个 pid 命名空间,您看不到那些其他命名空间或主机命名空间。在主机上,在任何命名空间之外,您将看到所有进程,包括在任何命名空间中的进程。

    当你 fork 一个新进程时,你继承了相同的命名空间和 cgroup,因此你将在 pid 命名空间中获得一个新的 pid,允许你像任何其他 Linux 环境一样运行多个进程。在容器内部,您可以运行 ps 命令(假设它包含在您的映像中)并查看多个进程正在运行:

    $ docker run -it --rm busybox /bin/sh
    / # sleep 30s &
    / # ps -ef
    PID   USER     TIME  COMMAND
        1 root      0:00 /bin/sh
        7 root      0:00 sleep 30s
        8 root      0:00 ps -ef
    

    关于仅运行单个进程的建议并非来自多线程应用程序,而是来自将容器视为轻量级 VM 的人们。它们将产生多个彼此没有硬依赖的应用程序,例如 Web 服务器、数据库和邮件服务器。完成后,有几个关键问题:

    • 容器日志不可用。它们要么混杂着多个进程,所有进程都写入同一个 stdout/stderr。或者它们是空的,而将日志写入容器文件系统,而这些文件系统经常会丢失。
    • 错误处理存在问题。如果邮件服务器出现错误,是否应该关闭数据库并重新启动以尝试更正问题?如果不杀掉整个容器,怎么知道邮件服务器宕机了?

    简而言之,管理容器的设计假设每个容器一个应用程序,如果你打破这个假设,当工具不支持你的用例时,你可以保留这两个部分。

    注意几句:

    • 一旦 pid 1 退出,您的容器就会结束,无论您的分叉进程是否仍在运行。这意味着所有进程都被杀死并被回收。
    • 通常在 Linux 上,当父进程在没有等待其子 pid 的情况下死亡时,僵尸进程最终会被以 pid 1 运行的 init 进程收割。此收割进程不会通过 pid 命名空间边界,因此如果您 fork child进程,确保容器内的 pid 1 正在等待这些子进程清理它们。此任务的常见 pid 1 进程是 tini(init 向后拼写)。甚至还有一个标志可以让 docker 为您运行它 (--init)。

    【讨论】:

    • 我试着理解你的话。所以你的意思是码头工人支持多处理。但是这些进程是被进程命名空间隔离的,所以我们不能在 docker 容器之外看到它们,对吧?
    • 进程隔离意味着容器内的进程看不到主机,它们只能看到自己在运行。但是主机将能够看到容器中运行的进程。 docker run -it --rm busybox /bin/sh -c top 来看看这个。
    • 这有助于隔离。但不包括CPU方面。当单个进程容器中的 100 个线程与容器中的 5 个进程中的 20 个线程时,操作系统调度是否会有所不同。
    • @yeehaw 分叉的进程与其父进程保持相同的 cgroup,因此在容器中,进程彼此具有相同的 cgroup,并在同一 pid 命名空间中看到其他进程。一旦命名空间的 cgroup 的 pid 1 终止,容器被视为已停止,并且该 cgroup 中的所有进程都将被杀死/回收。
    • @yeehaw 当容器中的 pid 1 退出时,容器应该停止。要亲自查看,您可以尝试构建一个示例并进行测试
    【解决方案2】:

    是的,请参阅有关主管的文档 https://docs.docker.com/engine/admin/using_supervisord/

    您还可以使用守护程序工具 http://cr.yp.to/daemontools.html

    或 s6 http://skarnet.org/software/s6/

    【讨论】:

    • 您好,非常感谢您的信息。但我认为你的回答不太适合我的问题。也许我确实把我的问题说清楚了。我再次重新编辑了我的问题。请再检查一遍好吗?
    • 我从一个旧的 Docker PR github.com/docker/docker/issues/3090 这个 Yes, applications running in the containers will scale to make use of CPU cores as if they were running outside of a container. 中提取
    • 您可能需要在您的docker run 中指定您允许您的docker 容器使用多少Cpus,从文档docs.docker.com/engine/reference/run--cpuset-cpus="" CPUs in which to allow execution (0-3, 0,1) 中提取
    • 谢谢。似乎多线程不是问题。但我担心的是多处理。如果容器只是一个进程。那我的多进程代码怎么处理呢?
    • 我不是专家,但我见过多进程 docker 容器通过使用主管作为 PID1 来编排进程。 docs.docker.com/engine/admin/using_supervisord
    最近更新 更多