【问题标题】:What is the PID in the host, of a process running inside a Docker container?在 Docker 容器中运行的进程的主机中的 PID 是什么?
【发布时间】:2017-02-17 06:48:39
【问题描述】:

在一个 Docker 容器中运行着几个进程,它们的 PID 被隔离在容器命名空间中,有没有办法弄清楚它们在 Docker 主机上的 PID 是什么?

例如,有一个 Apache Web 服务器在 Docker 容器中运行, (我使用来自Docker Hub 的 Apache+PHP 映像),Apache 在启动时会在容器内创建更多工作进程。这些工作进程实际上正在处理传入的请求。要查看这些进程,我在 docker 容器内运行 pstree

# pstree -p 1
apache2(1)-+-apache2(8)
           |-apache2(9)
           |-apache2(10)
           |-apache2(11)
           |-apache2(12)
           `-apache2(20)

父 Apache 进程在容器进程命名空间内的 PID 1 上运行。但是从主机的角度来看,它也可以访问, 但它在主机上的PID不同,可以通过运行docker compose命令来确定:

 $ docker inspect --format '{{.State.Pid}}' container
 17985

由此我们可以看到,容器进程命名空间中的 PID 1 映射到主机上的 PID 17985。所以我可以在主机上运行pstree,列出Apache进程的子进程:

$ pstree -p 17985
apache2(17985)─┬─apache2(18010)
               ├─apache2(18011)
               ├─apache2(18012)
               ├─apache2(18013)
               ├─apache2(18014)
               └─apache2(18164)

据此,我假设容器中的 PID 1 映射到主机上的 PID 17985 的方式相同,它也映射:

  • 容器中的 PID 8 到主机上的 PID 18010,并且
  • PID 9 到 PID 18011;
  • PID 10 到 PID 18012 等等……

(这允许我从 docker 容器中调试进程,使用仅在主机上可用的工具,而不是在容器中可用的工具,如 strace)

问题是我不知道假设 pstree 在容器和主机中以相同的顺序列出进程有多安全。

如果有人能提出一种更可靠的方法来检测在 Docker 容器内运行的特定进程的主机上的 PID 是什么,那就太好了。

【问题讨论】:

  • pid 也可以表示操作系统线程,至少在 linux 上是这样。您可以通过创建多个线程并计算您获得的 PIDS 来检查 java(或其他语言)。

标签: linux docker process pid


【解决方案1】:

如果您知道主机 pid 或容器 pid,您可以通过搜索主机上的所有 NSpid 映射来找到,如下所示:

# grep NSpid.*10061 /proc/*/status 2> /dev/null
/proc/1194200/status:NSpid: 1194200 10061
  • 1194200 是主机 pid
  • 10061是容器的pid

2>/dev/null 是忽略导致 grep 错误的短期进程,如下所示: grep: /proc/1588467/status: 没有这样的文件或目录

【讨论】:

  • 答案是正确且简短的,但与@larsks 答案中的原理基本相同,实际上更详细。我为了简洁起见,但我留下了与现在接受的相同的答案。谢谢
【解决方案2】:
  • 调用docker sdk api ContainerList 获取container_id map docker image
  • 读取/proc/<pid>/cgroup获取docker container_id
  • 可以获取pid、container_id、docker镜像
func GetContainerID(pid int32) string {
    cgroupPath := fmt.Sprintf("/proc/%s/cgroup", strconv.Itoa(int(pid)))
    return getContainerID(cgroupPath)
}

func GetImage(containerId string) string {
    if containerId == "" {
        return ""
    }
    image, ok := containerImage[containerId]
    if ok {
        return image
    } else {
        return ""
    }
}
func getContainerID(cgroupPath string) string {
    containerID := ""
    content, err := ioutil.ReadFile(cgroupPath)
    if err != nil {
        return containerID
    }
    lines := strings.Split(string(content), "\n")
    for _, line := range lines {
        field := strings.Split(line, ":")
        if len(field) < 3 {
            continue
        }
        cgroup_path := field[2]
        if len(cgroup_path) < 64 {
            continue
        }
        // Non-systemd Docker
        //5:net_prio,net_cls:/docker/de630f22746b9c06c412858f26ca286c6cdfed086d3b302998aa403d9dcedc42
        //3:net_cls:/kubepods/burstable/pod5f399c1a-f9fc-11e8-bf65-246e9659ebfc/9170559b8aadd07d99978d9460cf8d1c71552f3c64fefc7e9906ab3fb7e18f69
        pos := strings.LastIndex(cgroup_path, "/")
        if pos > 0 {
            id_len := len(cgroup_path) - pos - 1
            if id_len == 64 {
                //p.InDocker = true
                // docker id
                containerID = cgroup_path[pos+1 : pos+1+64]
                // logs.Debug("pid:%v in docker id:%v", pid, id)
                return containerID
            }
        }
        // systemd Docker
        //5:net_cls:/system.slice/docker-afd862d2ed48ef5dc0ce8f1863e4475894e331098c9a512789233ca9ca06fc62.scope
        docker_str := "docker-"
        pos = strings.Index(cgroup_path, docker_str)
        if pos > 0 {
            pos_scope := strings.Index(cgroup_path, ".scope")
            id_len := pos_scope - pos - len(docker_str)
            if pos_scope > 0 && id_len == 64 {
                containerID = cgroup_path[pos+len(docker_str) : pos+len(docker_str)+64]
                return containerID
            }
        }
    }
    return containerID
}

The golang code with get the pid container

【讨论】:

    【解决方案3】:

    您可以查看/proc/&lt;pid&gt;/status 文件以确定命名空间PID 和全局PID 之间的映射。例如,如果在 docker 容器中我启动了多个 sleep 900 进程,如下所示:

    # docker run --rm -it alpine sh
    / # sleep 900 &
    / # sleep 900 &
    / # sleep 900 &
    

    我可以看到它们在容器中运行:

    / # ps -fe
    PID   USER     TIME   COMMAND
        1 root       0:00 sh
        7 root       0:00 sleep 900
        8 root       0:00 sleep 900
        9 root       0:00 sleep 900
       10 root       0:00 ps -fe
    

    我可以在主机上查看这些:

    # ps -fe | grep sleep
    root     10394 10366  0 09:11 pts/10   00:00:00 sleep 900
    root     10397 10366  0 09:12 pts/10   00:00:00 sleep 900
    root     10398 10366  0 09:12 pts/10   00:00:00 sleep 900
    

    对于其中任何一个,我可以查看 status 文件以查看命名空间 pid:

    # grep -i pid /proc/10394/status
    Pid:    10394
    PPid:   10366
    TracerPid:  0
    NSpid:  10394   7
    

    查看 NSpid 行,我可以看到在 PID 命名空间内,该进程的 pid 为 7。事实上,如果我在主机上终止进程 10394

    # kill 10394
    

    然后在容器中我看到 PID 7 不再运行:

    / # ps -fe
    PID   USER     TIME   COMMAND
        1 root       0:00 sh
        8 root       0:00 sleep 900
        9 root       0:00 sleep 900
       11 root       0:00 ps -fe
    

    【讨论】:

    • 看起来它解决了我的问题,按照这个建议,我可以用这个单行检索 PID 的完整映射:for i in $(ps -ef | grep $(docker inspect --format '{{.State.Pid}}' php-sandbox) | awk '{print $2}') ; do grep NSpid: /proc/$i/status ; done
    • 请注意,NSpid 仅在 Linux 4.1 之后可用。所以这可能不适用于旧机器。作为替代方案,您可以通过 docker exec'ing grep -l pid /proc/*/sched 找到容器内 pid。
    • 这似乎不适用于 Mac。当我运行ps 时,docker top 命令给出的 pid 不存在。其次,Mac 上没有 /proc。
    • 这是因为在 Mac 上,您在 Linux VM 中运行 Docker。如果您要登录 Linux VM,您会看到此处描述的行为。
    • gr8 问答。这对我有用 - for i in $(ps -ef | grep `docker inspect --format '{{.State.Pid}}' containerID` | awk '{print $2}') ; do grep NSpid: /proc/$i/status ; done。任何人都可以提出为什么原件不起作用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-06-23
    • 1970-01-01
    • 1970-01-01
    • 2019-10-13
    • 1970-01-01
    • 2019-08-23
    • 1970-01-01
    相关资源
    最近更新 更多