我们能在Docker中运行多进程程序吗?

19

我有一些使用多进程的代码,如下:

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. 性能会受到影响吗?因为我使用多进程的原因是要同时完成任务以获得更好的性能。

2个回答

13

我怀疑很多人把容器想象成轻量级虚拟机而导致混淆。相反,把Linux容器看作是一种使用一些名称空间和cgroup设置运行进程的方式。

其中一个名称空间是pid名称空间。当你配置它时,你可以从该名称空间内以pid 1的形式看到该名称空间中的第一个进程。在另一个pid名称空间中,你无法看到其他名称空间或主机名称空间。在主机之外、任何名称空间之外,你将看到所有进程,包括任何名称空间中的进程。

当你fork一个新进程时,你继承相同的名称空间和cgroups,因此你将获得一个新的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

单进程容器的建议并非来自多线程应用程序,而是来自将容器视为轻量级虚拟机的人。他们将生成多个彼此没有硬依赖关系的应用程序,例如Web服务器、数据库和邮件服务器。这样做会出现一些关键问题:

  • 容器日志无法使用。它们要么混杂着多个进程同时写入相同的标准输出/标准错误流,要么为空,而日志则被写入容器文件系统中,经常会丢失。
  • 错误处理有问题。如果邮件服务器出现错误,是否应该关闭和重新启动数据库以尝试纠正问题?如果您不杀死整个容器,如何知道邮件服务器已关闭?

简而言之,管理容器的设计假定每个容器一个应用程序,如果您打破了这个假设,则在工具不支持您的用例时,您将得到两个碎片。

需要注意的几点:

  • 一旦pid 1退出,您的容器就会结束,而不管您的分叉进程是否仍在运行。这意味着所有进程都将被终止和收回。
  • 通常在Linux上,当父进程死亡而不等待其子pid时,僵尸进程最终将被init进程作为pid 1收回。此收割过程不会传递pid命名空间边界,因此如果您分叉子进程,请确保容器内的pid 1正在等待这些子进程以清理它们。用于此任务的常见pid 1进程是tini(倒序拼写的init)。甚至有一个标志可以让docker为您运行它(--init)。

我试图理解你的话。所以你的意思是Docker支持多进程。但是这些进程被进程命名空间隔离,所以我们在Docker容器外部看不到它们,对吗? - Kramer Li
进程隔离意味着容器内的进程无法看到主机,它们只能看到自己在运行。但是主机将能够看到容器中正在运行的进程。使用 docker run -it --rm busybox /bin/sh -c top 命令可以查看此过程。 - BMitch
@BMitch 你好!我想知道我的理解是否正确 - 当在容器内运行时,我的多进程Python代码仍然在多个进程中运行,但是(1)每个进程是否与其他进程隔离?还是这些进程整体上是隔离的?(2)当容器停止时,派生的子进程继续运行,这些僵尸进程是否仍然与其余僵尸进程隔离,还是这些僵尸进程整体上是隔离的?我知道我的问题可能不太清楚,对此我很抱歉。 - yeehaw
2
@yeehaw 分叉进程与其父进程保持相同的cgroup,因此在容器中,这些进程具有相同的cgroup,并且可以看到同一pid命名空间中的其他进程。一旦该命名空间的cgroup的pid 1死亡,容器被视为停止,并且该cgroup中的所有进程都将被杀死/收割。 - BMitch
1
@yeehaw 容器应该在容器内的 PID 1 退出时停止。为了验证,您可以尝试构建一个示例并进行测试。 - BMitch
显示剩余3条评论

0

嗨,非常感谢您的信息。但我认为您的答案并不是很适合我的问题。也许我没有表达清楚我的问题。我已经重新编辑了我的问题。您能再次检查一下吗? - Kramer Li
我从一个旧的 Docker PR https://github.com/docker/docker/issues/3090 中提取了以下内容:是的,运行在容器中的应用程序将会像在容器外部运行一样扩展以利用 CPU 核心。 - user2915097
在你的 docker run 中,你可能需要指定允许你的 Docker 容器使用多少个 CPU。可以从文档 https://docs.docker.com/engine/reference/run/ 中提取 --cpuset-cpus="" 参数来指定允许执行的 CPU(0-3, 0,1)。 - user2915097
谢谢。多线程似乎不是问题。但我担心的是多进程。如果容器只有一个进程,那么我的多进程代码将如何处理? - Kramer Li
我不是专家,但我曾经看到过使用supervisor作为PID1来编排进程的多进程docker容器运行。 - Dave

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接