如何确定进程是否在lxc/Docker容器中运行?

252

有没有办法确定进程(脚本)是否在lxc容器(~ Docker运行时)中运行?我知道有些程序能够检测它们是否在虚拟机中运行,是否有类似的方法适用于lxc/docker?


可能看起来有些迂腐,但最好重新措辞你的问题,描述你遇到的问题并询问如何解决它 - 如果没有这样做,问题被关闭的可能性更高。在许多情况下,这种改变很难实现,但在你的情况下,如果你愿意,简单地重新措辞就不难。 - mah
在容器内部执行以下命令时会得到一个有趣的响应:uptime。 - Scott Stensland
19个回答

246

Docker在容器内部的目录树根目录创建了一个.dockerenv文件。可以通过执行ls -la /.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,它可以返回所调用的容器类型。这可能会很有帮助。不过其他主要的发行版我就不清楚了。

4
在Debian上,“/bin/running-in-container”是由“upstart”提供的。随着转向systemd,它可能会消失。我希望不会 - 它听起来很有用! - Max Murphy
9
有人指出检查 .dockerenv不推荐的做法 - Dave
7
注意:仅当运行时为docker守护进程时,才能测试.dockerenv。如果您正在使用podman或其他工具,则此方法将失败。 - Ben Kircher
2
现在是2020年8月,/.dockerenv在ubuntu:20.04和alpine:3.12上都可用,所以我认为这绝对是最好的答案。 - Meir Gabay
3
Ubuntu 18.0.4没有/bin/running-in-container - Daniel Griscom
显示剩余3条评论

208

最可靠的方法是检查 /proc/1/cgroup。它会告诉你 init 进程的控制组,当你不在容器内时,所有层级的控制组都将显示为/。当你容器内部时,你将看到主机点的名称。针对 LXC/Docker 容器,分别为 /lxc/<containerid>/docker/<containerid>


14
Docker现在在这些路径中使用docker而不是lxc - Andy
6
无法在lxd/lxc容器中使用,但是https://dev59.com/qGIj5IYBdhLWcg3wq21z#20010626可以。 - Draco Ater
3
在Docker 18.09上,grep 'docker\|lxc' /proc/1/cgroup 对我有效。 - rypel
4
无法工作。主机为Ubuntu 19.04,使用LXC特权容器的客户机为Ubuntu 18.04。 /proc/1/cgroup文件中不包含lxc字符串。 - Gab
10
关于我的 Docker 镜像:cat /proc/1/cgroup 0::/ 无法运行。 - Brett Sutton
显示剩余8条评论

29

在一个新的ubuntu 16.04系统上,使用新的systemd和lxc 2.0版本。

sudo grep -qa container=lxc /proc/1/environ

2
这对我在Ubuntu focal 20.04上有效。在此之前的任何答案都没有起作用。 - Jonathan Hartley
谢谢!它适用于lxc!您能否解释为什么需要“-a”?grep -q container=lxc /proc/1/environ不足以吗? - Alek
1
/proc/$$/environ 用空字节分隔环境变量。如果没有使用 -a,则 man 手册中的以下内容适用:
默认情况下,TYPE 是二进制的,当 grep 发现 null 输入二进制数据后,它会抑制输出。
- Adam Azarchs
对我来说,使用 podman,/proc/1/environ 包含 container=podman。因此我使用了 grep -qa '^container=' - fstamour

20

在bash脚本中检查Docker/LXC的简洁方法是:

#!/bin/bash
if grep -sq 'docker\|lxc' /proc/1/cgroup; then
   echo "I am running on Docker."
fi

谢谢@DanielGriscom,看起来好多了。 - oNaiPs
当我的容器在 Kubernetes 中运行时,这个方法没能生效。 - erik
请注意,由于“I'm”中有单引号,因此echo命令应该使用双引号。 - TommyD

17

一个检查是否在 Docker 中运行的 Python 实用函数:

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()

5
重要提示!当容器在 Kubernetes 中运行时,似乎无法正常工作。取而代之的是,在最后一行将“docker”替换为“kubepod”。(或者,加入一个“或”语句来同时检查两者;) - JJC
2
我猜是 kubepods - rookie099

11
我们使用 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)

这有助于区分您是否在容器中。


