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