Як перевірити, чи виконується процес всередині контейнера докера


85

[Оновлено1] У мене є оболонка, яка змінить параметри ядра TCP у деяких функціях, але тепер мені потрібно зробити цю оболонку запущеною в контейнері Docker, це означає, що оболонка повинна знати, що вона працює всередині контейнера, і припинити налаштування ядра.

Тепер я не знаю, як цього досягти, ось вміст /proc/self/cgroupусередині контейнера:

9:hugetlb:/
8:perf_event:/
7:blkio:/
6:freezer:/
5:devices:/
4:memory:/
3:cpuacct:/
2:cpu:/docker/25ef774c390558ad8c4e9a8590b6a1956231aae404d6a7aba4dde320ff569b8b
1:cpuset:/

Будь-які прапорці вище я можу використати, щоб з’ясувати, чи виконується цей процес всередині контейнера?

[Оновлено2]: Я також помітив, як визначається, чи виконується процес всередині lxc / Docker , але, здається, у цьому випадку це не працює, вміст /proc/1/cgroupмого контейнера:

8:perf_event:/
7:blkio:/
6:freezer:/
5:devices:/
4:memory:/
3:cpuacct:/
2:cpu:/docker/25ef774c390558ad8c4e9a8590b6a1956231aae404d6a7aba4dde320ff569b8b
1:cpuset:/

No / lxc / containerid


Не дуже чітке питання. Навіщо вам це?
Хенк Лангевельд


@fish no / lxc / <containerid> у моєму випадку див. оновлення
harryz

1
Параметри ядра @HenkLangeveld доступні лише для читання в контейнері Docker, тому мені потрібно знати, чи працює моя оболонка всередині контейнерів, і вимкнути функції ядра в моїй оболонці. див. оновлення.
harryz

Деякі кроки у сценарії намагаються змінити параметри ядра, і їх потрібно пропустити під час запуску в Docker. Ясно.
Хенк Лангевельд

Відповіді:


68

Перевірити всередині контейнера Docker, чи перебуваєте ви всередині контейнера Docker, можна через /proc/1/cgroup. Як випливає з цього допису, ви можете зробити наступне:

Поза контейнером докера всі записи в /proc/1/cgroupкінці, /як ви можете бачити тут:

vagrant@ubuntu-13:~$ cat /proc/1/cgroup
11:name=systemd:/
10:hugetlb:/
9:perf_event:/
8:blkio:/
7:freezer:/
6:devices:/
5:memory:/
4:cpuacct:/
3:cpu:/
2:cpuset:/

Усередині контейнера Docker деякі контрольні групи належатимуть Docker (або LXC):

vagrant@ubuntu-13:~$ docker run busybox cat /proc/1/cgroup
11:name=systemd:/
10:hugetlb:/
9:perf_event:/
8:blkio:/
7:freezer:/
6:devices:/docker/3601745b3bd54d9780436faa5f0e4f72bb46231663bb99a6bb892764917832c2
5:memory:/
4:cpuacct:/
3:cpu:/docker/3601745b3bd54d9780436faa5f0e4f72bb46231663bb99a6bb892764917832c2
2:cpuset:/

Відповідь засновника чистіша
Скотт Стенсленд

5
це не зовсім вірно, що "Поза контейнером докера всі записи в / proc / 1 / cgroup закінчуються на /". Наприклад, на ubuntu 16.04 у мене є:12:perf_event:/ 11:blkio:/init.scope 10:cpuset:/ 9:devices:/init.scope 8:hugetlb:/ 7:cpu,cpuacct:/init.scope 6:net_cls,net_prio:/ 5:memory:/init.scope 4:pids:/init.scope 3:rdma:/ 2:freezer:/ 1:name=systemd:/init.scope
samfr

Це майже працює лише на Linux, а не на Дарвіні чи інших BSD, які навіть не використовують procfs.
Крістіан

@Christian Docker / LXC - це лише речі для Linux, так що це добре, правда :)?
Роберт Лакруа

@RobertLacroix, тож ти кажеш, якщо не знайдеш procfs, ти не в Docker? Ну, це досить справедливо, я думаю ...
Крістіан

107

Docker створює .dockerenv та .dockerinit( видаляється у версії 1.1 ) файли у верхній частині дерева каталогів контейнера, тож ви можете перевірити, чи існують вони.

Щось подібне мало би спрацювати.

#!/bin/bash
if [ -f /.dockerenv ]; then
    echo "I'm inside matrix ;(";
