【问题标题】:How to determine if a process runs inside lxc/Docker?如何确定进程是否在 lxc/Docker 内部运行?
【发布时间】:2013-11-29 09:13:43
【问题描述】:

有没有办法确定一个进程(脚本)是否在 lxc 容器(〜 Docker 运行时)内运行?我知道有些程序能够检测它们是否在虚拟机中运行,lxc/docker 有类似的东西吗?

【问题讨论】:

  • 这可能看起来很迂腐,但最好重新表述您的问题以描述您遇到的问题并询问如何解决它 - 否则,该问题更有可能被关闭.在许多情况下,很难做出这种改变,但在你的情况下,如果你愿意的话,简单地改写并不难。
  • 在容器内发出此命令时有一个有趣的响应:正常运行时间

标签: linux bash 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-containerupstart 提供。随着向 systemd 的过渡,它可能会消失。我希望不会 - 这听起来很有用!
  • “在目录树的顶部”,这是什么意思?那在哪里?
  • 其他人指出检查.dockerenvnot 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 容器,但 stackoverflow.com/a/20010626/170230 可以。
  • 在更高版本的 systemd 中,您似乎无法对所有 cgroup 使用 / 依赖进程 1;在我的 Debian 9 系统(systemd 232)上,十个 cgroup(3:cpuset4:perf_event7: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,你可以尝试执行mknodmount操作,如果失败,你很可能在一个能力下降的容器中。

【讨论】:

  • 这个不仅适用于 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') 按照此处接受的答案stackoverflow.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,这在基于systemdlaunchd 的系统上是不正确的......
        • @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。
          猜你喜欢
          • 1970-01-01
          • 2010-12-08
          • 2012-09-17
          • 1970-01-01
          • 2014-06-24
          • 2023-03-14
          • 2011-07-09
          相关资源
          最近更新 更多