Как определить, работает ли процесс внутри lxc / Docker?

172

Есть ли способ определить, выполняется ли процесс (скрипт) внутри контейнера lxc (~ среда выполнения Docker)? Я знаю, что некоторые программы могут определить, работают ли они внутри виртуальной машины, доступно ли что-то подобное для lxc / docker?

Мате Варга
источник
Это может показаться педантичным, но было бы лучше перефразировать ваш вопрос, описать проблему, с которой вы столкнулись, и спросить, как ее решить - без этого у вопроса больше шансов быть закрытым. Во многих случаях это сложно сделать, но в вашем случае было бы несложно просто перефразировать, если хотите.
мах
есть интересный ответ при выполнении этой команды в контейнере: uptime
Скотт Стенсланд

Ответы:

170

Самый надежный способ - это проверить /proc/1/cgroup. Он скажет вам контрольные группы процесса инициализации, и когда вы не в контейнере, это будет /для всех иерархий. Когда вы находитесь внутри контейнера, вы увидите название точки привязки. С контейнерами LXC / Docker это будет что-то вроде /lxc/<containerid>или /docker/<containerid>соответственно.

jpetazzo
источник
13
Докер теперь использует dockerвместо lxcэтих путей
Энди
4
Не работает для контейнеров lxd / lxc, но работает stackoverflow.com/a/20010626/170230 .
Драко Атер
В более поздних версиях systemd похоже, что вы не можете полагаться на использование процесса 1 /для всех cgroups; в моей системе Debian 9 (systemd 232) только три из десяти cgroups ( 3:cpuset, 4:perf_eventи 7:freezer) находятся в корне; остальные находятся под /init.scope. Тем не менее, я думаю, что поиск этого файла, :/docker/возможно, является наиболее надежной эвристикой на данный момент.
CJS
2
grep 'docker\|lxc' /proc/1/cgroupу меня работает на Docker 18.09.
rypel
1
Не работает для меня Хост Ubuntu 19.04, гостевой Ubuntu 18.04 с использованием привилегированного контейнера LXC. / proc / 1 / cgroup НЕ содержит строку lxc.
Габ
158

Docker создает .dockerenvфайл в корне дерева каталогов внутри контейнера. Вы можете запустить этот скрипт для проверки

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


Еще: Ubuntu на самом деле имеет скрипт bash: /bin/running-in-containerи он может возвращать тип контейнера, в котором он был вызван. Может быть полезным. Не знаю о других крупных дистрибутивах.

at0S
источник
13
Важное примечание: .dockerinitфайл был удален в последних версиях Docker , поэтому этот метод больше не будет работать. На момент написания, .dockerenvфайл все еще хранится, так что, возможно, его можно будет использовать вместо этого.
Джейсон Р
На Debian /bin/running-in-containerпредоставляется upstart. С переходом на systemd это может уйти. Я надеюсь, что нет - это звучит полезно!
Макс Мерфи
"поверх дерева каталогов", что это значит? где это?
Александр Миллс
3
Другие отмечают, что проверка .dockerenvявляется не рекомендуется
Dave
1
Примечание. Тестирование для .dockerenv работает только в том случае, если среда выполнения является демоном Docker. Если вы используете podman или что-то еще, это не поможет.
Бенджамин Кирхер
22

В новой системе Ubuntu 16.04, новом systemd & lxc 2.0

sudo grep -qa container=lxc /proc/1/environ
Larss
источник
Это работает для меня на Ubuntu Focal 20.04. Ни один из ответов выше этого пункта не сделал.
Джонатан Хартли
16

Краткий способ проверить наличие докера в скрипте bash:

#!/bin/bash
if grep docker /proc/1/cgroup -qa; then
   echo I'm running on docker.
fi
oNaiPs
источник
14

Удобная функция Python для проверки работы в Docker:

def in_docker():
    """ Returns: True if running in a Docker container, else False """
    with open('/proc/1/cgroup', 'rt') as ifh:
        return 'docker' in ifh.read()