else
    echo "I'm living in real world!";
fi

1
Якщо, звичайно, ви чи хтось інший не створили /.dockerinitна вашому хості (можливо, випадково), і в цьому випадку це буде неправильно поза контейнером.
sosiouxme

18
Якщо хтось інший зробив це в / тоді він є root, і у вас гірші проблеми, ніж знання, чи ви в Docker чи ні.
Дейві

15
Остерігайтеся покладатися на /.dockerenvдовгострокову перспективу. Він не призначений для використання таким чином .
ReactiveRaven

fwiw, Підман не створює /.dockerenv. Він створює, /run/.containerenvале за подібною логікою звучить як деталь реалізації, на яку не можна покладатися. Деякі альтернативи для підман див. На github.com/containers/libpod/issues/3586 .
Бені Чернявський-Паскін

21

Рішення Томаса як код:

running_in_docker() {
  (awk -F/ '$2 == "docker"' /proc/self/cgroup | read non_empty_input)
}

Примітка

readЗі змінним манекеном простий ідіоми для Does цих ніякого висновку? . Це компактний метод для перетворення можливо багатослівного grepабо awkв тест на шаблон.

Додаткова примітка щодо прочитаного


10
Крім ... це не вдасться в деяких середовищах, оскільки, наприклад, 3:cpu,cpuacct:/system.slice/docker-1ce79a0dec4a2084d54acf187a1e177e0339dc90d0218b48b4456576ecaf291e.scopeне збігається. Простіше до grep -q docker /proc/1/cgroup; код результату з цього також повинен бути достатнім.
larsks

2
readможе працювати bash, але в найбільш використовуваній dashоболонці вам доведеться використовувати або read dummy(або подібну), або використовувати таку конструкцію, як[ -n "$(command)" ]
Daniel Alder

@DanielAlder Хороший улов, Даніелю. Я оновлю текст.
Хенк Лангевельд

1
Раніше в цьому стверджувалося, що будь-яка оболонка, сумісна з Борном, підтримує рівнину readбез імені змінної. Це справедливо лише для bash та ksh93. Opengroup лише визначає read varта не згадує readповедінку без принаймні однієї змінної. У bash та ksh93 , якщо не вказано var , read використовує змінну оболонки REPLY.
Хенк Лангевельд

1
Чому ми не можемо просто використовувати awk -F: '$3 ~ /docker/' /proc/self/cgroup | read? Працює для мене.
Шубхем Чаудхарі,

21

Ми використовуємо sched (/ proc / $ PID / sched) для вилучення PID процесу. PID процесу всередині контейнера буде відрізнятися, ніж PID на хості (неконтейнерна система).

Наприклад, результат / proc / 1 / sched для контейнера повернеться:

