如何检查进程是否在Docker容器中运行


85

[Updated1]我有一个外壳程序,它将在某些功能中更改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:/

我可以使用上面的任何标志来确定此过程是否在容器内运行吗?

[Updated2]:我还注意到确定进程是否在lxc / Docker中运行,但在这种情况下似乎不起作用/proc/1/cgroup,我容器中的内容为:

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

否/ lxc / containerid


这不是一个很明确的问题。你为什么需要这个?
Henk Langeveld


@fish no / lxc / <containerid>在我的情况下,请参阅更新
harryz 2014年

1
@HenkLangeveld内核参数在Docker容器中是只读的,因此我需要知道我的shell是否在容器中运行,并在我的shell中禁用内核功能。查看更新。
哈里斯2014年

脚本中的某些步骤尝试修改内核参数,并且在Docker中运行时需要跳过。明确。
Henk Langeveld

Answers:


68

要检查Docker容器内是否在Docker容器内,可以通过进行/proc/1/cgroup。正如这篇文章所建议的,您可以执行以下操作:

在docker容器之外,所有条目都以/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:/

@Founder的答案更干净
Scott Stensland

5
“在docker容器之外/ 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 '18

这几乎只适用于Linux,不适用于Darwin或什至不使用procfs的其他BSD。
基督教徒

@Christian Docker / LXC只是Linux的东西,所以很好,对吧:)?
罗伯特·拉克鲁瓦

@RobertLacroix,所以您说的是如果找不到某个procfs,那么您不在Docker中吗?好吧,我猜这足够公平了……
Christian

107

Docker在容器的目录树顶部创建.dockerenv.dockerinit在v1.11中删除了)文件,因此您可能要检查它们是否存在。

这样的事情应该起作用。

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

1
当然,除非您或您的其他人已经/.dockerinit在您的主机上创建了(也许是偶然的),否则在容器外部将是错误的。
sosiouxme,2015年

18
如果其他人在/中创建了根目录,那么他们会比知道自己是否在docker中更难。
davey 2015年

15
/.dockerenv长期要当心。它不打算使用这种方式
ReactiveRaven

首先,Podman不创建/.dockerenv。它的确创建了,/run/.containerenv但是通过类似的逻辑,听起来像实现细节是不依赖的。有关特定于podman的替代方案,请参见github.com/containers/libpod/issues/3586
贝尼·切尔尼亚夫斯基-帕斯金

21

托马斯的解决方案作为代码:

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

注意

read与一个虚拟变量是一个简单的成语,这是否产生任何输出?。它是把一个可能的详细的一个紧凑的方法grepawk测试图案。

阅读时的附加说明


10
除非...这在某些环境中将失败,因为,例如,3:cpu,cpuacct:/system.slice/docker-1ce79a0dec4a2084d54acf187a1e177e0339dc90d0218b48b4456576ecaf291e.scope将不匹配。更简单grep -q docker /proc/1/cgroup; 结果代码也应该足够了。
larsk

2
read可能适用bash,但是在最常用的dash外壳中,您必须使用read dummy(或类似的)或使用类似的结构[ -n "$(command)" ]
Daniel Alder

@DanielAlder好,丹尼尔。我将更新文本。
Henk Langeveld

1
此前,它声称任何Bourne兼容外壳程序都支持read无变量名称的纯文本。这仅适用于bash和ksh93。在没有至少一个变量的情况下,Opengroup仅指定read var并且不提及read行为。在bashksh93中,如果未给出var,则read使用shell变量REPLY
Henk Langeveld

1
我们为什么不能只使用awk -F: '$3 ~ /docker/' /proc/self/cgroup | read呢?为我工作。
Shubham Chaudhary

21

我们使用proc的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

这实际上是相当有价值的信息。谢谢
Fabian Lange

4
根据操作系统的不同,可能需要将“ init”替换为“ systemd”。有关systemd的更多信息,请参见此处
BrianV

2
如@BrianV所述,这对我也不起作用。
Shubham Chaudhary

5
在k8s集群上运行的Docker容器中,head -n1 /proc/1/sched返回dumb-init (1, #threads: 1),因此此答案中建议的检查失败。(此外,与答案所暗示的相反,尽管我在容器中执行此操作,但PID在该行中显示为“ 1”。)
Stefan Majewsky,

这绝对不是一个通用的解决方案。你可以(在某种程度上)任何你想要的容器的PID 1.例如,如果你使用docker run --init ...它会docker-init。如果你这样做的docker run ... head -n 1 /proc/1/schedhead
jpkotta

6

对我有用的是检查“ /”的索引节点号。在docker内部,其数量非常高。在docker外部,其数字非常低,例如“ 2”。我认为这种方法还取决于所使用的FileSystem。

在docker内部:

# 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主机及其
Docker

1

我们需要排除在容器中运行的进程,但是我们决定不与docker cgroups进行比较/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/pidreadlink /proc/1/ns/pid产生相同的输出。
Stefan Majewsky,

1
@StefanMajewsky可能想尝试使用github.com/jessfraz/amicontained来查看在容器运行时中启用了哪些功能。
格雷格·布雷

0

基于Dan Walsh关于使用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)

很酷,但是如何确定您是否在容器中呢?
user528025 '19

FileNotFoundError: [Errno 2] No such file or directory: '/sys/fs/cgroup/memory/docker/<docker_id>/memory.stat'
Scrooge McDuck
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.