JJC
источник
2
Важная заметка! Это не работает, когда контейнер работает в kubernetes. Вместо этого замените последнюю строку на kubepod вместо docker. (Или, вставьте оператор «или», который проверяет оба;))
JJC
1
Это kubepodsя думаю.
rookie099
9

Мы используем схему процесса (/ 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)

Это помогает отличить, если вы находитесь в контейнере или нет.

основатель
источник
В зависимости от операционной системы, «init» может потребоваться заменить на «systemd». Больше информации о systemd здесь .
BrianV
Да, но точка не была именем процесса инициализации, точка была номером процесса.
MillerGeek
Кажется, это работает только на Docker. В контейнере LXC возвращается Systemd PID 1
MillerGeek
Теперь он также возвращает 1 в докере. Это обычно shи не initтам, но это может быть почти что-либо в любом.
Ян Худек
Под докером, это больше не так -bash-5.0# cat /proc/1/sched bash (1, #threads: 1)
шаломб
5

Самый простой способ - проверить окружающую среду. Если у вас есть container=lxcпеременная, вы находитесь в контейнере.

В противном случае, если вы являетесь пользователем root, вы можете попытаться выполнить mknodили выполнить mountоперацию, если она не удастся, вы, скорее всего, окажетесь в контейнере с удаленными возможностями.

creack
источник
Этот работает не только для докера (я этого не проверял), но, что более важно, для контейнеров lxd / lxc (проверено), где /proc/1/cgroupвы не можете это обнаружить.
Драко Атер
2
Вы можете отредактировать ответ с помощью кода вместо псевдокода? "container = lxc"? ничего не подходит. Вы имеете в виду что-то вроде if [["lxc" = "$ container"]]?
Александр Миллс
3
Я имею в виду ... это странно, обычно переменные env находятся во всех заглавных буквах, поэтому ищите некоторую точность здесь
Александр Миллс
7
docker run alpine envне дает ничего похожего на эту переменную
Архимед Траяно,
3

Мой ответ применим только к процессам Node.js, но может быть актуален для некоторых посетителей, которые сталкиваются с этим вопросом и ищут конкретный ответ Node.js.

У меня была та же проблема, и я полагался на то, /proc/self/cgroupчто создал пакет npm исключительно для этой цели - чтобы определить, выполняется ли процесс Node.js внутри контейнера Docker или нет.

Контейнерный модуль НПМ поможет вам в Node.js. В настоящее время он не тестируется в Io.js, но может также работать и там.

Мартин Таджур
источник
Спасибо за этот модуль, кажется, ожидается несколько открытых исправлений - вы все еще поддерживаете это?
Stevokk
2

Проверьте все решения выше в Python:

import os

def in_container():
    proc_1 = r'/proc/1/sched'

    if os.path.exists(proc_1):
        with open(proc_1, 'r') as fp:
            out = fp.read()
    else:
        out = ''

    checks = [
        'docker' in out,
        '/lxc/' in out,
        out.split(' ')[0] not in ('systemd', 'init',),
        os.path.exists('./dockerenv'),
        os.path.exists('/.dockerinit'),
        os.getenv('container') is not None
    ]
    return any(checks)


if __name__ == '__main__':
    print(in_container())

Подтверждение концепции:

$ docker run --rm -it --mount type=bind,source=${PWD}/incontainer.py,target=/tmp/script.py python:3 python /tmp/script.py
True
blakev
источник
Это не сработало для меня на док-контейнере для Mac. Возвращает пустой. Докер версии 2.1.0.1 (37199).
splintercell
Это сделал: def is_non_docker(): return os.path.exists('/proc/1/cgroup')согласно принятому ответу здесь stackoverflow.com/questions/20010199/…
splintercell
2
Вы получаете награду «Бесполезное использование кошек». И бесполезное использование Subprocess One.
Ян Худек
Да, это совершенно новый уровень ненужного cat! Хороший :-D
Тимммм
Вы правы, я обновлю ответ, хотя он все еще не является всеобъемлющим. @JanHudec
Blakev
1

