Каков PID в хосте процесса, запущенного внутри контейнера Docker?

#linux #docker #process #pid

#linux #docker #процесс #pid

Вопрос:

В контейнере Docker запущено несколько процессов, их PID изолированы в пространстве имен контейнера, есть ли способ выяснить, каковы их PID на хосте Docker?

Например, внутри контейнера Docker работает веб-сервер Apache (я использую изображение Apache PHP из Docker Hub), и Apache, когда он запускается, создает больше рабочих процессов внутри контейнера. Эти рабочие процессы фактически обрабатывают входящие запросы. Чтобы просмотреть эти процессы, которые я запускаю pstree внутри контейнера docker:

 # 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 перечисляет процессы в одном и том же порядке как в контейнере, так и в хосте.

Было бы здорово, если бы кто-нибудь мог предложить более надежный способ определить, что такое PID на хосте определенного процесса, запущенного внутри контейнера Docker.

Комментарии:

1. PID также могут означать потоки операционной системы, по крайней мере, в Linux. Вы можете проверить с помощью java (или другого языка), создав несколько потоков и подсчитав полученные PID.

Ответ №1:

Вы можете просмотреть /proc/<pid>/status файл, чтобы определить соответствие между PID пространства имен и глобальным PID. Например, если в контейнере docker я запускаю несколько sleep 900 процессов, например:

 # docker run --rm -it alpine sh
/ # sleep 900 amp;
/ # sleep 900 amp;
/ # sleep 900 amp;
  

Я вижу, что они работают в контейнере:

 / # 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
  

Комментарии:

1. Похоже, это решает мою проблему, следуя этому совету, я могу получить полное сопоставление PID с помощью этого однострочного: for i in $(ps -ef | grep $(docker inspect --format '{{.State.Pid}}' php-sandbox) | awk '{print $2}') ; do grep NSpid: /proc/$i/status ; done

2. Обратите внимание, что NSpid доступен только с Linux 4.1. так что это может не сработать на старых машинах. В качестве альтернативы вы можете найти pid в контейнере, выполнив docker grep -l pid /proc/*/sched .

3. Похоже, это не работает на Mac. Pid, заданный docker top командой, не существует при запуске ps . Во-вторых, на Mac нет /proc .

4. Это потому, что на Mac вы запускаете Docker в виртуальной машине Linux. Если бы вы вошли в виртуальную машину Linux, вы бы увидели поведение, описанное здесь .

5. gr8 Вопросы и ответы и C. это сработало для меня — for i in $(ps -ef | grep `docker inspect --format '{{.State.Pid}}' containerID` | awk '{print $2}') ; do grep NSpid: /proc/$i/status ; done . кто-нибудь может подсказать, почему оригинал не сработал.

Ответ №2:

Если вы знаете либо 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: нет такого файла или каталога

Комментарии:

1. Ответ правильный и короткий, но по сути тот же принцип, что и в ответе @larsks, который на самом деле более подробный. Я это для краткости, но я оставляю тот же ответ, что и принятый на данный момент. Спасибо

2. Хотя на практике это может быть достаточно близко для пользователя-человека, не следует использовать это решение в сценариях. Нет никакой гарантии, что не будет другого PID с суффиксом 10061, который может выдавать два или более результатов вместо одного.

Ответ №3:

  • вызовите docker sdk api ContainerList получите container_id map docker image
  • прочитайте /proc/<pid>/cgroup get 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 amp;amp; id_len == 64 {
                containerID = cgroup_path[pos len(docker_str) : pos len(docker_str) 64]
                return containerID
            }
        }
    }
    return containerID
}
  

Код golang с получением контейнера pid