root@33044d65037c:~# cat /proc/1/sched | head -n 1
bash (5276, #threads: 1)

Перебуваючи на неконтейнерному хості:

$ cat /proc/1/sched  | head -n 1
init (1, #threads: 1)

Це допомагає розрізнити, перебуваєте ви в контейнері чи ні. наприклад, ви можете зробити:

if [[ ! $(cat /proc/1/sched | head -n 1 | grep init) ]]; then {
    echo in docker
} else {
    echo not in docker
} fi

це насправді досить цінна інформація. подяка
Фабіан Ланге

4
Залежно від ОС, "init", можливо, доведеться замінити на "systemd". Більше інформації про systemd тут .
BrianV

2
Як згадував @BrianV, це теж не працює для мене.
Шубхем Чаудхарі

5
У контейнері Docker, який працює на кластері k8s, head -n1 /proc/1/schedповертається dumb-init (1, #threads: 1), тому перевірка, запропонована у цій відповіді, не вдається. (Крім того, на відміну від того, що пропонується у відповіді, PID в цьому рядку відображається як "1", хоча я роблю це в контейнері.)
Стефан Маєвський,

Це точно не універсальне рішення. Ви можете (як би) використовувати все, що завгодно, для PID контейнера 1. Наприклад, якщо ви docker run --init ...це зробите, це буде docker-init. Якщо ви це зробите, наприклад, docker run ... head -n 1 /proc/1/schedце буде head.
jpkotta

6

Мені підходить перевірка на наявність номера inode символу '/.' Всередині докера дуже велика кількість. Поза док-станцією його дуже мало, як "2". Я вважаю, що такий підхід також залежатиме від використовуваної FileSystem.

Приклад

Всередині докера:

# ls -ali / | sed '2!d' |awk {'print $1'}
1565265

Поза докером

$ ls -ali / | sed '2!d' |awk {'print $1'}
2

У сценарії:

#!/bin/bash
INODE_NUM=`ls -ali / | sed '2!d' |awk {'print $1'}`
if [ $INODE_NUM == '2' ];
then
        echo "Outside the docker"
else
        echo "Inside the docker"
fi

у MSYS2 ls -ali / | sed '2! d' | awk {'print $ 1'} 232779805740174872
bo0k

те саме, що ls -di /? здається inode num ненадійним на різних платформах
yurenchen

це єдине, що мені вдалося розрізнити хост Xen domU та його контейнер
докера

1

Нам потрібно було виключити процеси, що виконуються в контейнерах, але замість перевірки наявності лише докерних груп ми вирішили порівняти /proc/<pid>/ns/pidз системою init на /proc/1/ns/pid. Приклад:

pid=$(ps ax | grep "[r]edis-server \*:6379" | awk '{print $1}')
if [ $(readlink "/proc/$pid/ns/pid") == $(readlink /proc/1/ns/pid) ]; then
   echo "pid $pid is the same namespace as init system"
else
   echo "pid $pid is in a different namespace as init system"
fi

Або в нашому випадку ми хотіли один лайнер, який генерує помилку, якщо процес НЕ знаходиться в контейнері

bash -c "test -h /proc/4129/ns/pid && test $(readlink /proc/4129/ns/pid) != $(readlink /proc/1/ns/pid)"

який ми можемо виконати з іншого процесу, і якщо код виходу дорівнює нулю, тоді вказаний PID працює в іншому просторі імен.


Не працює у мене. Зсередини запланованого для k8s контейнера Docker readlink /proc/self/ns/pidі видайте readlink /proc/1/ns/pidтой самий результат.
Стефан Маєвський,

1
@StefanMajewsky Можливо, захоче спробувати використовувати github.com/jessfraz/amicontain, щоб побачити, які функції ввімкнено в середовищі виконання контейнера.
Грег Брей

0

На основі коментаря Дана Уолша щодо використання SELinux ps -eZ | grep container_t, але без необхідності psвстановлення:

$ podman run --rm fedora:31 cat /proc/1/attr/current
system_u:system_r:container_t:s0:c56,c299
$ podman run --rm alpine cat /proc/1/attr/current
system_u:system_r:container_t:s0:c558,c813
$ docker run --rm fedora:31 cat /proc/1/attr/current
system_u:system_r:container_t:s0:c8,c583
$ cat /proc/1/attr/current
system_u:system_r:init_t:s0

Це просто говорить вам , що ви працюєте в вигляді контейнера, але не які у час виконання.

Не перевіряв час роботи інших контейнерів, але https://opensource.com/article/18/2/understanding-selinux-labels-container-runtimes надає більше інформації та пропонує, що це широко використовується, може також працювати для rkt та lxc?


-1

Я створив невеликий скрипт на python. Сподіваюся, хтось вважає це корисним. :-)

#!/usr/bin/env python3
#@author Jorge III Altamirano Astorga 2018
import re
import math

total = None
meminfo = open('/proc/meminfo', 'r')
for line in meminfo:
    line = line.strip()
    if "MemTotal:" in line:
        line = re.sub("[^0-9]*", "", line)
        total = int(line)
meminfo.close()
print("Total memory: %d kB"%total)

procinfo = open('/proc/self/cgroup', 'r')
for line in procinfo: 
    line = line.strip()
    if re.match('.{1,5}:name=systemd:', line):
        dockerd = "/sys/fs/cgroup/memory" + \
            re.sub("^.{1,5}:name=systemd:", "", line) + \
            "/memory.stat"
        #print(dockerd)
        memstat = open(dockerd, 'r')
        for memline in memstat:
            memline = memline.strip()
            if re.match("hierarchical_memory_limit", memline):
                memline = re.sub("[^0-9]*", \
                    "", memline)  
                total = math.floor(int(memline) / 2**10)
        memstat.close()
procinfo.close()
print("Total available memory to the container: %d kB"%total)

це круто, але як це допомагає визначити, перебуваєте ви всередині контейнера чи ні?
користувач528025

FileNotFoundError: [Errno 2] No such file or directory: '/sys/fs/cgroup/memory/docker/<docker_id>/memory.stat'
Скрудж Макдак
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.