根据操作系统,可能需要将“init”替换为“systemd”。有关systemd的更多信息,请点击此处 - BrianV
是的,但重点不在于init进程的名称,而在于进程号。 - MillerGeek
2
这似乎只适用于Docker。在LXC容器中,它返回Systemd PID 1。 - MillerGeek
3
现在它在 Docker 中也返回 1。通常是 sh 而不是 init,但在任何一个中都可以是几乎任何东西。 - Jan Hudec
4
在Docker下,情况已经不同了 - bash-5.0# cat /proc/1/sched bash (1, #threads: 1) - shalomb

10
截至2022年,随着lxd v4.0+的推出,到目前为止所提供的答案都不能同时适用于docker和lxc。
- .dockerenv文件对非docker容器无效。 - 检查/proc/1/cgroup中所有层次结构是否为/可能有用。但是,在一些非容器化环境下,一些层次的值是/init.scope(Ubuntu 20.04 cgroup 0和1)。因此,这也不是完全可靠的方法。 - 在/proc/1/environ中检查container=lxc对lxc有效,但对docker无效。而且,它需要root权限。
到目前为止我发现在CentOS和Ubuntu上以及lxc(4.0)容器和Docker中,唯一可靠的方法,而且还不需要root权限,是检查PID 2。
在所有主机系统上,PID 2都是kthreadd:
$ ps -p 2
  PID TTY          TIME CMD
    2 ?        00:00:00 kthreadd

在容器中,此PID要么不存在,要么不是kthreadd。Docker和LXC均如此表现:

root@85396f8bce58:/# ps -p 2
    PID TTY          TIME CMD
root@85396f8bce58:/# 

最好的方法似乎是检查/proc/2/status:
$ head -n1 /proc/2/status
Name:   kthreadd

所以像这样似乎可以工作:

if [ -n "$(grep 'kthreadd' /proc/2/status 2>/dev/null)" ]; then
    echo "Not in container"
else
    echo "In container";
fi

4
在 macOS 上运行 Docker,通常情况下没有 PID 2。 - Daniel Widdis
使用 lxd 5.8,未看到 PID 为 2 的进程。 - Prem Anand
使用 Windows 10 的 WSL 2 发行版 Ubuntu 22.04.1,它没有 pid 2 的进程。但它肯定不是 Docker 环境。 - zerox

9

最简单的方法是检查环境。如果你有container=lxc变量,那么你就在容器里。

否则,如果你是root用户,可以尝试执行mknodmount操作,如果失败了,那么你很可能在一个被削弱能力的容器中。


这个不仅适用于Docker(我没有检查过),更重要的是适用于LXD/LXC容器(已经检查过),因为/proc/1/cgroup无法让你检测到。 - Draco Ater
5
你能否用代码而不是伪代码编辑答案?“container=lxc”不太对。你的意思是类似于if [[ "lxc" = "$container" ]]吗? - Alexander Mills
4
我的意思是...这有点奇怪,通常环境变量都是全大写的,所以我想要更准确一些。 - Alexander Mills
14
“docker run alpine env”没有返回任何看起来像该变量的内容。 - Archimedes Trajano

5
这是一个古老的问题,但确实非常好。 :) 我编写了一些自动化脚本,在裸金属、虚拟机和 Docker 容器上运行,并根据脚本执行的平台进行逻辑分支。在我的情况下,我有特权创建容器和 Docker 镜像,因此只有当您控制整个堆栈时,此解决方案才有效:
Dockerfile 片段:
FROM ubuntu:18.04

ENV PLATFORM="docker"

RUN apt update; \
...

脚本可以检查 $PLATFORM 的值,针对每个平台实现期望的结果。
#!/bin/bash

# Check for executor specification in environment
case $PLATFORM in
  docker)
    # If running in Docker, do this stuff
    echo "Running containerized, proceeding..."
    ;;
  virtual)
    # If running in a VM, do different stuff
    echo "Running on a VM, loading VM stuff..."
    modprobe some-kernel-module
    ;;
  *)
    echo "Unknown executor specified! Exiting..."
    exit 1
    ;;
esac

为了简洁起见,在上述代码中我省略了baremetal。


3
在Docker容器中,条目/proc/self/cgroup被挂载到主机上的控制组。例如,在一个容器中:
# awk -F: '/cpuset/' /proc/self/cgroup
3:cpuset:/docker/22bd0c154fb4e0d1b6c748faf1f1a12116acc21ce287618a115ad2bea41256b3

然而,在主机上相同的情况下

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

使用shell中的某个东西进行低调测试

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

在两者上都返回1。 - sorin
可以工作了!谢谢!@sorin,实际上它只在主机级别返回1,但在容器内部返回零。 - Dmitry Shevkoplyas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接