Докер развивается день ото дня, поэтому мы не можем точно сказать, собираются ли они остаться .dockerenv .dockerinitв будущем.

В большинстве версий Linux initэто первый процесс для запуска. Но в случае контейнеров это не так.

#!/bin/bash
if ps -p1|grep -q init;then  
  echo "non-docker" 
else 
  echo "docker" 
fi
Говинд Кайлас
источник
6
@RomanTrofimov LXC / Docker тоже нет. Какой забавный комментарий.
abourget
1
Это не работает в Centos 7, а также. Когда я бегу на своем хост-компьютере, он говорит Docker. Похоже, systemd работает как идентификатор процесса 1
Venkateswara Rao
@VenkateswaraRao - это должно быть выполнено внутри контейнера. Цель состоит в том, чтобы выяснить, находитесь ли вы в док-контейнере или нет.
Говинд Кайлас
1
@GovindKailas: Проблема в том, что это предполагает, что нормальный PID init, который не соответствует действительности systemdили launchdсистем на основе ...
Герт ван ден Берг
3
@SamThomas: launchd, upstart, Solaris SMF, systemd, init в стиле Sys V, init в стиле BSD ( initхотя эти два и некоторые другие могут называть их PID 1 ), OpenRC, initng, runit. Смотрите здесь . Большинство современных систем на основе Linux будет использовать systemd, некоторые старые, выскочки .... Все современные системы OS X будут использоватьlaunchd
Герт ван ден Берг
0

Это SO Q & A: «Узнайте, работает ли ОС в виртуальной среде» ; хотя это не то же самое, что вопрос ОП, он действительно отвечает на распространенные случаи определения того, в каком контейнере вы находитесь (если вообще).

В частности, установите и прочитайте код этого bash-скрипта, который, кажется, работает довольно хорошо:

вирт-что :

sudo apt install virt-what
kaiwan
источник
Не работает с virt-whatверсией 1.14-1 в Ubuntu 16.04. Нужен патч.
Лукас
0

Я перевел ответ JJC на рубин

def in_docker
  File.open('/proc/1/cgroup', 'rt') do |f|
    contents = f.read
    return contents =~ /docker/i || contents =~ /kubepod/i
  end
rescue StandardError => e
  p 'Local development'
  p e
  false
end
Сурадип Нанда
источник
-1

В Docker-контейнере записи /proc/self/cgroup монтируются в cgroups на хосте.

например, в контейнере

# awk -F: '/cpuset/' /proc/self/cgroup
3:cpuset:/docker/22bd0c154fb4e0d1b6c748faf1f1a12116acc21ce287618a115ad2bea41256b3

тогда как на хосте тоже самое

$ awk -F: '/cpuset/' /proc/self/cgroup
3:cpuset:/

Использование чего-то в оболочке для низкопрофильного теста

is_running_in_container() {
  awk -F: '/cpuset/ && $3 ~ /^\/$/{ c=1 } END { exit c }' /proc/self/cgroup
}

if is_running_in_container; then
  echo "Aye!! I'm in a container"
else 
  echo "Nay!! I'm not in a container"
fi
shalomb
источник
Возвращает 1 на обоих.
сорин
-4

Может быть, это сделать трюк:

if [ -z $(docker ps -q) ]; then
    echo "There is not process currently running"
else
    echo "There are processes running"
fi

Это то, что вы хотите? Надеюсь, это поможет =)

Леонардо да Винчи
источник
1
Нет dockerдвоичная доступен с внутренней стороны контейнера, очевидно.
Торининген
3
Хм, это может не сработать в ситуациях (например, gitlab docker-in-docker), где у контролирующего контейнера есть dockerдоступ к сокету docker хоста.
шалом
1
да, ты прав, конечно, нет ^^. Я получил неправильное толкование вопроса еще тогда, когда читал его. Спасибо, Шаломб.
Леонардо да Винчи