簡體   English   中英

在 Docker 容器內運行的進程的主機中的 PID 是什么?

[英]What is the PID in the host, of a process running inside a Docker container?

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 是什么,那就太好了。

您可以查看/proc/<pid>/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進程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
  • 調用 docker sdk api ContainerList 獲取 container_id 映射 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
}

獲取pid容器的golang代碼

如果您知道主機 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: No such file or directory

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM