如何确定进程是不是在 lxc/Docker 内部运行?
Posted
技术标签:
【中文标题】如何确定进程是不是在 lxc/Docker 内部运行?【英文标题】:How to determine if a process runs inside lxc/Docker?如何确定进程是否在 lxc/Docker 内部运行? 【发布时间】:2013-11-29 09:13:43 【问题描述】:有没有办法确定一个进程(脚本)是否在 lxc 容器(〜 Docker 运行时)内运行?我知道有些程序能够检测它们是否在虚拟机中运行,lxc/docker 有类似的东西吗?
【问题讨论】:
这可能看起来很迂腐,但最好重新表述您的问题以描述您遇到的问题并询问如何解决它 - 否则,该问题更有可能被关闭.在许多情况下,很难做出这种改变,但在你的情况下,如果你愿意的话,简单地改写并不难。 在容器内发出此命令时有一个有趣的响应:正常运行时间 【参考方案1】: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
,它可以返回调用它的容器类型。可能会有所帮助。
不过不知道其他主要发行版。
【讨论】:
在 Debian 上/bin/running-in-container
由 upstart
提供。随着向 systemd 的过渡,它可能会消失。我希望不会 - 这听起来很有用!
“在目录树的顶部”,这是什么意思?那在哪里?
其他人指出检查.dockerenv
是not recommended
注意:仅当运行时是 docker 守护进程时,对 .dockerenv 的测试才有效。如果您使用的是 podman 或其他东西,则会失败。
Ubuntu 18.0.4 没有/bin/running-in-container
。【参考方案2】:
最可靠的方法是查看/proc/1/cgroup
。它会告诉你初始化进程的控制组,当你不是在容器中时,所有层次结构都是/
。当您在一个容器中时,您将看到锚点的名称。对于 LXC/Docker 容器,它将分别类似于 /lxc/<containerid>
或 /docker/<containerid>
。
【讨论】:
docker 现在在这些路径中使用docker
而不是 lxc
不适用于 lxd/lxc 容器,但 ***.com/a/20010626/170230 可以。
在更高版本的 systemd 中,您似乎无法对所有 cgroup 使用 /
依赖进程 1;在我的 Debian 9 系统(systemd 232)上,十个 cgroup(3:cpuset
、4:perf_event
和 7:freezer
)中只有三个是 root;其余的都在/init.scope
之下。也就是说,我认为在该文件中搜索 :/docker/
可能是目前最可靠的启发式方法。
grep 'docker\|lxc' /proc/1/cgroup
在 Docker 18.09 上为我工作。
不适合我。使用 LXC 特权容器托管 Ubuntu 19.04,来宾 Ubuntu 18.04。 /proc/1/cgroup 不包含 lxc 字符串。【参考方案3】:
在新的 ubuntu 16.04 系统上,新的 systemd 和 lxc 2.0
sudo grep -qa container=lxc /proc/1/environ
【讨论】:
这适用于我在 Ubuntu 焦点 20.04 上。以上的答案都没有。 谢谢!它适用于lxc!你能解释一下为什么需要'-a'吗?grep -q container=lxc /proc/1/environ
还不够吗?
/proc/$$/environ
用空字节分隔环境变量。如果没有-a
,则适用手册页中的这段话: > 默认情况下,TYPE 是二进制,并且 grep 在发现空输入二进制数据后抑制输出【参考方案4】:
在 bash 脚本中检查 docker/lxc 的简洁方法是:
#!/bin/bash
if grep -sq 'docker\|lxc' /proc/1/cgroup; then
echo I'm running on docker.
fi
【讨论】:
感谢@DanielGriscom,看起来好多了。 当我的容器在 kubernetes 中运行时,这不起作用。【参考方案5】:检查是否在 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()
【讨论】:
重要提示!当容器在 Kubernetes 中运行时,这似乎不起作用。相反,用 'kubepod' 代替 'docker' 替换最后一行。 (或者,放入一个“或”语句来检查两者;)) 我猜是kubepods
。【参考方案6】:
我们使用 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 的更多信息here。 是的,但重点不是init进程的名字,而是进程号。 这似乎只适用于 Docker。在 LXC 容器中它返回 Systemd PID 1 它现在也在 docker 中返回 1。它通常是sh
而不是init
,但它几乎可以是任何一个。
在docker下,已经不是这样了——bash-5.0# cat /proc/1/sched bash (1, #threads: 1)
【参考方案7】:
最简单的方法是检查环境。如果你有 container=lxc
变量,你就在一个容器中。
否则,如果你是root,你可以尝试执行mknod
或mount
操作,如果失败,你很可能在一个能力下降的容器中。
【讨论】:
这个不仅适用于 docker(我没有检查),更重要的是适用于 lxd/lxc 容器(检查),其中/proc/1/cgroup
不允许您检测到。跨度>
你能用代码而不是伪代码来编辑答案吗? "container=lxc" ? 不合适。你的意思是像 if [[ "lxc" = "$container" ]] 吗?
我的意思是......这很奇怪,通常环境变量都是大写的,所以在这里寻找一些精确度
docker run alpine env
没有给出任何看起来像那个变量的东西【参考方案8】:
我的回答仅适用于 Node.js 进程,但可能与一些偶然发现此问题并寻找 Node.js 特定答案的访问者相关。
我遇到了同样的问题,我依赖 /proc/self/cgroup
创建了一个 npm 包 仅用于此目的 - 检测 Node.js 进程是否在 Docker 容器内运行。
containerized npm module 将在 Node.js 中帮助您。它目前尚未在 Io.js 中进行测试,但也可以在那里工作。
【讨论】:
感谢这个模块,似乎有几个待解决的开放修复 - 你还在维护这个吗?【参考方案9】:这是一个老问题,但非常好。 :)
我编写了一些我们在裸机、VM 和 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。
【讨论】:
【参考方案10】:我已将 JJC 的答案翻译成 ruby
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
【讨论】:
【参考方案11】:在 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
【讨论】:
这在基于 Mac 的 docker 容器上对我不起作用。返回空。 Docker 版本 2.1.0.1 (37199)。 这个做了:def is_non_docker(): return os.path.exists('/proc/1/cgroup')
按照此处接受的答案***.com/questions/20010199/…
你获得了无用的猫奖。以及子流程一的无用使用。
是的,这是一个全新级别的不必要cat
!不错的一个:-D
你是对的,我会更新它的答案,即使它仍然不是包罗万象。 @JanHudec【参考方案12】:
本SO问答:"Find out if the OS is running in a virtual environment";虽然与 OP 的问题不同,但它确实回答了查找您所在容器的常见情况(如果有的话)。
特别是,安装并阅读这个似乎运行良好的 bash 脚本的代码:
virt-what:
sudo apt install virt-what
【讨论】:
不适用于 Ubuntu 16.04 上的virt-what
版本 1.14-1。需要补丁。
有趣的是,在 windows 上的 docker 内部,virt-what
报告 hyperv
,就像我的 WSL2 bash shell 一样。
这在 Ubuntu 20.04 中确实有效,它在 /proc/1/environ 中搜索“lxc”【参考方案13】:
golang代码获取pid container_id,可以获取map 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 && id_len == 64
containerID = cgroup_path[pos+len(docker_str) : pos+len(docker_str)+64]
return containerID
return containerID
【讨论】:
【参考方案14】:Docker 每天都在发展,所以我们不能确定他们将来是否会保留.dockerenv .dockerinit
。
在大多数 Linux 风格中,init
是第一个启动的进程。但在容器的情况下,情况并非如此。
#!/bin/bash
if ps -p1|grep -q init;then
echo "non-docker"
else
echo "docker"
fi
【讨论】:
@RomanTrofimov LXC/Docker 也没有。多么有趣的评论。 它在 centos 7 中也不起作用。当我在我的主机上运行时,它会显示 docker。看起来 systemd 正在以进程 id 1 运行 @VenkateswaraRao - 这必须在容器内运行。目的是确定您是否在 docker 容器内。 @GovindKailas:问题在于,这假设正常的 PID 是init
,这在基于systemd
或launchd
的系统上是不正确的......
@SamThomas:launchd、upstart、Solaris SMF、systemd、Sys V 风格的初始化、BSD 风格的初始化(尽管这两个和其他一些可能称他们的 PID 1 init
)、OpenRC、initng、runit . See here。大多数现代基于 Linux 的系统将使用 systemd
,一些较旧的系统,upstart....所有现代 OS X 系统都将使用 launchd
【参考方案15】:
这是 Ruby 中的一个解决方案,
# Usage: DockerHelper.running_in_docker?
module DockerHelper
extend self
def running_in_docker?
!!(File.read("/proc/1/cgroup") =~ %r[^\d+:\w+:/docker/]) # !! => true/false
rescue Errno::ENOENT
false
end
end
如果你喜欢用你的代码进行测试,here's a spec in the gist。
【讨论】:
【参考方案16】:在 docker 容器中,条目 /proc/self/cgroup
被挂载到主机上的 cgroups。
例如在容器中
# 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。【参考方案17】:也许这可以解决问题:
if [ -z $(docker ps -q) ]; then
echo "There is not process currently running"
else
echo "There are processes running"
fi
这就是你想要的吗?希望对你有帮助=)
【讨论】:
显然没有docker
二进制文件可从容器内部获得。
嗯,这在控制容器具有docker
并访问主机的 docker 套接字的情况下(例如 gitlab docker-in-docker)会失败。
是的,你是对的,当然没有^^。我在阅读该问题时得到了错误的解释。谢谢你,Shalomb。以上是关于如何确定进程是不是在 lxc/Docker 内部运行?的主要内容,如果未能解决你的问题,请参考以下文章